summaryrefslogtreecommitdiffstats
path: root/contrib/tools/python3/Objects
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/tools/python3/Objects')
-rw-r--r--contrib/tools/python3/Objects/abstract.c405
-rw-r--r--contrib/tools/python3/Objects/boolobject.c13
-rw-r--r--contrib/tools/python3/Objects/bytearrayobject.c293
-rw-r--r--contrib/tools/python3/Objects/bytes_methods.c154
-rw-r--r--contrib/tools/python3/Objects/bytesobject.c355
-rw-r--r--contrib/tools/python3/Objects/call.c122
-rw-r--r--contrib/tools/python3/Objects/capsule.c178
-rw-r--r--contrib/tools/python3/Objects/cellobject.c9
-rw-r--r--contrib/tools/python3/Objects/classobject.c51
-rw-r--r--contrib/tools/python3/Objects/clinic/bytearrayobject.c.h397
-rw-r--r--contrib/tools/python3/Objects/clinic/bytesobject.c.h403
-rw-r--r--contrib/tools/python3/Objects/clinic/classobject.c.h8
-rw-r--r--contrib/tools/python3/Objects/clinic/codeobject.c.h56
-rw-r--r--contrib/tools/python3/Objects/clinic/complexobject.c.h11
-rw-r--r--contrib/tools/python3/Objects/clinic/descrobject.c.h12
-rw-r--r--contrib/tools/python3/Objects/clinic/dictobject.c.h129
-rw-r--r--contrib/tools/python3/Objects/clinic/enumobject.c.h8
-rw-r--r--contrib/tools/python3/Objects/clinic/floatobject.c.h11
-rw-r--r--contrib/tools/python3/Objects/clinic/funcobject.c.h152
-rw-r--r--contrib/tools/python3/Objects/clinic/listobject.c.h80
-rw-r--r--contrib/tools/python3/Objects/clinic/longobject.c.h22
-rw-r--r--contrib/tools/python3/Objects/clinic/memoryobject.c.h15
-rw-r--r--contrib/tools/python3/Objects/clinic/moduleobject.c.h11
-rw-r--r--contrib/tools/python3/Objects/clinic/odictobject.c.h8
-rw-r--r--contrib/tools/python3/Objects/clinic/setobject.c.h571
-rw-r--r--contrib/tools/python3/Objects/clinic/structseq.c.h8
-rw-r--r--contrib/tools/python3/Objects/clinic/tupleobject.c.h8
-rw-r--r--contrib/tools/python3/Objects/clinic/typeobject.c.h13
-rw-r--r--contrib/tools/python3/Objects/clinic/typevarobject.c.h370
-rw-r--r--contrib/tools/python3/Objects/clinic/unicodeobject.c.h460
-rw-r--r--contrib/tools/python3/Objects/codeobject.c738
-rw-r--r--contrib/tools/python3/Objects/complexobject.c40
-rw-r--r--contrib/tools/python3/Objects/descrobject.c412
-rw-r--r--contrib/tools/python3/Objects/dictobject.c4249
-rw-r--r--contrib/tools/python3/Objects/enumobject.c3
-rw-r--r--contrib/tools/python3/Objects/exceptions.c320
-rw-r--r--contrib/tools/python3/Objects/fileobject.c55
-rw-r--r--contrib/tools/python3/Objects/floatobject.c171
-rw-r--r--contrib/tools/python3/Objects/frameobject.c1247
-rw-r--r--contrib/tools/python3/Objects/funcobject.c457
-rw-r--r--contrib/tools/python3/Objects/genericaliasobject.c109
-rw-r--r--contrib/tools/python3/Objects/genobject.c350
-rw-r--r--contrib/tools/python3/Objects/interpreteridobject.c294
-rw-r--r--contrib/tools/python3/Objects/iterobject.c2
-rw-r--r--contrib/tools/python3/Objects/listobject.c1700
-rw-r--r--contrib/tools/python3/Objects/longobject.c374
-rw-r--r--contrib/tools/python3/Objects/memoryobject.c180
-rw-r--r--contrib/tools/python3/Objects/methodobject.c28
-rw-r--r--contrib/tools/python3/Objects/mimalloc/alloc-aligned.c298
-rw-r--r--contrib/tools/python3/Objects/mimalloc/alloc-override.c297
-rw-r--r--contrib/tools/python3/Objects/mimalloc/alloc-posix.c185
-rw-r--r--contrib/tools/python3/Objects/mimalloc/alloc.c1074
-rw-r--r--contrib/tools/python3/Objects/mimalloc/arena.c935
-rw-r--r--contrib/tools/python3/Objects/mimalloc/bitmap.c432
-rw-r--r--contrib/tools/python3/Objects/mimalloc/bitmap.h115
-rw-r--r--contrib/tools/python3/Objects/mimalloc/heap.c693
-rw-r--r--contrib/tools/python3/Objects/mimalloc/init.c687
-rw-r--r--contrib/tools/python3/Objects/mimalloc/options.c571
-rw-r--r--contrib/tools/python3/Objects/mimalloc/os.c698
-rw-r--r--contrib/tools/python3/Objects/mimalloc/page-queue.c332
-rw-r--r--contrib/tools/python3/Objects/mimalloc/page.c972
-rw-r--r--contrib/tools/python3/Objects/mimalloc/prim/osx/alloc-override-zone.c458
-rw-r--r--contrib/tools/python3/Objects/mimalloc/prim/osx/prim.c9
-rw-r--r--contrib/tools/python3/Objects/mimalloc/prim/prim.c24
-rw-r--r--contrib/tools/python3/Objects/mimalloc/prim/unix/prim.c860
-rw-r--r--contrib/tools/python3/Objects/mimalloc/prim/wasi/prim.c276
-rw-r--r--contrib/tools/python3/Objects/mimalloc/prim/windows/etw.h905
-rw-r--r--contrib/tools/python3/Objects/mimalloc/prim/windows/prim.c622
-rw-r--r--contrib/tools/python3/Objects/mimalloc/random.c254
-rw-r--r--contrib/tools/python3/Objects/mimalloc/segment-map.c153
-rw-r--r--contrib/tools/python3/Objects/mimalloc/segment.c1682
-rw-r--r--contrib/tools/python3/Objects/mimalloc/static.c40
-rw-r--r--contrib/tools/python3/Objects/mimalloc/stats.c467
-rw-r--r--contrib/tools/python3/Objects/moduleobject.c454
-rw-r--r--contrib/tools/python3/Objects/namespaceobject.c82
-rw-r--r--contrib/tools/python3/Objects/object.c1171
-rw-r--r--contrib/tools/python3/Objects/obmalloc.c1013
-rw-r--r--contrib/tools/python3/Objects/odictobject.c42
-rw-r--r--contrib/tools/python3/Objects/picklebufobject.c5
-rw-r--r--contrib/tools/python3/Objects/rangeobject.c23
-rw-r--r--contrib/tools/python3/Objects/setobject.c1183
-rw-r--r--contrib/tools/python3/Objects/sliceobject.c65
-rw-r--r--contrib/tools/python3/Objects/stringlib/README.txt2
-rw-r--r--contrib/tools/python3/Objects/stringlib/clinic/transmogrify.h.h11
-rw-r--r--contrib/tools/python3/Objects/stringlib/codecs.h3
-rw-r--r--contrib/tools/python3/Objects/stringlib/fastsearch.h12
-rw-r--r--contrib/tools/python3/Objects/stringlib/find.h47
-rw-r--r--contrib/tools/python3/Objects/stringlib/unicode_format.h15
-rw-r--r--contrib/tools/python3/Objects/structseq.c163
-rw-r--r--contrib/tools/python3/Objects/tupleobject.c71
-rw-r--r--contrib/tools/python3/Objects/typeobject.c2003
-rw-r--r--contrib/tools/python3/Objects/typevarobject.c473
-rw-r--r--contrib/tools/python3/Objects/unicodectype.c16
-rw-r--r--contrib/tools/python3/Objects/unicodeobject.c1207
-rw-r--r--contrib/tools/python3/Objects/unicodetype_db.h1955
-rw-r--r--contrib/tools/python3/Objects/unionobject.c67
-rw-r--r--contrib/tools/python3/Objects/weakrefobject.c802
97 files changed, 30593 insertions, 8393 deletions
diff --git a/contrib/tools/python3/Objects/abstract.c b/contrib/tools/python3/Objects/abstract.c
index e95785900c9..8357175aa55 100644
--- a/contrib/tools/python3/Objects/abstract.c
+++ b/contrib/tools/python3/Objects/abstract.c
@@ -2,16 +2,17 @@
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
+#include "pycore_pybuffer.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
+#include "pycore_crossinterp.h" // _Py_CallInInterpreter()
#include "pycore_object.h" // _Py_CheckSlotResult()
#include "pycore_long.h" // _Py_IsNegative
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_unionobject.h" // _PyUnion_Check()
-#include <ctype.h>
-#include <stddef.h> // offsetof()
+#include <stddef.h> // offsetof()
/* Shorthands to return certain errors */
@@ -182,7 +183,7 @@ PyObject_GetItem(PyObject *o, PyObject *key)
return Py_GenericAlias(o, key);
}
- if (_PyObject_LookupAttr(o, &_Py_ID(__class_getitem__), &meth) < 0) {
+ if (PyObject_GetOptionalAttr(o, &_Py_ID(__class_getitem__), &meth) < 0) {
return NULL;
}
if (meth && meth != Py_None) {
@@ -200,6 +201,25 @@ PyObject_GetItem(PyObject *o, PyObject *key)
}
int
+PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result)
+{
+ if (PyDict_CheckExact(obj)) {
+ return PyDict_GetItemRef(obj, key, result);
+ }
+
+ *result = PyObject_GetItem(obj, key);
+ if (*result) {
+ return 1;
+ }
+ assert(PyErr_Occurred());
+ if (!PyErr_ExceptionMatches(PyExc_KeyError)) {
+ return -1;
+ }
+ PyErr_Clear();
+ return 0;
+}
+
+int
PyObject_SetItem(PyObject *o, PyObject *key, PyObject *value)
{
if (o == NULL || key == NULL || value == NULL) {
@@ -294,11 +314,17 @@ PyObject_CheckBuffer(PyObject *obj)
return (tp_as_buffer != NULL && tp_as_buffer->bf_getbuffer != NULL);
}
+// Old buffer protocols (deprecated, abi only)
+
+/* Checks whether an arbitrary object supports the (character, single segment)
+ buffer interface.
-/* We release the buffer right after use of this function which could
+ Returns 1 on success, 0 on failure.
+
+ We release the buffer right after use of this function which could
cause issues later on. Don't use these functions in new code.
*/
-int
+PyAPI_FUNC(int) /* abi_only */
PyObject_CheckReadBuffer(PyObject *obj)
{
PyBufferProcs *pb = Py_TYPE(obj)->tp_as_buffer;
@@ -333,7 +359,13 @@ as_read_buffer(PyObject *obj, const void **buffer, Py_ssize_t *buffer_len)
return 0;
}
-int
+/* Takes an arbitrary object which must support the (character, single segment)
+ buffer interface and returns a pointer to a read-only memory location
+ usable as character based input for subsequent processing.
+
+ Return 0 on success. buffer and buffer_len are only set in case no error
+ occurs. Otherwise, -1 is returned and an exception set. */
+PyAPI_FUNC(int) /* abi_only */
PyObject_AsCharBuffer(PyObject *obj,
const char **buffer,
Py_ssize_t *buffer_len)
@@ -341,16 +373,30 @@ PyObject_AsCharBuffer(PyObject *obj,
return as_read_buffer(obj, (const void **)buffer, buffer_len);
}
-int PyObject_AsReadBuffer(PyObject *obj,
- const void **buffer,
- Py_ssize_t *buffer_len)
+/* Same as PyObject_AsCharBuffer() except that this API expects (readable,
+ single segment) buffer interface and returns a pointer to a read-only memory
+ location which can contain arbitrary data.
+
+ 0 is returned on success. buffer and buffer_len are only set in case no
+ error occurs. Otherwise, -1 is returned and an exception set. */
+PyAPI_FUNC(int) /* abi_only */
+PyObject_AsReadBuffer(PyObject *obj,
+ const void **buffer,
+ Py_ssize_t *buffer_len)
{
return as_read_buffer(obj, buffer, buffer_len);
}
-int PyObject_AsWriteBuffer(PyObject *obj,
- void **buffer,
- Py_ssize_t *buffer_len)
+/* Takes an arbitrary object which must support the (writable, single segment)
+ buffer interface and returns a pointer to a writable memory location in
+ buffer of size 'buffer_len'.
+
+ Return 0 on success. buffer and buffer_len are only set in case no error
+ occurs. Otherwise, -1 is returned and an exception set. */
+PyAPI_FUNC(int) /* abi_only */
+PyObject_AsWriteBuffer(PyObject *obj,
+ void **buffer,
+ Py_ssize_t *buffer_len)
{
PyBufferProcs *pb;
Py_buffer view;
@@ -379,6 +425,12 @@ int PyObject_AsWriteBuffer(PyObject *obj,
int
PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags)
{
+ if (flags != PyBUF_SIMPLE) { /* fast path */
+ if (flags == PyBUF_READ || flags == PyBUF_WRITE) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ }
PyBufferProcs *pb = Py_TYPE(obj)->tp_as_buffer;
if (pb == NULL || pb->bf_getbuffer == NULL) {
@@ -491,7 +543,7 @@ PyBuffer_GetPointer(const Py_buffer *view, const Py_ssize_t *indices)
}
-void
+static void
_Py_add_one_to_index_F(int nd, Py_ssize_t *index, const Py_ssize_t *shape)
{
int k;
@@ -507,7 +559,7 @@ _Py_add_one_to_index_F(int nd, Py_ssize_t *index, const Py_ssize_t *shape)
}
}
-void
+static void
_Py_add_one_to_index_C(int nd, Py_ssize_t *index, const Py_ssize_t *shape)
{
int k;
@@ -715,11 +767,17 @@ PyBuffer_FillInfo(Py_buffer *view, PyObject *obj, void *buf, Py_ssize_t len,
return -1;
}
- if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) &&
- (readonly == 1)) {
- PyErr_SetString(PyExc_BufferError,
- "Object is not writable.");
- return -1;
+ if (flags != PyBUF_SIMPLE) { /* fast path */
+ if (flags == PyBUF_READ || flags == PyBUF_WRITE) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) &&
+ (readonly == 1)) {
+ PyErr_SetString(PyExc_BufferError,
+ "Object is not writable.");
+ return -1;
+ }
}
view->obj = Py_XNewRef(obj);
@@ -757,6 +815,27 @@ PyBuffer_Release(Py_buffer *view)
Py_DECREF(obj);
}
+static int
+_buffer_release_call(void *arg)
+{
+ PyBuffer_Release((Py_buffer *)arg);
+ return 0;
+}
+
+int
+_PyBuffer_ReleaseInInterpreter(PyInterpreterState *interp,
+ Py_buffer *view)
+{
+ return _Py_CallInInterpreter(interp, _buffer_release_call, view);
+}
+
+int
+_PyBuffer_ReleaseInInterpreterAndRawFree(PyInterpreterState *interp,
+ Py_buffer *view)
+{
+ return _Py_CallInInterpreterAndRawFree(interp, _buffer_release_call, view);
+}
+
PyObject *
PyObject_Format(PyObject *obj, PyObject *format_spec)
{
@@ -1113,29 +1192,10 @@ PyNumber_Multiply(PyObject *v, PyObject *w)
return result;
}
-PyObject *
-PyNumber_MatrixMultiply(PyObject *v, PyObject *w)
-{
- return binary_op(v, w, NB_SLOT(nb_matrix_multiply), "@");
-}
-
-PyObject *
-PyNumber_FloorDivide(PyObject *v, PyObject *w)
-{
- return binary_op(v, w, NB_SLOT(nb_floor_divide), "//");
-}
-
-PyObject *
-PyNumber_TrueDivide(PyObject *v, PyObject *w)
-{
- return binary_op(v, w, NB_SLOT(nb_true_divide), "/");
-}
-
-PyObject *
-PyNumber_Remainder(PyObject *v, PyObject *w)
-{
- return binary_op(v, w, NB_SLOT(nb_remainder), "%");
-}
+BINARY_FUNC(PyNumber_MatrixMultiply, nb_matrix_multiply, "@")
+BINARY_FUNC(PyNumber_FloorDivide, nb_floor_divide, "//")
+BINARY_FUNC(PyNumber_TrueDivide, nb_true_divide, "/")
+BINARY_FUNC(PyNumber_Remainder, nb_remainder, "%")
PyObject *
PyNumber_Power(PyObject *v, PyObject *w, PyObject *z)
@@ -1312,73 +1372,27 @@ _PyNumber_InPlacePowerNoMod(PyObject *lhs, PyObject *rhs)
/* Unary operators and functions */
-PyObject *
-PyNumber_Negative(PyObject *o)
-{
- if (o == NULL) {
- return null_error();
- }
-
- PyNumberMethods *m = Py_TYPE(o)->tp_as_number;
- if (m && m->nb_negative) {
- PyObject *res = (*m->nb_negative)(o);
- assert(_Py_CheckSlotResult(o, "__neg__", res != NULL));
- return res;
- }
-
- return type_error("bad operand type for unary -: '%.200s'", o);
-}
-
-PyObject *
-PyNumber_Positive(PyObject *o)
-{
- if (o == NULL) {
- return null_error();
- }
-
- PyNumberMethods *m = Py_TYPE(o)->tp_as_number;
- if (m && m->nb_positive) {
- PyObject *res = (*m->nb_positive)(o);
- assert(_Py_CheckSlotResult(o, "__pos__", res != NULL));
- return res;
+#define UNARY_FUNC(func, op, meth_name, descr) \
+ PyObject * \
+ func(PyObject *o) { \
+ if (o == NULL) { \
+ return null_error(); \
+ } \
+ \
+ PyNumberMethods *m = Py_TYPE(o)->tp_as_number; \
+ if (m && m->op) { \
+ PyObject *res = (*m->op)(o); \
+ assert(_Py_CheckSlotResult(o, #meth_name, res != NULL)); \
+ return res; \
+ } \
+ \
+ return type_error("bad operand type for "descr": '%.200s'", o); \
}
- return type_error("bad operand type for unary +: '%.200s'", o);
-}
-
-PyObject *
-PyNumber_Invert(PyObject *o)
-{
- if (o == NULL) {
- return null_error();
- }
-
- PyNumberMethods *m = Py_TYPE(o)->tp_as_number;
- if (m && m->nb_invert) {
- PyObject *res = (*m->nb_invert)(o);
- assert(_Py_CheckSlotResult(o, "__invert__", res != NULL));
- return res;
- }
-
- return type_error("bad operand type for unary ~: '%.200s'", o);
-}
-
-PyObject *
-PyNumber_Absolute(PyObject *o)
-{
- if (o == NULL) {
- return null_error();
- }
-
- PyNumberMethods *m = Py_TYPE(o)->tp_as_number;
- if (m && m->nb_absolute) {
- PyObject *res = m->nb_absolute(o);
- assert(_Py_CheckSlotResult(o, "__abs__", res != NULL));
- return res;
- }
-
- return type_error("bad operand type for abs(): '%.200s'", o);
-}
+UNARY_FUNC(PyNumber_Negative, nb_negative, __neg__, "unary -")
+UNARY_FUNC(PyNumber_Positive, nb_positive, __pos__, "unary +")
+UNARY_FUNC(PyNumber_Invert, nb_invert, __invert__, "unary ~")
+UNARY_FUNC(PyNumber_Absolute, nb_absolute, __abs__, "abs()")
int
@@ -2341,6 +2355,24 @@ PyMapping_GetItemString(PyObject *o, const char *key)
}
int
+PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result)
+{
+ if (key == NULL) {
+ *result = NULL;
+ null_error();
+ return -1;
+ }
+ PyObject *okey = PyUnicode_FromString(key);
+ if (okey == NULL) {
+ *result = NULL;
+ return -1;
+ }
+ int rc = PyMapping_GetOptionalItem(obj, okey, result);
+ Py_DECREF(okey);
+ return rc;
+}
+
+int
PyMapping_SetItemString(PyObject *o, const char *key, PyObject *value)
{
PyObject *okey;
@@ -2360,31 +2392,71 @@ PyMapping_SetItemString(PyObject *o, const char *key, PyObject *value)
}
int
-PyMapping_HasKeyString(PyObject *o, const char *key)
+PyMapping_HasKeyStringWithError(PyObject *obj, const char *key)
{
- PyObject *v;
+ PyObject *res;
+ int rc = PyMapping_GetOptionalItemString(obj, key, &res);
+ Py_XDECREF(res);
+ return rc;
+}
- v = PyMapping_GetItemString(o, key);
- if (v) {
- Py_DECREF(v);
- return 1;
- }
- PyErr_Clear();
- return 0;
+int
+PyMapping_HasKeyWithError(PyObject *obj, PyObject *key)
+{
+ PyObject *res;
+ int rc = PyMapping_GetOptionalItem(obj, key, &res);
+ Py_XDECREF(res);
+ return rc;
}
int
-PyMapping_HasKey(PyObject *o, PyObject *key)
+PyMapping_HasKeyString(PyObject *obj, const char *key)
{
- PyObject *v;
+ PyObject *value;
+ int rc;
+ if (obj == NULL) {
+ // For backward compatibility.
+ // PyMapping_GetOptionalItemString() crashes if obj is NULL.
+ null_error();
+ rc = -1;
+ }
+ else {
+ rc = PyMapping_GetOptionalItemString(obj, key, &value);
+ }
+ if (rc < 0) {
+ PyErr_FormatUnraisable(
+ "Exception ignored in PyMapping_HasKeyString(); consider using "
+ "PyMapping_HasKeyStringWithError(), "
+ "PyMapping_GetOptionalItemString() or PyMapping_GetItemString()");
+ return 0;
+ }
+ Py_XDECREF(value);
+ return rc;
+}
- v = PyObject_GetItem(o, key);
- if (v) {
- Py_DECREF(v);
- return 1;
+int
+PyMapping_HasKey(PyObject *obj, PyObject *key)
+{
+ PyObject *value;
+ int rc;
+ if (obj == NULL || key == NULL) {
+ // For backward compatibility.
+ // PyMapping_GetOptionalItem() crashes if any of them is NULL.
+ null_error();
+ rc = -1;
}
- PyErr_Clear();
- return 0;
+ else {
+ rc = PyMapping_GetOptionalItem(obj, key, &value);
+ }
+ if (rc < 0) {
+ PyErr_FormatUnraisable(
+ "Exception ignored in PyMapping_HasKey(); consider using "
+ "PyMapping_HasKeyWithError(), "
+ "PyMapping_GetOptionalItem() or PyObject_GetItem()");
+ return 0;
+ }
+ Py_XDECREF(value);
+ return rc;
}
/* This function is quite similar to PySequence_Fast(), but specialized to be
@@ -2486,7 +2558,7 @@ abstract_get_bases(PyObject *cls)
{
PyObject *bases;
- (void)_PyObject_LookupAttr(cls, &_Py_ID(__bases__), &bases);
+ (void)PyObject_GetOptionalAttr(cls, &_Py_ID(__bases__), &bases);
if (bases != NULL && !PyTuple_Check(bases)) {
Py_DECREF(bases);
return NULL;
@@ -2570,7 +2642,7 @@ object_isinstance(PyObject *inst, PyObject *cls)
if (PyType_Check(cls)) {
retval = PyObject_TypeCheck(inst, (PyTypeObject *)cls);
if (retval == 0) {
- retval = _PyObject_LookupAttr(inst, &_Py_ID(__class__), &icls);
+ retval = PyObject_GetOptionalAttr(inst, &_Py_ID(__class__), &icls);
if (icls != NULL) {
if (icls != (PyObject *)(Py_TYPE(inst)) && PyType_Check(icls)) {
retval = PyType_IsSubtype(
@@ -2588,7 +2660,7 @@ object_isinstance(PyObject *inst, PyObject *cls)
if (!check_class(cls,
"isinstance() arg 2 must be a type, a tuple of types, or a union"))
return -1;
- retval = _PyObject_LookupAttr(inst, &_Py_ID(__class__), &icls);
+ retval = PyObject_GetOptionalAttr(inst, &_Py_ID(__class__), &icls);
if (icls != NULL) {
retval = abstract_issubclass(icls, cls);
Py_DECREF(icls);
@@ -2746,7 +2818,7 @@ object_issubclass(PyThreadState *tstate, PyObject *derived, PyObject *cls)
return -1;
}
- /* Probably never reached anymore. */
+ /* Can be reached when infinite recursion happens. */
return recursive_issubclass(derived, cls);
}
@@ -2880,80 +2952,3 @@ PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result)
}
return PYGEN_ERROR;
}
-
-/*
- * Flatten a sequence of bytes() objects into a C array of
- * NULL terminated string pointers with a NULL char* terminating the array.
- * (ie: an argv or env list)
- *
- * Memory allocated for the returned list is allocated using PyMem_Malloc()
- * and MUST be freed by _Py_FreeCharPArray().
- */
-char *const *
-_PySequence_BytesToCharpArray(PyObject* self)
-{
- char **array;
- Py_ssize_t i, argc;
- PyObject *item = NULL;
- Py_ssize_t size;
-
- argc = PySequence_Size(self);
- if (argc == -1)
- return NULL;
-
- assert(argc >= 0);
-
- if ((size_t)argc > (PY_SSIZE_T_MAX-sizeof(char *)) / sizeof(char *)) {
- PyErr_NoMemory();
- return NULL;
- }
-
- array = PyMem_Malloc((argc + 1) * sizeof(char *));
- if (array == NULL) {
- PyErr_NoMemory();
- return NULL;
- }
- for (i = 0; i < argc; ++i) {
- char *data;
- item = PySequence_GetItem(self, i);
- if (item == NULL) {
- /* NULL terminate before freeing. */
- array[i] = NULL;
- goto fail;
- }
- /* check for embedded null bytes */
- if (PyBytes_AsStringAndSize(item, &data, NULL) < 0) {
- /* NULL terminate before freeing. */
- array[i] = NULL;
- goto fail;
- }
- size = PyBytes_GET_SIZE(item) + 1;
- array[i] = PyMem_Malloc(size);
- if (!array[i]) {
- PyErr_NoMemory();
- goto fail;
- }
- memcpy(array[i], data, size);
- Py_DECREF(item);
- }
- array[argc] = NULL;
-
- return array;
-
-fail:
- Py_XDECREF(item);
- _Py_FreeCharPArray(array);
- return NULL;
-}
-
-
-/* Free's a NULL terminated char** array of C strings. */
-void
-_Py_FreeCharPArray(char *const array[])
-{
- Py_ssize_t i;
- for (i = 0; array[i] != NULL; ++i) {
- PyMem_Free(array[i]);
- }
- PyMem_Free((void*)array);
-}
diff --git a/contrib/tools/python3/Objects/boolobject.c b/contrib/tools/python3/Objects/boolobject.c
index 74e16dd2700..a88a8ad0cfd 100644
--- a/contrib/tools/python3/Objects/boolobject.c
+++ b/contrib/tools/python3/Objects/boolobject.c
@@ -1,8 +1,9 @@
/* Boolean type, a subtype of int */
#include "Python.h"
-#include "pycore_object.h" // _Py_FatalRefcountError()
-#include "pycore_long.h" // FALSE_TAG TRUE_TAG
+#include "pycore_long.h" // FALSE_TAG TRUE_TAG
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
+#include "pycore_object.h" // _Py_FatalRefcountError()
#include "pycore_runtime.h" // _Py_ID()
#include <stddef.h>
@@ -12,8 +13,7 @@
static PyObject *
bool_repr(PyObject *self)
{
- PyObject *res = self == Py_True ? &_Py_ID(True) : &_Py_ID(False);
- return Py_NewRef(res);
+ return self == Py_True ? &_Py_ID(True) : &_Py_ID(False);
}
/* Function to return a bool from a C long */
@@ -110,9 +110,10 @@ bool_xor(PyObject *a, PyObject *b)
/* Doc string */
PyDoc_STRVAR(bool_doc,
-"bool(x) -> bool\n\
+"bool(object=False, /)\n\
+--\n\
\n\
-Returns True when the argument x is true, False otherwise.\n\
+Returns True when the argument is true, False otherwise.\n\
The builtins True and False are the only two instances of the class bool.\n\
The class bool is a subclass of the class int, and cannot be subclassed.");
diff --git a/contrib/tools/python3/Objects/bytearrayobject.c b/contrib/tools/python3/Objects/bytearrayobject.c
index 07c20ac6316..feea0aef276 100644
--- a/contrib/tools/python3/Objects/bytearrayobject.c
+++ b/contrib/tools/python3/Objects/bytearrayobject.c
@@ -1,10 +1,10 @@
/* PyByteArray (bytearray) implementation */
-#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_bytes_methods.h"
#include "pycore_bytesobject.h"
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_strhex.h" // _Py_strhex_with_sep()
#include "pycore_long.h" // _PyLong_FromUnsignedChar()
@@ -44,15 +44,15 @@ _getbytevalue(PyObject* arg, int *value)
static int
bytearray_getbuffer(PyByteArrayObject *obj, Py_buffer *view, int flags)
{
- void *ptr;
if (view == NULL) {
PyErr_SetString(PyExc_BufferError,
"bytearray_getbuffer: view==NULL argument is obsolete");
return -1;
}
- ptr = (void *) PyByteArray_AS_STRING(obj);
- /* cannot fail if view != NULL and readonly == 0 */
- (void)PyBuffer_FillInfo(view, (PyObject*)obj, ptr, Py_SIZE(obj), 0, flags);
+ void *ptr = (void *) PyByteArray_AS_STRING(obj);
+ if (PyBuffer_FillInfo(view, (PyObject*)obj, ptr, Py_SIZE(obj), 0, flags) < 0) {
+ return -1;
+ }
obj->ob_exports++;
return 0;
}
@@ -64,6 +64,24 @@ bytearray_releasebuffer(PyByteArrayObject *obj, Py_buffer *view)
assert(obj->ob_exports >= 0);
}
+typedef PyObject* (*_ba_bytes_op)(const char *buf, Py_ssize_t len,
+ PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+_bytearray_with_buffer(PyByteArrayObject *self, _ba_bytes_op op, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
+{
+ PyObject *res;
+
+ /* Increase exports to prevent bytearray storage from changing during op. */
+ self->ob_exports++;
+ res = op(PyByteArray_AS_STRING(self), Py_SIZE(self), sub, start, end);
+ self->ob_exports--;
+
+ return res;
+}
+
static int
_canresize(PyByteArrayObject *self)
{
@@ -132,7 +150,7 @@ PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size)
}
else {
alloc = size + 1;
- new->ob_bytes = PyObject_Malloc(alloc);
+ new->ob_bytes = PyMem_Malloc(alloc);
if (new->ob_bytes == NULL) {
Py_DECREF(new);
return PyErr_NoMemory();
@@ -221,17 +239,17 @@ PyByteArray_Resize(PyObject *self, Py_ssize_t requested_size)
}
if (logical_offset > 0) {
- sval = PyObject_Malloc(alloc);
+ sval = PyMem_Malloc(alloc);
if (sval == NULL) {
PyErr_NoMemory();
return -1;
}
memcpy(sval, PyByteArray_AS_STRING(self),
Py_MIN((size_t)requested_size, (size_t)Py_SIZE(self)));
- PyObject_Free(obj->ob_bytes);
+ PyMem_Free(obj->ob_bytes);
}
else {
- sval = PyObject_Realloc(obj->ob_bytes, alloc);
+ sval = PyMem_Realloc(obj->ob_bytes, alloc);
if (sval == NULL) {
PyErr_NoMemory();
return -1;
@@ -591,8 +609,10 @@ static int
bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *values)
{
Py_ssize_t start, stop, step, slicelen, needed;
- char *buf, *bytes;
- buf = PyByteArray_AS_STRING(self);
+ char *bytes;
+ // Do not store a reference to the internal buffer since
+ // index.__index__() or _getbytevalue() may alter 'self'.
+ // See https://github.com/python/cpython/issues/91153.
if (_PyIndex_Check(index)) {
Py_ssize_t i = PyNumber_AsSsize_t(index, PyExc_IndexError);
@@ -627,7 +647,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu
}
else {
assert(0 <= ival && ival < 256);
- buf[i] = (char)ival;
+ PyByteArray_AS_STRING(self)[i] = (char)ival;
return 0;
}
}
@@ -682,6 +702,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu
/* Delete slice */
size_t cur;
Py_ssize_t i;
+ char *buf = PyByteArray_AS_STRING(self);
if (!_canresize(self))
return -1;
@@ -722,6 +743,7 @@ bytearray_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *valu
/* Assign slice */
Py_ssize_t i;
size_t cur;
+ char *buf = PyByteArray_AS_STRING(self);
if (needed != slicelen) {
PyErr_Format(PyExc_ValueError,
@@ -951,7 +973,7 @@ bytearray_repr(PyByteArrayObject *self)
}
newsize += 6 + length * 4;
- buffer = PyObject_Malloc(newsize);
+ buffer = PyMem_Malloc(newsize);
if (buffer == NULL) {
PyErr_NoMemory();
return NULL;
@@ -1008,7 +1030,7 @@ bytearray_repr(PyByteArrayObject *self)
}
v = PyUnicode_FromStringAndSize(buffer, p - buffer);
- PyObject_Free(buffer);
+ PyMem_Free(buffer);
return v;
}
@@ -1088,7 +1110,7 @@ bytearray_dealloc(PyByteArrayObject *self)
PyErr_Print();
}
if (self->ob_bytes != 0) {
- PyObject_Free(self->ob_bytes);
+ PyMem_Free(self->ob_bytes);
}
Py_TYPE(self)->tp_free((PyObject *)self);
}
@@ -1121,16 +1143,42 @@ bytearray_dealloc(PyByteArrayObject *self)
#include "stringlib/transmogrify.h"
+/*[clinic input]
+@text_signature "($self, sub[, start[, end]], /)"
+bytearray.find
+
+ sub: object
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ Optional start position. Default: start of the bytes.
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ Optional stop position. Default: end of the bytes.
+ /
+
+Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start:end].
+
+Return -1 on failure.
+[clinic start generated code]*/
+
static PyObject *
-bytearray_find(PyByteArrayObject *self, PyObject *args)
+bytearray_find_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=413e1cab2ae87da0 input=793dfad803e2952f]*/
{
- return _Py_bytes_find(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
+ return _bytearray_with_buffer(self, _Py_bytes_find, sub, start, end);
}
+/*[clinic input]
+bytearray.count = bytearray.find
+
+Return the number of non-overlapping occurrences of subsection 'sub' in bytes B[start:end].
+[clinic start generated code]*/
+
static PyObject *
-bytearray_count(PyByteArrayObject *self, PyObject *args)
+bytearray_count_impl(PyByteArrayObject *self, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
+/*[clinic end generated code: output=a21ee2692e4f1233 input=4deb529db38deda8]*/
{
- return _Py_bytes_count(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
+ return _bytearray_with_buffer(self, _Py_bytes_count, sub, start, end);
}
/*[clinic input]
@@ -1162,40 +1210,113 @@ bytearray_copy_impl(PyByteArrayObject *self)
PyByteArray_GET_SIZE(self));
}
+/*[clinic input]
+bytearray.index = bytearray.find
+
+Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start:end].
+
+Raise ValueError if the subsection is not found.
+[clinic start generated code]*/
+
static PyObject *
-bytearray_index(PyByteArrayObject *self, PyObject *args)
+bytearray_index_impl(PyByteArrayObject *self, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
+/*[clinic end generated code: output=067a1e78efc672a7 input=8cbaf6836dbd2a9a]*/
{
- return _Py_bytes_index(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
+ return _bytearray_with_buffer(self, _Py_bytes_index, sub, start, end);
}
+/*[clinic input]
+bytearray.rfind = bytearray.find
+
+Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start:end].
+
+Return -1 on failure.
+[clinic start generated code]*/
+
static PyObject *
-bytearray_rfind(PyByteArrayObject *self, PyObject *args)
+bytearray_rfind_impl(PyByteArrayObject *self, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
+/*[clinic end generated code: output=51bf886f932b283c input=eaa107468a158423]*/
{
- return _Py_bytes_rfind(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
+ return _bytearray_with_buffer(self, _Py_bytes_rfind, sub, start, end);
}
+/*[clinic input]
+bytearray.rindex = bytearray.find
+
+Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start:end].
+
+Raise ValueError if the subsection is not found.
+[clinic start generated code]*/
+
static PyObject *
-bytearray_rindex(PyByteArrayObject *self, PyObject *args)
+bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
+/*[clinic end generated code: output=38e1cf66bafb08b9 input=81cf49d0af4d5bd0]*/
{
- return _Py_bytes_rindex(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
+ return _bytearray_with_buffer(self, _Py_bytes_rindex, sub, start, end);
}
static int
bytearray_contains(PyObject *self, PyObject *arg)
{
- return _Py_bytes_contains(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), arg);
+ int ret = -1;
+ PyByteArrayObject *ba = _PyByteArray_CAST(self);
+
+ /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
+ ba->ob_exports++;
+ ret = _Py_bytes_contains(PyByteArray_AS_STRING(ba),
+ PyByteArray_GET_SIZE(self),
+ arg);
+ ba->ob_exports--;
+ return ret;
}
+/*[clinic input]
+@text_signature "($self, prefix[, start[, end]], /)"
+bytearray.startswith
+
+ prefix as subobj: object
+ A bytes or a tuple of bytes to try.
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ Optional start position. Default: start of the bytearray.
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ Optional stop position. Default: end of the bytearray.
+ /
+
+Return True if the bytearray starts with the specified prefix, False otherwise.
+[clinic start generated code]*/
+
static PyObject *
-bytearray_startswith(PyByteArrayObject *self, PyObject *args)
+bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end)
+/*[clinic end generated code: output=a3d9b6d44d3662a6 input=76385e0b376b45c1]*/
{
- return _Py_bytes_startswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
+ return _bytearray_with_buffer(self, _Py_bytes_startswith, subobj, start, end);
}
+/*[clinic input]
+@text_signature "($self, suffix[, start[, end]], /)"
+bytearray.endswith
+
+ suffix as subobj: object
+ A bytes or a tuple of bytes to try.
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ Optional start position. Default: start of the bytearray.
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ Optional stop position. Default: end of the bytearray.
+ /
+
+Return True if the bytearray ends with the specified suffix, False otherwise.
+[clinic start generated code]*/
+
static PyObject *
-bytearray_endswith(PyByteArrayObject *self, PyObject *args)
+bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end)
+/*[clinic end generated code: output=e75ea8c227954caa input=9b8baa879aa3d74b]*/
{
- return _Py_bytes_endswith(PyByteArray_AS_STRING(self), PyByteArray_GET_SIZE(self), args);
+ return _bytearray_with_buffer(self, _Py_bytes_endswith, subobj, start, end);
}
/*[clinic input]
@@ -1377,7 +1498,7 @@ bytearray.maketrans
to: Py_buffer
/
-Return a translation table useable for the bytes or bytearray translate method.
+Return a translation table usable for the bytes or bytearray translate method.
The returned table will be one where each byte in frm is mapped to the byte at
the same position in to.
@@ -1387,7 +1508,7 @@ The bytes objects frm and to must be of the same length.
static PyObject *
bytearray_maketrans_impl(Py_buffer *frm, Py_buffer *to)
-/*[clinic end generated code: output=1df267d99f56b15e input=5925a81d2fbbf151]*/
+/*[clinic end generated code: output=1df267d99f56b15e input=b10de38c85950a63]*/
{
return _Py_bytes_maketrans(frm, to);
}
@@ -1438,26 +1559,32 @@ bytearray_split_impl(PyByteArrayObject *self, PyObject *sep,
Py_ssize_t maxsplit)
/*[clinic end generated code: output=833e2cf385d9a04d input=24f82669f41bf523]*/
{
- Py_ssize_t len = PyByteArray_GET_SIZE(self), n;
- const char *s = PyByteArray_AS_STRING(self), *sub;
- PyObject *list;
- Py_buffer vsub;
+ PyObject *list = NULL;
+
+ /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
+ self->ob_exports++;
+ const char *sbuf = PyByteArray_AS_STRING(self);
+ Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self);
if (maxsplit < 0)
maxsplit = PY_SSIZE_T_MAX;
- if (sep == Py_None)
- return stringlib_split_whitespace((PyObject*) self, s, len, maxsplit);
+ if (sep == Py_None) {
+ list = stringlib_split_whitespace((PyObject*)self, sbuf, slen, maxsplit);
+ goto done;
+ }
- if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0)
- return NULL;
- sub = vsub.buf;
- n = vsub.len;
+ Py_buffer vsub;
+ if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) {
+ goto done;
+ }
- list = stringlib_split(
- (PyObject*) self, s, len, sub, n, maxsplit
- );
+ list = stringlib_split((PyObject*)self, sbuf, slen,
+ (const char *)vsub.buf, vsub.len, maxsplit);
PyBuffer_Release(&vsub);
+
+done:
+ self->ob_exports--;
return list;
}
@@ -1549,26 +1676,32 @@ bytearray_rsplit_impl(PyByteArrayObject *self, PyObject *sep,
Py_ssize_t maxsplit)
/*[clinic end generated code: output=a55e0b5a03cb6190 input=a68286e4dd692ffe]*/
{
- Py_ssize_t len = PyByteArray_GET_SIZE(self), n;
- const char *s = PyByteArray_AS_STRING(self), *sub;
- PyObject *list;
- Py_buffer vsub;
+ PyObject *list = NULL;
+
+ /* Increase exports to prevent bytearray storage from changing during _Py_bytes_contains(). */
+ self->ob_exports++;
+ const char *sbuf = PyByteArray_AS_STRING(self);
+ Py_ssize_t slen = PyByteArray_GET_SIZE((PyObject *)self);
if (maxsplit < 0)
maxsplit = PY_SSIZE_T_MAX;
- if (sep == Py_None)
- return stringlib_rsplit_whitespace((PyObject*) self, s, len, maxsplit);
+ if (sep == Py_None) {
+ list = stringlib_rsplit_whitespace((PyObject*)self, sbuf, slen, maxsplit);
+ goto done;
+ }
- if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0)
- return NULL;
- sub = vsub.buf;
- n = vsub.len;
+ Py_buffer vsub;
+ if (PyObject_GetBuffer(sep, &vsub, PyBUF_SIMPLE) != 0) {
+ goto done;
+ }
- list = stringlib_rsplit(
- (PyObject*) self, s, len, sub, n, maxsplit
- );
+ list = stringlib_rsplit((PyObject*)self, sbuf, slen,
+ (const char *)vsub.buf, vsub.len, maxsplit);
PyBuffer_Release(&vsub);
+
+done:
+ self->ob_exports--;
return list;
}
@@ -1729,12 +1862,15 @@ bytearray_extend(PyByteArrayObject *self, PyObject *iterable_of_ints)
while ((item = PyIter_Next(it)) != NULL) {
if (! _getbytevalue(item, &value)) {
+ if (PyErr_ExceptionMatches(PyExc_TypeError) && PyUnicode_Check(iterable_of_ints)) {
+ PyErr_Format(PyExc_TypeError,
+ "expected iterable of integers; got: 'str'");
+ }
Py_DECREF(item);
Py_DECREF(it);
Py_DECREF(bytearray_obj);
return NULL;
}
- buf[len++] = value;
Py_DECREF(item);
if (len >= buf_size) {
@@ -1744,7 +1880,7 @@ bytearray_extend(PyByteArrayObject *self, PyObject *iterable_of_ints)
Py_DECREF(bytearray_obj);
return PyErr_NoMemory();
}
- addition = len >> 1;
+ addition = len ? len >> 1 : 1;
if (addition > PY_SSIZE_T_MAX - len - 1)
buf_size = PY_SSIZE_T_MAX;
else
@@ -1758,6 +1894,7 @@ bytearray_extend(PyByteArrayObject *self, PyObject *iterable_of_ints)
have invalidated it. */
buf = PyByteArray_AS_STRING(bytearray_obj);
}
+ buf[len++] = value;
}
Py_DECREF(it);
@@ -2087,7 +2224,13 @@ bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep)
{
char* argbuf = PyByteArray_AS_STRING(self);
Py_ssize_t arglen = PyByteArray_GET_SIZE(self);
- return _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
+ // Prevent 'self' from being freed if computing len(sep) mutates 'self'
+ // in _Py_strhex_with_sep().
+ // See: https://github.com/python/cpython/issues/143195.
+ self->ob_exports++;
+ PyObject *res = _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
+ self->ob_exports--;
+ return res;
}
static PyObject *
@@ -2196,18 +2339,15 @@ bytearray_methods[] = {
STRINGLIB_CENTER_METHODDEF
BYTEARRAY_CLEAR_METHODDEF
BYTEARRAY_COPY_METHODDEF
- {"count", (PyCFunction)bytearray_count, METH_VARARGS,
- _Py_count__doc__},
+ BYTEARRAY_COUNT_METHODDEF
BYTEARRAY_DECODE_METHODDEF
- {"endswith", (PyCFunction)bytearray_endswith, METH_VARARGS,
- _Py_endswith__doc__},
+ BYTEARRAY_ENDSWITH_METHODDEF
STRINGLIB_EXPANDTABS_METHODDEF
BYTEARRAY_EXTEND_METHODDEF
- {"find", (PyCFunction)bytearray_find, METH_VARARGS,
- _Py_find__doc__},
+ BYTEARRAY_FIND_METHODDEF
BYTEARRAY_FROMHEX_METHODDEF
BYTEARRAY_HEX_METHODDEF
- {"index", (PyCFunction)bytearray_index, METH_VARARGS, _Py_index__doc__},
+ BYTEARRAY_INDEX_METHODDEF
BYTEARRAY_INSERT_METHODDEF
{"isalnum", stringlib_isalnum, METH_NOARGS,
_Py_isalnum__doc__},
@@ -2237,16 +2377,15 @@ bytearray_methods[] = {
BYTEARRAY_REMOVEPREFIX_METHODDEF
BYTEARRAY_REMOVESUFFIX_METHODDEF
BYTEARRAY_REVERSE_METHODDEF
- {"rfind", (PyCFunction)bytearray_rfind, METH_VARARGS, _Py_rfind__doc__},
- {"rindex", (PyCFunction)bytearray_rindex, METH_VARARGS, _Py_rindex__doc__},
+ BYTEARRAY_RFIND_METHODDEF
+ BYTEARRAY_RINDEX_METHODDEF
STRINGLIB_RJUST_METHODDEF
BYTEARRAY_RPARTITION_METHODDEF
BYTEARRAY_RSPLIT_METHODDEF
BYTEARRAY_RSTRIP_METHODDEF
BYTEARRAY_SPLIT_METHODDEF
BYTEARRAY_SPLITLINES_METHODDEF
- {"startswith", (PyCFunction)bytearray_startswith, METH_VARARGS ,
- _Py_startswith__doc__},
+ BYTEARRAY_STARTSWITH_METHODDEF
BYTEARRAY_STRIP_METHODDEF
{"swapcase", stringlib_swapcase, METH_NOARGS,
_Py_swapcase__doc__},
@@ -2262,7 +2401,15 @@ bytearray_mod(PyObject *v, PyObject *w)
{
if (!PyByteArray_Check(v))
Py_RETURN_NOTIMPLEMENTED;
- return _PyBytes_FormatEx(PyByteArray_AS_STRING(v), PyByteArray_GET_SIZE(v), w, 1);
+
+ PyByteArrayObject *self = _PyByteArray_CAST(v);
+ /* Increase exports to prevent bytearray storage from changing during op. */
+ self->ob_exports++;
+ PyObject *res = _PyBytes_FormatEx(
+ PyByteArray_AS_STRING(v), PyByteArray_GET_SIZE(v), w, 1
+ );
+ self->ob_exports--;
+ return res;
}
static PyNumberMethods bytearray_as_number = {
diff --git a/contrib/tools/python3/Objects/bytes_methods.c b/contrib/tools/python3/Objects/bytes_methods.c
index 33aa9c3db6e..981aa571643 100644
--- a/contrib/tools/python3/Objects/bytes_methods.c
+++ b/contrib/tools/python3/Objects/bytes_methods.c
@@ -1,4 +1,3 @@
-#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_bytes_methods.h"
@@ -454,31 +453,21 @@ stringlib_parse_args_finds().
*/
Py_LOCAL_INLINE(int)
-parse_args_finds_byte(const char *function_name, PyObject *args,
- PyObject **subobj, char *byte,
- Py_ssize_t *start, Py_ssize_t *end)
+parse_args_finds_byte(const char *function_name, PyObject **subobj, char *byte)
{
- PyObject *tmp_subobj;
- Py_ssize_t ival;
-
- if(!stringlib_parse_args_finds(function_name, args, &tmp_subobj,
- start, end))
- return 0;
-
- if (PyObject_CheckBuffer(tmp_subobj)) {
- *subobj = tmp_subobj;
+ if (PyObject_CheckBuffer(*subobj)) {
return 1;
}
- if (!_PyIndex_Check(tmp_subobj)) {
+ if (!_PyIndex_Check(*subobj)) {
PyErr_Format(PyExc_TypeError,
"argument should be integer or bytes-like object, "
"not '%.200s'",
- Py_TYPE(tmp_subobj)->tp_name);
+ Py_TYPE(*subobj)->tp_name);
return 0;
}
- ival = PyNumber_AsSsize_t(tmp_subobj, NULL);
+ Py_ssize_t ival = PyNumber_AsSsize_t(*subobj, NULL);
if (ival == -1 && PyErr_Occurred()) {
return 0;
}
@@ -509,19 +498,19 @@ parse_args_finds_byte(const char *function_name, PyObject *args,
Py_LOCAL_INLINE(Py_ssize_t)
find_internal(const char *str, Py_ssize_t len,
- const char *function_name, PyObject *args, int dir)
+ const char *function_name, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end,
+ int dir)
{
- PyObject *subobj;
char byte;
Py_buffer subbuf;
const char *sub;
Py_ssize_t sub_len;
- Py_ssize_t start = 0, end = PY_SSIZE_T_MAX;
Py_ssize_t res;
- if (!parse_args_finds_byte(function_name, args,
- &subobj, &byte, &start, &end))
+ if (!parse_args_finds_byte(function_name, &subobj, &byte)) {
return -2;
+ }
if (subobj) {
if (PyObject_GetBuffer(subobj, &subbuf, PyBUF_SIMPLE) != 0)
@@ -567,37 +556,21 @@ find_internal(const char *str, Py_ssize_t len,
return res;
}
-PyDoc_STRVAR_shared(_Py_find__doc__,
-"B.find(sub[, start[, end]]) -> int\n\
-\n\
-Return the lowest index in B where subsection sub is found,\n\
-such that sub is contained within B[start,end]. Optional\n\
-arguments start and end are interpreted as in slice notation.\n\
-\n\
-Return -1 on failure.");
-
PyObject *
-_Py_bytes_find(const char *str, Py_ssize_t len, PyObject *args)
+_Py_bytes_find(const char *str, Py_ssize_t len, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
{
- Py_ssize_t result = find_internal(str, len, "find", args, +1);
+ Py_ssize_t result = find_internal(str, len, "find", sub, start, end, +1);
if (result == -2)
return NULL;
return PyLong_FromSsize_t(result);
}
-PyDoc_STRVAR_shared(_Py_index__doc__,
-"B.index(sub[, start[, end]]) -> int\n\
-\n\
-Return the lowest index in B where subsection sub is found,\n\
-such that sub is contained within B[start,end]. Optional\n\
-arguments start and end are interpreted as in slice notation.\n\
-\n\
-Raises ValueError when the subsection is not found.");
-
PyObject *
-_Py_bytes_index(const char *str, Py_ssize_t len, PyObject *args)
+_Py_bytes_index(const char *str, Py_ssize_t len, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
{
- Py_ssize_t result = find_internal(str, len, "index", args, +1);
+ Py_ssize_t result = find_internal(str, len, "index", sub, start, end, +1);
if (result == -2)
return NULL;
if (result == -1) {
@@ -608,37 +581,21 @@ _Py_bytes_index(const char *str, Py_ssize_t len, PyObject *args)
return PyLong_FromSsize_t(result);
}
-PyDoc_STRVAR_shared(_Py_rfind__doc__,
-"B.rfind(sub[, start[, end]]) -> int\n\
-\n\
-Return the highest index in B where subsection sub is found,\n\
-such that sub is contained within B[start,end]. Optional\n\
-arguments start and end are interpreted as in slice notation.\n\
-\n\
-Return -1 on failure.");
-
PyObject *
-_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *args)
+_Py_bytes_rfind(const char *str, Py_ssize_t len, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
{
- Py_ssize_t result = find_internal(str, len, "rfind", args, -1);
+ Py_ssize_t result = find_internal(str, len, "rfind", sub, start, end, -1);
if (result == -2)
return NULL;
return PyLong_FromSsize_t(result);
}
-PyDoc_STRVAR_shared(_Py_rindex__doc__,
-"B.rindex(sub[, start[, end]]) -> int\n\
-\n\
-Return the highest index in B where subsection sub is found,\n\
-such that sub is contained within B[start,end]. Optional\n\
-arguments start and end are interpreted as in slice notation.\n\
-\n\
-Raise ValueError when the subsection is not found.");
-
PyObject *
-_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args)
+_Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end)
{
- Py_ssize_t result = find_internal(str, len, "rindex", args, -1);
+ Py_ssize_t result = find_internal(str, len, "rindex", sub, start, end, -1);
if (result == -2)
return NULL;
if (result == -1) {
@@ -649,28 +606,20 @@ _Py_bytes_rindex(const char *str, Py_ssize_t len, PyObject *args)
return PyLong_FromSsize_t(result);
}
-PyDoc_STRVAR_shared(_Py_count__doc__,
-"B.count(sub[, start[, end]]) -> int\n\
-\n\
-Return the number of non-overlapping occurrences of subsection sub in\n\
-bytes B[start:end]. Optional arguments start and end are interpreted\n\
-as in slice notation.");
-
PyObject *
-_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *args)
+_Py_bytes_count(const char *str, Py_ssize_t len, PyObject *sub_obj,
+ Py_ssize_t start, Py_ssize_t end)
{
- PyObject *sub_obj;
const char *sub;
Py_ssize_t sub_len;
char byte;
- Py_ssize_t start = 0, end = PY_SSIZE_T_MAX;
Py_buffer vsub;
PyObject *count_obj;
- if (!parse_args_finds_byte("count", args,
- &sub_obj, &byte, &start, &end))
+ if (!parse_args_finds_byte("count", &sub_obj, &byte)) {
return NULL;
+ }
if (sub_obj) {
if (PyObject_GetBuffer(sub_obj, &vsub, PyBUF_SIMPLE) != 0)
@@ -772,66 +721,47 @@ notfound:
static PyObject *
_Py_bytes_tailmatch(const char *str, Py_ssize_t len,
- const char *function_name, PyObject *args,
+ const char *function_name, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end,
int direction)
{
- Py_ssize_t start = 0;
- Py_ssize_t end = PY_SSIZE_T_MAX;
- PyObject *subobj = NULL;
- int result;
-
- if (!stringlib_parse_args_finds(function_name, args, &subobj, &start, &end))
- return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
- result = tailmatch(str, len, PyTuple_GET_ITEM(subobj, i),
- start, end, direction);
- if (result == -1)
+ PyObject *item = PyTuple_GET_ITEM(subobj, i);
+ int result = tailmatch(str, len, item, start, end, direction);
+ if (result < 0) {
return NULL;
+ }
else if (result) {
Py_RETURN_TRUE;
}
}
Py_RETURN_FALSE;
}
- result = tailmatch(str, len, subobj, start, end, direction);
+ int result = tailmatch(str, len, subobj, start, end, direction);
if (result == -1) {
- if (PyErr_ExceptionMatches(PyExc_TypeError))
+ if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Format(PyExc_TypeError,
"%s first arg must be bytes or a tuple of bytes, "
"not %s",
function_name, Py_TYPE(subobj)->tp_name);
+ }
return NULL;
}
- else
- return PyBool_FromLong(result);
+ return PyBool_FromLong(result);
}
-PyDoc_STRVAR_shared(_Py_startswith__doc__,
-"B.startswith(prefix[, start[, end]]) -> bool\n\
-\n\
-Return True if B starts with the specified prefix, False otherwise.\n\
-With optional start, test B beginning at that position.\n\
-With optional end, stop comparing B at that position.\n\
-prefix can also be a tuple of bytes to try.");
-
PyObject *
-_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *args)
+_Py_bytes_startswith(const char *str, Py_ssize_t len, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end)
{
- return _Py_bytes_tailmatch(str, len, "startswith", args, -1);
+ return _Py_bytes_tailmatch(str, len, "startswith", subobj, start, end, -1);
}
-PyDoc_STRVAR_shared(_Py_endswith__doc__,
-"B.endswith(suffix[, start[, end]]) -> bool\n\
-\n\
-Return True if B ends with the specified suffix, False otherwise.\n\
-With optional start, test B beginning at that position.\n\
-With optional end, stop comparing B at that position.\n\
-suffix can also be a tuple of bytes to try.");
-
PyObject *
-_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *args)
+_Py_bytes_endswith(const char *str, Py_ssize_t len, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end)
{
- return _Py_bytes_tailmatch(str, len, "endswith", args, +1);
+ return _Py_bytes_tailmatch(str, len, "endswith", subobj, start, end, +1);
}
diff --git a/contrib/tools/python3/Objects/bytesobject.c b/contrib/tools/python3/Objects/bytesobject.c
index f3a978c86c3..d891121589f 100644
--- a/contrib/tools/python3/Objects/bytesobject.c
+++ b/contrib/tools/python3/Objects/bytesobject.c
@@ -1,14 +1,13 @@
/* bytes object implementation */
-#define PY_SSIZE_T_CLEAN
-
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
-#include "pycore_bytesobject.h" // _PyBytes_Find(), _PyBytes_Repeat()
#include "pycore_bytes_methods.h" // _Py_bytes_startswith()
+#include "pycore_bytesobject.h" // _PyBytes_Find(), _PyBytes_Repeat()
#include "pycore_call.h" // _PyObject_CallNoArgs()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_format.h" // F_LJUST
-#include "pycore_global_objects.h" // _Py_GET_GLOBAL_OBJECT()
+#include "pycore_global_objects.h"// _Py_GET_GLOBAL_OBJECT()
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_long.h" // _PyLong_DigitValue
#include "pycore_object.h" // _PyObject_GC_TRACK
@@ -43,17 +42,39 @@ Py_LOCAL_INLINE(Py_ssize_t) _PyBytesWriter_GetSize(_PyBytesWriter *writer,
#define EMPTY (&_Py_SINGLETON(bytes_empty))
-// Return a borrowed reference to the empty bytes string singleton.
+// Return a reference to the immortal empty bytes string singleton.
static inline PyObject* bytes_get_empty(void)
{
- return &EMPTY->ob_base.ob_base;
+ PyObject *empty = &EMPTY->ob_base.ob_base;
+ assert(_Py_IsImmortalLoose(empty));
+ return empty;
}
-// Return a strong reference to the empty bytes string singleton.
-static inline PyObject* bytes_new_empty(void)
+static inline void
+set_ob_shash(PyBytesObject *a, Py_hash_t hash)
{
- return Py_NewRef(EMPTY);
+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
+#ifdef Py_GIL_DISABLED
+ _Py_atomic_store_ssize_relaxed(&a->ob_shash, hash);
+#else
+ a->ob_shash = hash;
+#endif
+_Py_COMP_DIAG_POP
+}
+
+static inline Py_hash_t
+get_ob_shash(PyBytesObject *a)
+{
+_Py_COMP_DIAG_PUSH
+_Py_COMP_DIAG_IGNORE_DEPR_DECLS
+#ifdef Py_GIL_DISABLED
+ return _Py_atomic_load_ssize_relaxed(&a->ob_shash);
+#else
+ return a->ob_shash;
+#endif
+_Py_COMP_DIAG_POP
}
@@ -86,7 +107,7 @@ _PyBytes_FromSize(Py_ssize_t size, int use_calloc)
assert(size >= 0);
if (size == 0) {
- return bytes_new_empty();
+ return bytes_get_empty();
}
if ((size_t)size > (size_t)PY_SSIZE_T_MAX - PyBytesObject_SIZE) {
@@ -104,10 +125,7 @@ _PyBytes_FromSize(Py_ssize_t size, int use_calloc)
return PyErr_NoMemory();
}
_PyObject_InitVar((PyVarObject*)op, &PyBytes_Type, size);
-_Py_COMP_DIAG_PUSH
-_Py_COMP_DIAG_IGNORE_DEPR_DECLS
- op->ob_shash = -1;
-_Py_COMP_DIAG_POP
+ set_ob_shash(op, -1);
if (!use_calloc) {
op->ob_sval[size] = '\0';
}
@@ -125,10 +143,11 @@ PyBytes_FromStringAndSize(const char *str, Py_ssize_t size)
}
if (size == 1 && str != NULL) {
op = CHARACTER(*str & 255);
- return Py_NewRef(op);
+ assert(_Py_IsImmortalLoose(op));
+ return (PyObject *)op;
}
if (size == 0) {
- return bytes_new_empty();
+ return bytes_get_empty();
}
op = (PyBytesObject *)_PyBytes_FromSize(size, 0);
@@ -156,11 +175,12 @@ PyBytes_FromString(const char *str)
}
if (size == 0) {
- return bytes_new_empty();
+ return bytes_get_empty();
}
else if (size == 1) {
op = CHARACTER(*str & 255);
- return Py_NewRef(op);
+ assert(_Py_IsImmortalLoose(op));
+ return (PyObject *)op;
}
/* Inline PyObject_NewVar */
@@ -169,10 +189,7 @@ PyBytes_FromString(const char *str)
return PyErr_NoMemory();
}
_PyObject_InitVar((PyVarObject*)op, &PyBytes_Type, size);
-_Py_COMP_DIAG_PUSH
-_Py_COMP_DIAG_IGNORE_DEPR_DECLS
- op->ob_shash = -1;
-_Py_COMP_DIAG_POP
+ set_ob_shash(op, -1);
memcpy(op->ob_sval, str, size+1);
return (PyObject *) op;
}
@@ -726,11 +743,11 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
if (--fmtcnt >= 0)
c = *fmt++;
}
- else if (c >= 0 && isdigit(c)) {
+ else if (c >= 0 && Py_ISDIGIT(c)) {
width = c - '0';
while (--fmtcnt >= 0) {
c = Py_CHARMASK(*fmt++);
- if (!isdigit(c))
+ if (!Py_ISDIGIT(c))
break;
if (width > (PY_SSIZE_T_MAX - ((int)c - '0')) / 10) {
PyErr_SetString(
@@ -757,7 +774,7 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
"* wants int");
goto error;
}
- prec = _PyLong_AsInt(v);
+ prec = PyLong_AsInt(v);
if (prec == -1 && PyErr_Occurred())
goto error;
if (prec < 0)
@@ -765,11 +782,11 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
if (--fmtcnt >= 0)
c = *fmt++;
}
- else if (c >= 0 && isdigit(c)) {
+ else if (c >= 0 && Py_ISDIGIT(c)) {
prec = c - '0';
while (--fmtcnt >= 0) {
c = Py_CHARMASK(*fmt++);
- if (!isdigit(c))
+ if (!Py_ISDIGIT(c))
break;
if (prec > (INT_MAX - ((int)c - '0')) / 10) {
PyErr_SetString(
@@ -948,8 +965,10 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
/* 2: size preallocated for %s */
if (alloc > 2) {
res = _PyBytesWriter_Prepare(&writer, res, alloc - 2);
- if (res == NULL)
+ if (res == NULL) {
+ Py_XDECREF(temp);
goto error;
+ }
}
#ifndef NDEBUG
char *before = res;
@@ -1048,10 +1067,11 @@ _PyBytes_FormatEx(const char *format, Py_ssize_t format_len,
}
/* Unescape a backslash-escaped string. */
-PyObject *_PyBytes_DecodeEscape(const char *s,
+PyObject *_PyBytes_DecodeEscape2(const char *s,
Py_ssize_t len,
const char *errors,
- const char **first_invalid_escape)
+ int *first_invalid_escape_char,
+ const char **first_invalid_escape_ptr)
{
int c;
char *p;
@@ -1065,7 +1085,8 @@ PyObject *_PyBytes_DecodeEscape(const char *s,
return NULL;
writer.overallocate = 1;
- *first_invalid_escape = NULL;
+ *first_invalid_escape_char = -1;
+ *first_invalid_escape_ptr = NULL;
end = s + len;
while (s < end) {
@@ -1103,9 +1124,10 @@ PyObject *_PyBytes_DecodeEscape(const char *s,
c = (c<<3) + *s++ - '0';
}
if (c > 0377) {
- if (*first_invalid_escape == NULL) {
- *first_invalid_escape = s-3; /* Back up 3 chars, since we've
- already incremented s. */
+ if (*first_invalid_escape_char == -1) {
+ *first_invalid_escape_char = c;
+ /* Back up 3 chars, since we've already incremented s. */
+ *first_invalid_escape_ptr = s - 3;
}
}
*p++ = c;
@@ -1146,9 +1168,10 @@ PyObject *_PyBytes_DecodeEscape(const char *s,
break;
default:
- if (*first_invalid_escape == NULL) {
- *first_invalid_escape = s-1; /* Back up one char, since we've
- already incremented s. */
+ if (*first_invalid_escape_char == -1) {
+ *first_invalid_escape_char = (unsigned char)s[-1];
+ /* Back up one char, since we've already incremented s. */
+ *first_invalid_escape_ptr = s - 1;
}
*p++ = '\\';
s--;
@@ -1162,23 +1185,37 @@ PyObject *_PyBytes_DecodeEscape(const char *s,
return NULL;
}
+// Export for binary compatibility.
+PyObject *_PyBytes_DecodeEscape(const char *s,
+ Py_ssize_t len,
+ const char *errors,
+ const char **first_invalid_escape)
+{
+ int first_invalid_escape_char;
+ return _PyBytes_DecodeEscape2(
+ s, len, errors,
+ &first_invalid_escape_char,
+ first_invalid_escape);
+}
+
PyObject *PyBytes_DecodeEscape(const char *s,
Py_ssize_t len,
const char *errors,
Py_ssize_t Py_UNUSED(unicode),
const char *Py_UNUSED(recode_encoding))
{
- const char* first_invalid_escape;
- PyObject *result = _PyBytes_DecodeEscape(s, len, errors,
- &first_invalid_escape);
+ int first_invalid_escape_char;
+ const char *first_invalid_escape_ptr;
+ PyObject *result = _PyBytes_DecodeEscape2(s, len, errors,
+ &first_invalid_escape_char,
+ &first_invalid_escape_ptr);
if (result == NULL)
return NULL;
- if (first_invalid_escape != NULL) {
- unsigned char c = *first_invalid_escape;
- if ('4' <= c && c <= '7') {
+ if (first_invalid_escape_char != -1) {
+ if (first_invalid_escape_char > 0xff) {
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
- "invalid octal escape sequence '\\%.3s'",
- first_invalid_escape) < 0)
+ "invalid octal escape sequence '\\%o'",
+ first_invalid_escape_char) < 0)
{
Py_DECREF(result);
return NULL;
@@ -1187,7 +1224,7 @@ PyObject *PyBytes_DecodeEscape(const char *s,
else {
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"invalid escape sequence '\\%c'",
- c) < 0)
+ first_invalid_escape_char) < 0)
{
Py_DECREF(result);
return NULL;
@@ -1479,10 +1516,7 @@ bytes_repeat(PyBytesObject *a, Py_ssize_t n)
return PyErr_NoMemory();
}
_PyObject_InitVar((PyVarObject*)op, &PyBytes_Type, size);
-_Py_COMP_DIAG_PUSH
-_Py_COMP_DIAG_IGNORE_DEPR_DECLS
- op->ob_shash = -1;
-_Py_COMP_DIAG_POP
+ set_ob_shash(op, -1);
op->ob_sval[size] = '\0';
_PyBytes_Repeat(op->ob_sval, size, a->ob_sval, Py_SIZE(a));
@@ -1587,14 +1621,13 @@ bytes_richcompare(PyBytesObject *a, PyBytesObject *b, int op)
static Py_hash_t
bytes_hash(PyBytesObject *a)
{
-_Py_COMP_DIAG_PUSH
-_Py_COMP_DIAG_IGNORE_DEPR_DECLS
- if (a->ob_shash == -1) {
+ Py_hash_t hash = get_ob_shash(a);
+ if (hash == -1) {
/* Can't fail */
- a->ob_shash = _Py_HashBytes(a->ob_sval, Py_SIZE(a));
+ hash = _Py_HashBytes(a->ob_sval, Py_SIZE(a));
+ set_ob_shash(a, hash);
}
- return a->ob_shash;
-_Py_COMP_DIAG_POP
+ return hash;
}
static PyObject*
@@ -1867,30 +1900,80 @@ _PyBytes_Join(PyObject *sep, PyObject *x)
return bytes_join((PyBytesObject*)sep, x);
}
+/*[clinic input]
+@text_signature "($self, sub[, start[, end]], /)"
+bytes.find
+
+ sub: object
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ Optional start position. Default: start of the bytes.
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ Optional stop position. Default: end of the bytes.
+ /
+
+Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end].
+
+Return -1 on failure.
+[clinic start generated code]*/
+
static PyObject *
-bytes_find(PyBytesObject *self, PyObject *args)
+bytes_find_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=d5961a1c77b472a1 input=3171e62a8ae7f240]*/
{
- return _Py_bytes_find(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
+ return _Py_bytes_find(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
+ sub, start, end);
}
+/*[clinic input]
+bytes.index = bytes.find
+
+Return the lowest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end].
+
+Raise ValueError if the subsection is not found.
+[clinic start generated code]*/
+
static PyObject *
-bytes_index(PyBytesObject *self, PyObject *args)
+bytes_index_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=0da25cc74683ba42 input=aa34ad71ba0bafe3]*/
{
- return _Py_bytes_index(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
+ return _Py_bytes_index(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
+ sub, start, end);
}
+/*[clinic input]
+bytes.rfind = bytes.find
+
+Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end].
+
+Return -1 on failure.
+[clinic start generated code]*/
static PyObject *
-bytes_rfind(PyBytesObject *self, PyObject *args)
+bytes_rfind_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=51b60fa4ad011c09 input=864c3e7f3010b33c]*/
{
- return _Py_bytes_rfind(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
+ return _Py_bytes_rfind(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
+ sub, start, end);
}
+/*[clinic input]
+bytes.rindex = bytes.find
+
+Return the highest index in B where subsection 'sub' is found, such that 'sub' is contained within B[start,end].
+
+Raise ValueError if the subsection is not found.
+[clinic start generated code]*/
static PyObject *
-bytes_rindex(PyBytesObject *self, PyObject *args)
+bytes_rindex_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=42bf674e0a0aabf6 input=21051fc5cfeacf2c]*/
{
- return _Py_bytes_rindex(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
+ return _Py_bytes_rindex(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
+ sub, start, end);
}
@@ -2027,10 +2110,19 @@ bytes_rstrip_impl(PyBytesObject *self, PyObject *bytes)
}
+/*[clinic input]
+bytes.count = bytes.find
+
+Return the number of non-overlapping occurrences of subsection 'sub' in bytes B[start:end].
+[clinic start generated code]*/
+
static PyObject *
-bytes_count(PyBytesObject *self, PyObject *args)
+bytes_count_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=9848140b9be17d0f input=b6e4a5ed515e1e59]*/
{
- return _Py_bytes_count(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
+ return _Py_bytes_count(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
+ sub, start, end);
}
@@ -2171,7 +2263,7 @@ bytes.maketrans
to: Py_buffer
/
-Return a translation table useable for the bytes or bytearray translate method.
+Return a translation table usable for the bytes or bytearray translate method.
The returned table will be one where each byte in frm is mapped to the byte at
the same position in to.
@@ -2181,7 +2273,7 @@ The bytes objects frm and to must be of the same length.
static PyObject *
bytes_maketrans_impl(Py_buffer *frm, Py_buffer *to)
-/*[clinic end generated code: output=a36f6399d4b77f6f input=de7a8fc5632bb8f1]*/
+/*[clinic end generated code: output=a36f6399d4b77f6f input=a3bd00d430a0979f]*/
{
return _Py_bytes_maketrans(frm, to);
}
@@ -2289,16 +2381,52 @@ bytes_removesuffix_impl(PyBytesObject *self, Py_buffer *suffix)
return PyBytes_FromStringAndSize(self_start, self_len);
}
+/*[clinic input]
+@text_signature "($self, prefix[, start[, end]], /)"
+bytes.startswith
+
+ prefix as subobj: object
+ A bytes or a tuple of bytes to try.
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ Optional start position. Default: start of the bytes.
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ Optional stop position. Default: end of the bytes.
+ /
+
+Return True if the bytes starts with the specified prefix, False otherwise.
+[clinic start generated code]*/
+
static PyObject *
-bytes_startswith(PyBytesObject *self, PyObject *args)
+bytes_startswith_impl(PyBytesObject *self, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end)
+/*[clinic end generated code: output=b1e8da1cbd528e8c input=8a4165df8adfa6c9]*/
{
- return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
+ return _Py_bytes_startswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
+ subobj, start, end);
}
+/*[clinic input]
+@text_signature "($self, suffix[, start[, end]], /)"
+bytes.endswith
+
+ suffix as subobj: object
+ A bytes or a tuple of bytes to try.
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ Optional start position. Default: start of the bytes.
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ Optional stop position. Default: end of the bytes.
+ /
+
+Return True if the bytes ends with the specified suffix, False otherwise.
+[clinic start generated code]*/
+
static PyObject *
-bytes_endswith(PyBytesObject *self, PyObject *args)
+bytes_endswith_impl(PyBytesObject *self, PyObject *subobj, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=038b633111f3629d input=b5c3407a2a5c9aac]*/
{
- return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self), args);
+ return _Py_bytes_endswith(PyBytes_AS_STRING(self), PyBytes_GET_SIZE(self),
+ subobj, start, end);
}
@@ -2384,8 +2512,6 @@ _PyBytes_FromHex(PyObject *string, int use_bytearray)
writer.use_bytearray = use_bytearray;
assert(PyUnicode_Check(string));
- if (PyUnicode_READY(string))
- return NULL;
hexlen = PyUnicode_GET_LENGTH(string);
if (!PyUnicode_IS_ASCII(string)) {
@@ -2494,17 +2620,14 @@ bytes_methods[] = {
{"capitalize", stringlib_capitalize, METH_NOARGS,
_Py_capitalize__doc__},
STRINGLIB_CENTER_METHODDEF
- {"count", (PyCFunction)bytes_count, METH_VARARGS,
- _Py_count__doc__},
+ BYTES_COUNT_METHODDEF
BYTES_DECODE_METHODDEF
- {"endswith", (PyCFunction)bytes_endswith, METH_VARARGS,
- _Py_endswith__doc__},
+ BYTES_ENDSWITH_METHODDEF
STRINGLIB_EXPANDTABS_METHODDEF
- {"find", (PyCFunction)bytes_find, METH_VARARGS,
- _Py_find__doc__},
+ BYTES_FIND_METHODDEF
BYTES_FROMHEX_METHODDEF
BYTES_HEX_METHODDEF
- {"index", (PyCFunction)bytes_index, METH_VARARGS, _Py_index__doc__},
+ BYTES_INDEX_METHODDEF
{"isalnum", stringlib_isalnum, METH_NOARGS,
_Py_isalnum__doc__},
{"isalpha", stringlib_isalpha, METH_NOARGS,
@@ -2530,16 +2653,15 @@ bytes_methods[] = {
BYTES_REPLACE_METHODDEF
BYTES_REMOVEPREFIX_METHODDEF
BYTES_REMOVESUFFIX_METHODDEF
- {"rfind", (PyCFunction)bytes_rfind, METH_VARARGS, _Py_rfind__doc__},
- {"rindex", (PyCFunction)bytes_rindex, METH_VARARGS, _Py_rindex__doc__},
+ BYTES_RFIND_METHODDEF
+ BYTES_RINDEX_METHODDEF
STRINGLIB_RJUST_METHODDEF
BYTES_RPARTITION_METHODDEF
BYTES_RSPLIT_METHODDEF
BYTES_RSTRIP_METHODDEF
BYTES_SPLIT_METHODDEF
BYTES_SPLITLINES_METHODDEF
- {"startswith", (PyCFunction)bytes_startswith, METH_VARARGS,
- _Py_startswith__doc__},
+ BYTES_STARTSWITH_METHODDEF
BYTES_STRIP_METHODDEF
{"swapcase", stringlib_swapcase, METH_NOARGS,
_Py_swapcase__doc__},
@@ -2885,10 +3007,7 @@ bytes_alloc(PyTypeObject *self, Py_ssize_t nitems)
if (obj == NULL) {
return NULL;
}
-_Py_COMP_DIAG_PUSH
-_Py_COMP_DIAG_IGNORE_DEPR_DECLS
- obj->ob_shash = -1;
-_Py_COMP_DIAG_POP
+ set_ob_shash(obj, -1);
return (PyObject*)obj;
}
@@ -2905,11 +3024,8 @@ bytes_subtype_new(PyTypeObject *type, PyObject *tmp)
if (pnew != NULL) {
memcpy(PyBytes_AS_STRING(pnew),
PyBytes_AS_STRING(tmp), n+1);
-_Py_COMP_DIAG_PUSH
-_Py_COMP_DIAG_IGNORE_DEPR_DECLS
- ((PyBytesObject *)pnew)->ob_shash =
- ((PyBytesObject *)tmp)->ob_shash;
-_Py_COMP_DIAG_POP
+ set_ob_shash((PyBytesObject *)pnew,
+ get_ob_shash((PyBytesObject *)tmp));
}
return pnew;
}
@@ -3031,11 +3147,9 @@ PyBytes_ConcatAndDel(PyObject **pv, PyObject *w)
/* The following function breaks the notion that bytes are immutable:
- it changes the size of a bytes object. We get away with this only if there
- is only one module referencing the object. You can also think of it
+ it changes the size of a bytes object. You can think of it
as creating a new bytes object and destroying the old one, only
- more efficiently. In any case, don't use this if the bytes object may
- already be known to some other part of the code...
+ more efficiently.
Note that if there's not enough memory to resize the bytes object, the
original bytes object at *pv is deallocated, *pv is set to NULL, an "out of
memory" exception is set, and -1 is returned. Else (on success) 0 is
@@ -3051,36 +3165,49 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
PyBytesObject *sv;
v = *pv;
if (!PyBytes_Check(v) || newsize < 0) {
- goto error;
+ *pv = 0;
+ Py_DECREF(v);
+ PyErr_BadInternalCall();
+ return -1;
}
- if (Py_SIZE(v) == newsize) {
+ Py_ssize_t oldsize = PyBytes_GET_SIZE(v);
+ if (oldsize == newsize) {
/* return early if newsize equals to v->ob_size */
return 0;
}
- if (Py_SIZE(v) == 0) {
- if (newsize == 0) {
- return 0;
- }
+ if (oldsize == 0) {
*pv = _PyBytes_FromSize(newsize, 0);
Py_DECREF(v);
return (*pv == NULL) ? -1 : 0;
}
- if (Py_REFCNT(v) != 1) {
- goto error;
- }
if (newsize == 0) {
- *pv = bytes_new_empty();
+ *pv = bytes_get_empty();
Py_DECREF(v);
return 0;
}
+ if (Py_REFCNT(v) != 1) {
+ if (oldsize < newsize) {
+ *pv = _PyBytes_FromSize(newsize, 0);
+ if (*pv) {
+ memcpy(PyBytes_AS_STRING(*pv), PyBytes_AS_STRING(v), oldsize);
+ }
+ }
+ else {
+ *pv = PyBytes_FromStringAndSize(PyBytes_AS_STRING(v), newsize);
+ }
+ Py_DECREF(v);
+ return (*pv == NULL) ? -1 : 0;
+ }
+
#ifdef Py_TRACE_REFS
_Py_ForgetReference(v);
#endif
+ _PyReftracerTrack(v, PyRefTracer_DESTROY);
*pv = (PyObject *)
PyObject_Realloc(v, PyBytesObject_SIZE + newsize);
if (*pv == NULL) {
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal(_PyInterpreterState_GET());
+ _Py_DecRefTotal(_PyThreadState_GET());
#endif
PyObject_Free(v);
PyErr_NoMemory();
@@ -3090,16 +3217,8 @@ _PyBytes_Resize(PyObject **pv, Py_ssize_t newsize)
sv = (PyBytesObject *) *pv;
Py_SET_SIZE(sv, newsize);
sv->ob_sval[newsize] = '\0';
-_Py_COMP_DIAG_PUSH
-_Py_COMP_DIAG_IGNORE_DEPR_DECLS
- sv->ob_shash = -1; /* invalidate cached hash value */
-_Py_COMP_DIAG_POP
+ set_ob_shash(sv, -1); /* invalidate cached hash value */
return 0;
-error:
- *pv = 0;
- Py_DECREF(v);
- PyErr_BadInternalCall();
- return -1;
}
diff --git a/contrib/tools/python3/Objects/call.c b/contrib/tools/python3/Objects/call.c
index 0d548dcd5e1..6e331a43899 100644
--- a/contrib/tools/python3/Objects/call.c
+++ b/contrib/tools/python3/Objects/call.c
@@ -2,6 +2,8 @@
#include "pycore_call.h" // _PyObject_CallNoArgsTstate()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
#include "pycore_dict.h" // _PyDict_FromItems()
+#include "pycore_function.h" // _PyFunction_Vectorcall() definition
+#include "pycore_modsupport.h" // _Py_VaBuildStack()
#include "pycore_object.h" // _PyCFunctionWithKeywords_TrampolineCall()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
@@ -106,9 +108,9 @@ PyObject_CallNoArgs(PyObject *func)
PyObject *
-_PyObject_FastCallDictTstate(PyThreadState *tstate, PyObject *callable,
- PyObject *const *args, size_t nargsf,
- PyObject *kwargs)
+_PyObject_VectorcallDictTstate(PyThreadState *tstate, PyObject *callable,
+ PyObject *const *args, size_t nargsf,
+ PyObject *kwargs)
{
assert(callable != NULL);
@@ -122,7 +124,7 @@ _PyObject_FastCallDictTstate(PyThreadState *tstate, PyObject *callable,
assert(nargs == 0 || args != NULL);
assert(kwargs == NULL || PyDict_Check(kwargs));
- vectorcallfunc func = _PyVectorcall_Function(callable);
+ vectorcallfunc func = PyVectorcall_Function(callable);
if (func == NULL) {
/* Use tp_call instead */
return _PyObject_MakeTpCall(tstate, callable, args, nargs, kwargs);
@@ -154,7 +156,7 @@ PyObject_VectorcallDict(PyObject *callable, PyObject *const *args,
size_t nargsf, PyObject *kwargs)
{
PyThreadState *tstate = _PyThreadState_GET();
- return _PyObject_FastCallDictTstate(tstate, callable, args, nargsf, kwargs);
+ return _PyObject_VectorcallDictTstate(tstate, callable, args, nargsf, kwargs);
}
static void
@@ -172,7 +174,7 @@ object_is_not_callable(PyThreadState *tstate, PyObject *callable)
goto basic_type_error;
}
PyObject *attr;
- int res = _PyObject_LookupAttr(callable, name, &attr);
+ int res = PyObject_GetOptionalAttr(callable, name, &attr);
if (res < 0) {
_PyErr_Clear(tstate);
}
@@ -328,14 +330,6 @@ PyObject_Vectorcall(PyObject *callable, PyObject *const *args,
PyObject *
-_PyObject_FastCall(PyObject *func, PyObject *const *args, Py_ssize_t nargs)
-{
- PyThreadState *tstate = _PyThreadState_GET();
- return _PyObject_FastCallTstate(tstate, func, args, nargs);
-}
-
-
-PyObject *
_PyObject_Call(PyThreadState *tstate, PyObject *callable,
PyObject *args, PyObject *kwargs)
{
@@ -349,7 +343,7 @@ _PyObject_Call(PyThreadState *tstate, PyObject *callable,
assert(PyTuple_Check(args));
assert(kwargs == NULL || PyDict_Check(kwargs));
EVAL_CALL_STAT_INC_IF_FUNCTION(EVAL_CALL_API, callable);
- vectorcallfunc vector_func = _PyVectorcall_Function(callable);
+ vectorcallfunc vector_func = PyVectorcall_Function(callable);
if (vector_func != NULL) {
return _PyVectorcall_Call(tstate, vector_func, callable, args, kwargs);
}
@@ -380,11 +374,11 @@ PyObject_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
}
-PyObject *
+/* Function removed in the Python 3.13 API but kept in the stable ABI. */
+PyAPI_FUNC(PyObject *)
PyCFunction_Call(PyObject *callable, PyObject *args, PyObject *kwargs)
{
- PyThreadState *tstate = _PyThreadState_GET();
- return _PyObject_Call(tstate, callable, args, kwargs);
+ return PyObject_Call(callable, args, kwargs);
}
@@ -426,8 +420,9 @@ _PyFunction_Vectorcall(PyObject *func, PyObject* const* stack,
/* --- More complex call functions -------------------------------- */
/* External interface to call any callable object.
- The args must be a tuple or NULL. The kwargs must be a dict or NULL. */
-PyObject *
+ The args must be a tuple or NULL. The kwargs must be a dict or NULL.
+ Function removed in Python 3.13 API but kept in the stable ABI. */
+PyAPI_FUNC(PyObject*)
PyEval_CallObjectWithKeywords(PyObject *callable,
PyObject *args, PyObject *kwargs)
{
@@ -452,7 +447,8 @@ PyEval_CallObjectWithKeywords(PyObject *callable,
}
if (args == NULL) {
- return _PyObject_FastCallDictTstate(tstate, callable, NULL, 0, kwargs);
+ return _PyObject_VectorcallDictTstate(tstate, callable,
+ NULL, 0, kwargs);
}
else {
return _PyObject_Call(tstate, callable, args, kwargs);
@@ -505,9 +501,9 @@ _PyObject_Call_Prepend(PyThreadState *tstate, PyObject *callable,
_PyTuple_ITEMS(args),
argcount * sizeof(PyObject *));
- PyObject *result = _PyObject_FastCallDictTstate(tstate, callable,
- stack, argcount + 1,
- kwargs);
+ PyObject *result = _PyObject_VectorcallDictTstate(tstate, callable,
+ stack, argcount + 1,
+ kwargs);
if (stack != small_stack) {
PyMem_Free(stack);
}
@@ -519,7 +515,7 @@ _PyObject_Call_Prepend(PyThreadState *tstate, PyObject *callable,
static PyObject *
_PyObject_CallFunctionVa(PyThreadState *tstate, PyObject *callable,
- const char *format, va_list va, int is_size_t)
+ const char *format, va_list va)
{
PyObject* small_stack[_PY_FASTCALL_SMALL_STACK];
const Py_ssize_t small_stack_len = Py_ARRAY_LENGTH(small_stack);
@@ -535,14 +531,8 @@ _PyObject_CallFunctionVa(PyThreadState *tstate, PyObject *callable,
return _PyObject_CallNoArgsTstate(tstate, callable);
}
- if (is_size_t) {
- stack = _Py_VaBuildStack_SizeT(small_stack, small_stack_len,
- format, va, &nargs);
- }
- else {
- stack = _Py_VaBuildStack(small_stack, small_stack_len,
- format, va, &nargs);
- }
+ stack = _Py_VaBuildStack(small_stack, small_stack_len,
+ format, va, &nargs);
if (stack == NULL) {
return NULL;
}
@@ -581,7 +571,7 @@ PyObject_CallFunction(PyObject *callable, const char *format, ...)
PyThreadState *tstate = _PyThreadState_GET();
va_start(va, format);
- result = _PyObject_CallFunctionVa(tstate, callable, format, va, 0);
+ result = _PyObject_CallFunctionVa(tstate, callable, format, va);
va_end(va);
return result;
@@ -589,9 +579,8 @@ PyObject_CallFunction(PyObject *callable, const char *format, ...)
/* PyEval_CallFunction is exact copy of PyObject_CallFunction.
- * This function is kept for backward compatibility.
- */
-PyObject *
+ Function removed in Python 3.13 API but kept in the stable ABI. */
+PyAPI_FUNC(PyObject*)
PyEval_CallFunction(PyObject *callable, const char *format, ...)
{
va_list va;
@@ -599,21 +588,24 @@ PyEval_CallFunction(PyObject *callable, const char *format, ...)
PyThreadState *tstate = _PyThreadState_GET();
va_start(va, format);
- result = _PyObject_CallFunctionVa(tstate, callable, format, va, 0);
+ result = _PyObject_CallFunctionVa(tstate, callable, format, va);
va_end(va);
return result;
}
-PyObject *
+/* _PyObject_CallFunction_SizeT is exact copy of PyObject_CallFunction.
+ * This function must be kept because it is part of the stable ABI.
+ */
+PyAPI_FUNC(PyObject *) /* abi_only */
_PyObject_CallFunction_SizeT(PyObject *callable, const char *format, ...)
{
PyThreadState *tstate = _PyThreadState_GET();
va_list va;
va_start(va, format);
- PyObject *result = _PyObject_CallFunctionVa(tstate, callable, format, va, 1);
+ PyObject *result = _PyObject_CallFunctionVa(tstate, callable, format, va);
va_end(va);
return result;
@@ -621,7 +613,7 @@ _PyObject_CallFunction_SizeT(PyObject *callable, const char *format, ...)
static PyObject*
-callmethod(PyThreadState *tstate, PyObject* callable, const char *format, va_list va, int is_size_t)
+callmethod(PyThreadState *tstate, PyObject* callable, const char *format, va_list va)
{
assert(callable != NULL);
if (!PyCallable_Check(callable)) {
@@ -631,7 +623,7 @@ callmethod(PyThreadState *tstate, PyObject* callable, const char *format, va_lis
return NULL;
}
- return _PyObject_CallFunctionVa(tstate, callable, format, va, is_size_t);
+ return _PyObject_CallFunctionVa(tstate, callable, format, va);
}
PyObject *
@@ -650,7 +642,7 @@ PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)
va_list va;
va_start(va, format);
- PyObject *retval = callmethod(tstate, callable, format, va, 0);
+ PyObject *retval = callmethod(tstate, callable, format, va);
va_end(va);
Py_DECREF(callable);
@@ -659,9 +651,8 @@ PyObject_CallMethod(PyObject *obj, const char *name, const char *format, ...)
/* PyEval_CallMethod is exact copy of PyObject_CallMethod.
- * This function is kept for backward compatibility.
- */
-PyObject *
+ Function removed in Python 3.13 API but kept in the stable ABI. */
+PyAPI_FUNC(PyObject*)
PyEval_CallMethod(PyObject *obj, const char *name, const char *format, ...)
{
PyThreadState *tstate = _PyThreadState_GET();
@@ -676,7 +667,7 @@ PyEval_CallMethod(PyObject *obj, const char *name, const char *format, ...)
va_list va;
va_start(va, format);
- PyObject *retval = callmethod(tstate, callable, format, va, 0);
+ PyObject *retval = callmethod(tstate, callable, format, va);
va_end(va);
Py_DECREF(callable);
@@ -700,7 +691,7 @@ _PyObject_CallMethod(PyObject *obj, PyObject *name,
va_list va;
va_start(va, format);
- PyObject *retval = callmethod(tstate, callable, format, va, 1);
+ PyObject *retval = callmethod(tstate, callable, format, va);
va_end(va);
Py_DECREF(callable);
@@ -724,7 +715,7 @@ _PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name,
va_list va;
va_start(va, format);
- PyObject *retval = callmethod(tstate, callable, format, va, 0);
+ PyObject *retval = callmethod(tstate, callable, format, va);
va_end(va);
Py_DECREF(callable);
@@ -735,15 +726,18 @@ _PyObject_CallMethodId(PyObject *obj, _Py_Identifier *name,
PyObject * _PyObject_CallMethodFormat(PyThreadState *tstate, PyObject *callable,
const char *format, ...)
{
+ assert(callable != NULL);
va_list va;
va_start(va, format);
- PyObject *retval = callmethod(tstate, callable, format, va, 0);
+ PyObject *retval = callmethod(tstate, callable, format, va);
va_end(va);
return retval;
}
-PyObject *
+// _PyObject_CallMethod_SizeT is exact copy of PyObject_CallMethod.
+// This function must be kept because it is part of the stable ABI.
+PyAPI_FUNC(PyObject *) /* abi_only */
_PyObject_CallMethod_SizeT(PyObject *obj, const char *name,
const char *format, ...)
{
@@ -759,31 +753,7 @@ _PyObject_CallMethod_SizeT(PyObject *obj, const char *name,
va_list va;
va_start(va, format);
- PyObject *retval = callmethod(tstate, callable, format, va, 1);
- va_end(va);
-
- Py_DECREF(callable);
- return retval;
-}
-
-
-PyObject *
-_PyObject_CallMethodId_SizeT(PyObject *obj, _Py_Identifier *name,
- const char *format, ...)
-{
- PyThreadState *tstate = _PyThreadState_GET();
- if (obj == NULL || name == NULL) {
- return null_error(tstate);
- }
-
- PyObject *callable = _PyObject_GetAttrId(obj, name);
- if (callable == NULL) {
- return NULL;
- }
-
- va_list va;
- va_start(va, format);
- PyObject *retval = callmethod(tstate, callable, format, va, 1);
+ PyObject *retval = callmethod(tstate, callable, format, va);
va_end(va);
Py_DECREF(callable);
diff --git a/contrib/tools/python3/Objects/capsule.c b/contrib/tools/python3/Objects/capsule.c
index a28e0304e83..c8dd1253cda 100644
--- a/contrib/tools/python3/Objects/capsule.c
+++ b/contrib/tools/python3/Objects/capsule.c
@@ -1,6 +1,10 @@
/* Wrap void * pointers to be passed between C modules */
#include "Python.h"
+#include "pycore_capsule.h" // export _PyCapsule_SetTraverse()
+#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
+#include "pycore_object.h" // _PyObject_GC_TRACK()
+
/* Internal structure of PyCapsule */
typedef struct {
@@ -9,18 +13,28 @@ typedef struct {
const char *name;
void *context;
PyCapsule_Destructor destructor;
+ traverseproc traverse_func;
+ inquiry clear_func;
} PyCapsule;
static int
-_is_legal_capsule(PyCapsule *capsule, const char *invalid_capsule)
+_is_legal_capsule(PyObject *op, const char *invalid_capsule)
{
- if (!capsule || !PyCapsule_CheckExact(capsule) || capsule->pointer == NULL) {
- PyErr_SetString(PyExc_ValueError, invalid_capsule);
- return 0;
+ if (!op || !PyCapsule_CheckExact(op)) {
+ goto error;
+ }
+ PyCapsule *capsule = (PyCapsule *)op;
+
+ if (capsule->pointer == NULL) {
+ goto error;
}
return 1;
+
+error:
+ PyErr_SetString(PyExc_ValueError, invalid_capsule);
+ return 0;
}
#define is_legal_capsule(capsule, name) \
@@ -50,7 +64,7 @@ PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
return NULL;
}
- capsule = PyObject_New(PyCapsule, &PyCapsule_Type);
+ capsule = PyObject_GC_New(PyCapsule, &PyCapsule_Type);
if (capsule == NULL) {
return NULL;
}
@@ -59,15 +73,18 @@ PyCapsule_New(void *pointer, const char *name, PyCapsule_Destructor destructor)
capsule->name = name;
capsule->context = NULL;
capsule->destructor = destructor;
+ capsule->traverse_func = NULL;
+ capsule->clear_func = NULL;
+ // Only track the object by the GC when _PyCapsule_SetTraverse() is called
return (PyObject *)capsule;
}
int
-PyCapsule_IsValid(PyObject *o, const char *name)
+PyCapsule_IsValid(PyObject *op, const char *name)
{
- PyCapsule *capsule = (PyCapsule *)o;
+ PyCapsule *capsule = (PyCapsule *)op;
return (capsule != NULL &&
PyCapsule_CheckExact(capsule) &&
@@ -77,13 +94,12 @@ PyCapsule_IsValid(PyObject *o, const char *name)
void *
-PyCapsule_GetPointer(PyObject *o, const char *name)
+PyCapsule_GetPointer(PyObject *op, const char *name)
{
- PyCapsule *capsule = (PyCapsule *)o;
-
- if (!is_legal_capsule(capsule, "PyCapsule_GetPointer")) {
+ if (!is_legal_capsule(op, "PyCapsule_GetPointer")) {
return NULL;
}
+ PyCapsule *capsule = (PyCapsule *)op;
if (!name_matches(name, capsule->name)) {
PyErr_SetString(PyExc_ValueError, "PyCapsule_GetPointer called with incorrect name");
@@ -95,52 +111,48 @@ PyCapsule_GetPointer(PyObject *o, const char *name)
const char *
-PyCapsule_GetName(PyObject *o)
+PyCapsule_GetName(PyObject *op)
{
- PyCapsule *capsule = (PyCapsule *)o;
-
- if (!is_legal_capsule(capsule, "PyCapsule_GetName")) {
+ if (!is_legal_capsule(op, "PyCapsule_GetName")) {
return NULL;
}
+ PyCapsule *capsule = (PyCapsule *)op;
return capsule->name;
}
PyCapsule_Destructor
-PyCapsule_GetDestructor(PyObject *o)
+PyCapsule_GetDestructor(PyObject *op)
{
- PyCapsule *capsule = (PyCapsule *)o;
-
- if (!is_legal_capsule(capsule, "PyCapsule_GetDestructor")) {
+ if (!is_legal_capsule(op, "PyCapsule_GetDestructor")) {
return NULL;
}
+ PyCapsule *capsule = (PyCapsule *)op;
return capsule->destructor;
}
void *
-PyCapsule_GetContext(PyObject *o)
+PyCapsule_GetContext(PyObject *op)
{
- PyCapsule *capsule = (PyCapsule *)o;
-
- if (!is_legal_capsule(capsule, "PyCapsule_GetContext")) {
+ if (!is_legal_capsule(op, "PyCapsule_GetContext")) {
return NULL;
}
+ PyCapsule *capsule = (PyCapsule *)op;
return capsule->context;
}
int
-PyCapsule_SetPointer(PyObject *o, void *pointer)
+PyCapsule_SetPointer(PyObject *op, void *pointer)
{
- PyCapsule *capsule = (PyCapsule *)o;
-
- if (!pointer) {
- PyErr_SetString(PyExc_ValueError, "PyCapsule_SetPointer called with null pointer");
+ if (!is_legal_capsule(op, "PyCapsule_SetPointer")) {
return -1;
}
+ PyCapsule *capsule = (PyCapsule *)op;
- if (!is_legal_capsule(capsule, "PyCapsule_SetPointer")) {
+ if (!pointer) {
+ PyErr_SetString(PyExc_ValueError, "PyCapsule_SetPointer called with null pointer");
return -1;
}
@@ -150,13 +162,12 @@ PyCapsule_SetPointer(PyObject *o, void *pointer)
int
-PyCapsule_SetName(PyObject *o, const char *name)
+PyCapsule_SetName(PyObject *op, const char *name)
{
- PyCapsule *capsule = (PyCapsule *)o;
-
- if (!is_legal_capsule(capsule, "PyCapsule_SetName")) {
+ if (!is_legal_capsule(op, "PyCapsule_SetName")) {
return -1;
}
+ PyCapsule *capsule = (PyCapsule *)op;
capsule->name = name;
return 0;
@@ -164,13 +175,12 @@ PyCapsule_SetName(PyObject *o, const char *name)
int
-PyCapsule_SetDestructor(PyObject *o, PyCapsule_Destructor destructor)
+PyCapsule_SetDestructor(PyObject *op, PyCapsule_Destructor destructor)
{
- PyCapsule *capsule = (PyCapsule *)o;
-
- if (!is_legal_capsule(capsule, "PyCapsule_SetDestructor")) {
+ if (!is_legal_capsule(op, "PyCapsule_SetDestructor")) {
return -1;
}
+ PyCapsule *capsule = (PyCapsule *)op;
capsule->destructor = destructor;
return 0;
@@ -178,19 +188,42 @@ PyCapsule_SetDestructor(PyObject *o, PyCapsule_Destructor destructor)
int
-PyCapsule_SetContext(PyObject *o, void *context)
+PyCapsule_SetContext(PyObject *op, void *context)
{
- PyCapsule *capsule = (PyCapsule *)o;
-
- if (!is_legal_capsule(capsule, "PyCapsule_SetContext")) {
+ if (!is_legal_capsule(op, "PyCapsule_SetContext")) {
return -1;
}
+ PyCapsule *capsule = (PyCapsule *)op;
capsule->context = context;
return 0;
}
+int
+_PyCapsule_SetTraverse(PyObject *op, traverseproc traverse_func, inquiry clear_func)
+{
+ if (!is_legal_capsule(op, "_PyCapsule_SetTraverse")) {
+ return -1;
+ }
+ PyCapsule *capsule = (PyCapsule *)op;
+
+ if (traverse_func == NULL || clear_func == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "_PyCapsule_SetTraverse() called with NULL callback");
+ return -1;
+ }
+
+ if (!_PyObject_GC_IS_TRACKED(op)) {
+ _PyObject_GC_TRACK(op);
+ }
+
+ capsule->traverse_func = traverse_func;
+ capsule->clear_func = clear_func;
+ return 0;
+}
+
+
void *
PyCapsule_Import(const char *name, int no_block)
{
@@ -254,13 +287,14 @@ EXIT:
static void
-capsule_dealloc(PyObject *o)
+capsule_dealloc(PyObject *op)
{
- PyCapsule *capsule = (PyCapsule *)o;
+ PyCapsule *capsule = (PyCapsule *)op;
+ PyObject_GC_UnTrack(op);
if (capsule->destructor) {
- capsule->destructor(o);
+ capsule->destructor(op);
}
- PyObject_Free(o);
+ PyObject_GC_Del(op);
}
@@ -284,6 +318,31 @@ capsule_repr(PyObject *o)
}
+static int
+capsule_traverse(PyCapsule *capsule, visitproc visit, void *arg)
+{
+ // Capsule object is only tracked by the GC
+ // if _PyCapsule_SetTraverse() is called, but
+ // this can still be manually triggered by gc.get_referents()
+
+ if (capsule->traverse_func != NULL) {
+ return capsule->traverse_func((PyObject*)capsule, visit, arg);
+ }
+
+ return 0;
+}
+
+
+static int
+capsule_clear(PyCapsule *capsule)
+{
+ // Capsule object is only tracked by the GC
+ // if _PyCapsule_SetTraverse() is called
+ assert(capsule->clear_func != NULL);
+
+ return capsule->clear_func((PyObject*)capsule);
+}
+
PyDoc_STRVAR(PyCapsule_Type__doc__,
"Capsule objects let you wrap a C \"void *\" pointer in a Python\n\
@@ -298,27 +357,14 @@ Python import mechanism to link to one another.\n\
PyTypeObject PyCapsule_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "PyCapsule", /*tp_name*/
- sizeof(PyCapsule), /*tp_basicsize*/
- 0, /*tp_itemsize*/
- /* methods */
- capsule_dealloc, /*tp_dealloc*/
- 0, /*tp_vectorcall_offset*/
- 0, /*tp_getattr*/
- 0, /*tp_setattr*/
- 0, /*tp_as_async*/
- capsule_repr, /*tp_repr*/
- 0, /*tp_as_number*/
- 0, /*tp_as_sequence*/
- 0, /*tp_as_mapping*/
- 0, /*tp_hash*/
- 0, /*tp_call*/
- 0, /*tp_str*/
- 0, /*tp_getattro*/
- 0, /*tp_setattro*/
- 0, /*tp_as_buffer*/
- 0, /*tp_flags*/
- PyCapsule_Type__doc__ /*tp_doc*/
+ .tp_name = "PyCapsule",
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
+ .tp_basicsize = sizeof(PyCapsule),
+ .tp_dealloc = capsule_dealloc,
+ .tp_repr = capsule_repr,
+ .tp_doc = PyCapsule_Type__doc__,
+ .tp_traverse = (traverseproc)capsule_traverse,
+ .tp_clear = (inquiry)capsule_clear,
};
diff --git a/contrib/tools/python3/Objects/cellobject.c b/contrib/tools/python3/Objects/cellobject.c
index f516707f6f8..b1154e4ca4a 100644
--- a/contrib/tools/python3/Objects/cellobject.c
+++ b/contrib/tools/python3/Objects/cellobject.c
@@ -1,6 +1,8 @@
/* Cell object implementation */
#include "Python.h"
+#include "pycore_cell.h" // PyCell_GetRef()
+#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_object.h"
PyObject *
@@ -55,8 +57,7 @@ PyCell_Get(PyObject *op)
PyErr_BadInternalCall();
return NULL;
}
- PyObject *value = PyCell_GET(op);
- return Py_XNewRef(value);
+ return PyCell_GetRef((PyCellObject *)op);
}
int
@@ -66,9 +67,7 @@ PyCell_Set(PyObject *op, PyObject *value)
PyErr_BadInternalCall();
return -1;
}
- PyObject *old_value = PyCell_GET(op);
- PyCell_SET(op, Py_XNewRef(value));
- Py_XDECREF(old_value);
+ PyCell_SetTakeRef((PyCellObject *)op, Py_XNewRef(value));
return 0;
}
diff --git a/contrib/tools/python3/Objects/classobject.c b/contrib/tools/python3/Objects/classobject.c
index 12dc276f289..c8f215a2b05 100644
--- a/contrib/tools/python3/Objects/classobject.c
+++ b/contrib/tools/python3/Objects/classobject.c
@@ -2,10 +2,12 @@
#include "Python.h"
#include "pycore_call.h" // _PyObject_VectorcallTstate()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h" // _PyThreadState_GET()
-#include "structmember.h" // PyMemberDef
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
+
#include "clinic/classobject.c.h"
@@ -152,9 +154,9 @@ static PyMethodDef method_methods[] = {
#define MO_OFF(x) offsetof(PyMethodObject, x)
static PyMemberDef method_memberlist[] = {
- {"__func__", T_OBJECT, MO_OFF(im_func), READONLY,
+ {"__func__", _Py_T_OBJECT, MO_OFF(im_func), Py_READONLY,
"the function (or other callable) implementing a method"},
- {"__self__", T_OBJECT, MO_OFF(im_self), READONLY,
+ {"__self__", _Py_T_OBJECT, MO_OFF(im_self), Py_READONLY,
"the instance to which a method is bound"},
{NULL} /* Sentinel */
};
@@ -187,15 +189,18 @@ method_getattro(PyObject *obj, PyObject *name)
if (PyType_Ready(tp) < 0)
return NULL;
}
- descr = _PyType_Lookup(tp, name);
+ descr = _PyType_LookupRef(tp, name);
}
if (descr != NULL) {
descrgetfunc f = TP_DESCR_GET(Py_TYPE(descr));
- if (f != NULL)
- return f(descr, obj, (PyObject *)Py_TYPE(obj));
+ if (f != NULL) {
+ PyObject *res = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ Py_DECREF(descr);
+ return res;
+ }
else {
- return Py_NewRef(descr);
+ return descr;
}
}
@@ -234,8 +239,7 @@ static void
method_dealloc(PyMethodObject *im)
{
_PyObject_GC_UNTRACK(im);
- if (im->im_weakreflist != NULL)
- PyObject_ClearWeakRefs((PyObject *)im);
+ FT_CLEAR_WEAKREFS((PyObject*)im, im->im_weakreflist);
Py_DECREF(im->im_func);
Py_XDECREF(im->im_self);
PyObject_GC_Del(im);
@@ -277,9 +281,9 @@ method_repr(PyMethodObject *a)
PyObject *funcname, *result;
const char *defname = "?";
- if (_PyObject_LookupAttr(func, &_Py_ID(__qualname__), &funcname) < 0 ||
+ if (PyObject_GetOptionalAttr(func, &_Py_ID(__qualname__), &funcname) < 0 ||
(funcname == NULL &&
- _PyObject_LookupAttr(func, &_Py_ID(__name__), &funcname) < 0))
+ PyObject_GetOptionalAttr(func, &_Py_ID(__name__), &funcname) < 0))
{
return NULL;
}
@@ -300,7 +304,7 @@ static Py_hash_t
method_hash(PyMethodObject *a)
{
Py_hash_t x, y;
- x = _Py_HashPointer(a->im_self);
+ x = PyObject_GenericHash(a->im_self);
y = PyObject_Hash(a->im_func);
if (y == -1)
return -1;
@@ -318,6 +322,13 @@ method_traverse(PyMethodObject *im, visitproc visit, void *arg)
return 0;
}
+static PyObject *
+method_descr_get(PyObject *meth, PyObject *obj, PyObject *cls)
+{
+ Py_INCREF(meth);
+ return meth;
+}
+
PyTypeObject PyMethod_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "method",
@@ -338,6 +349,7 @@ PyTypeObject PyMethod_Type = {
.tp_methods = method_methods,
.tp_members = method_memberlist,
.tp_getset = method_getset,
+ .tp_descr_get = method_descr_get,
.tp_new = method_new,
};
@@ -374,7 +386,7 @@ PyInstanceMethod_Function(PyObject *im)
#define IMO_OFF(x) offsetof(PyInstanceMethodObject, x)
static PyMemberDef instancemethod_memberlist[] = {
- {"__func__", T_OBJECT, IMO_OFF(func), READONLY,
+ {"__func__", _Py_T_OBJECT, IMO_OFF(func), Py_READONLY,
"the function (or other callable) implementing a method"},
{NULL} /* Sentinel */
};
@@ -401,14 +413,17 @@ instancemethod_getattro(PyObject *self, PyObject *name)
if (PyType_Ready(tp) < 0)
return NULL;
}
- descr = _PyType_Lookup(tp, name);
+ descr = _PyType_LookupRef(tp, name);
if (descr != NULL) {
descrgetfunc f = TP_DESCR_GET(Py_TYPE(descr));
- if (f != NULL)
- return f(descr, self, (PyObject *)Py_TYPE(self));
+ if (f != NULL) {
+ PyObject *res = f(descr, self, (PyObject *)Py_TYPE(self));
+ Py_DECREF(descr);
+ return res;
+ }
else {
- return Py_NewRef(descr);
+ return descr;
}
}
@@ -481,7 +496,7 @@ instancemethod_repr(PyObject *self)
return NULL;
}
- if (_PyObject_LookupAttr(func, &_Py_ID(__name__), &funcname) < 0) {
+ if (PyObject_GetOptionalAttr(func, &_Py_ID(__name__), &funcname) < 0) {
return NULL;
}
if (funcname != NULL && !PyUnicode_Check(funcname)) {
diff --git a/contrib/tools/python3/Objects/clinic/bytearrayobject.c.h b/contrib/tools/python3/Objects/clinic/bytearrayobject.c.h
index e7bf3183af8..c748c53e1c0 100644
--- a/contrib/tools/python3/Objects/clinic/bytearrayobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/bytearrayobject.c.h
@@ -3,10 +3,11 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_abstract.h" // _PyNumber_Index()
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
static int
bytearray___init___impl(PyByteArrayObject *self, PyObject *arg,
@@ -100,6 +101,106 @@ exit:
return return_value;
}
+PyDoc_STRVAR(bytearray_find__doc__,
+"find($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the lowest index in B where subsection \'sub\' is found, such that \'sub\' is contained within B[start:end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.\n"
+"\n"
+"Return -1 on failure.");
+
+#define BYTEARRAY_FIND_METHODDEF \
+ {"find", _PyCFunction_CAST(bytearray_find), METH_FASTCALL, bytearray_find__doc__},
+
+static PyObject *
+bytearray_find_impl(PyByteArrayObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+bytearray_find(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("find", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytearray_find_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytearray_count__doc__,
+"count($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the number of non-overlapping occurrences of subsection \'sub\' in bytes B[start:end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.");
+
+#define BYTEARRAY_COUNT_METHODDEF \
+ {"count", _PyCFunction_CAST(bytearray_count), METH_FASTCALL, bytearray_count__doc__},
+
+static PyObject *
+bytearray_count_impl(PyByteArrayObject *self, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end);
+
+static PyObject *
+bytearray_count(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("count", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytearray_count_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(bytearray_clear__doc__,
"clear($self, /)\n"
"--\n"
@@ -136,6 +237,261 @@ bytearray_copy(PyByteArrayObject *self, PyObject *Py_UNUSED(ignored))
return bytearray_copy_impl(self);
}
+PyDoc_STRVAR(bytearray_index__doc__,
+"index($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the lowest index in B where subsection \'sub\' is found, such that \'sub\' is contained within B[start:end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.\n"
+"\n"
+"Raise ValueError if the subsection is not found.");
+
+#define BYTEARRAY_INDEX_METHODDEF \
+ {"index", _PyCFunction_CAST(bytearray_index), METH_FASTCALL, bytearray_index__doc__},
+
+static PyObject *
+bytearray_index_impl(PyByteArrayObject *self, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end);
+
+static PyObject *
+bytearray_index(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("index", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytearray_index_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytearray_rfind__doc__,
+"rfind($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the highest index in B where subsection \'sub\' is found, such that \'sub\' is contained within B[start:end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.\n"
+"\n"
+"Return -1 on failure.");
+
+#define BYTEARRAY_RFIND_METHODDEF \
+ {"rfind", _PyCFunction_CAST(bytearray_rfind), METH_FASTCALL, bytearray_rfind__doc__},
+
+static PyObject *
+bytearray_rfind_impl(PyByteArrayObject *self, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end);
+
+static PyObject *
+bytearray_rfind(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("rfind", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytearray_rfind_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytearray_rindex__doc__,
+"rindex($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the highest index in B where subsection \'sub\' is found, such that \'sub\' is contained within B[start:end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.\n"
+"\n"
+"Raise ValueError if the subsection is not found.");
+
+#define BYTEARRAY_RINDEX_METHODDEF \
+ {"rindex", _PyCFunction_CAST(bytearray_rindex), METH_FASTCALL, bytearray_rindex__doc__},
+
+static PyObject *
+bytearray_rindex_impl(PyByteArrayObject *self, PyObject *sub,
+ Py_ssize_t start, Py_ssize_t end);
+
+static PyObject *
+bytearray_rindex(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("rindex", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytearray_rindex_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytearray_startswith__doc__,
+"startswith($self, prefix[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return True if the bytearray starts with the specified prefix, False otherwise.\n"
+"\n"
+" prefix\n"
+" A bytes or a tuple of bytes to try.\n"
+" start\n"
+" Optional start position. Default: start of the bytearray.\n"
+" end\n"
+" Optional stop position. Default: end of the bytearray.");
+
+#define BYTEARRAY_STARTSWITH_METHODDEF \
+ {"startswith", _PyCFunction_CAST(bytearray_startswith), METH_FASTCALL, bytearray_startswith__doc__},
+
+static PyObject *
+bytearray_startswith_impl(PyByteArrayObject *self, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end);
+
+static PyObject *
+bytearray_startswith(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *subobj;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("startswith", nargs, 1, 3)) {
+ goto exit;
+ }
+ subobj = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytearray_startswith_impl(self, subobj, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytearray_endswith__doc__,
+"endswith($self, suffix[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return True if the bytearray ends with the specified suffix, False otherwise.\n"
+"\n"
+" suffix\n"
+" A bytes or a tuple of bytes to try.\n"
+" start\n"
+" Optional start position. Default: start of the bytearray.\n"
+" end\n"
+" Optional stop position. Default: end of the bytearray.");
+
+#define BYTEARRAY_ENDSWITH_METHODDEF \
+ {"endswith", _PyCFunction_CAST(bytearray_endswith), METH_FASTCALL, bytearray_endswith__doc__},
+
+static PyObject *
+bytearray_endswith_impl(PyByteArrayObject *self, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end);
+
+static PyObject *
+bytearray_endswith(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *subobj;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("endswith", nargs, 1, 3)) {
+ goto exit;
+ }
+ subobj = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytearray_endswith_impl(self, subobj, start, end);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(bytearray_removeprefix__doc__,
"removeprefix($self, prefix, /)\n"
"--\n"
@@ -161,10 +517,6 @@ bytearray_removeprefix(PyByteArrayObject *self, PyObject *arg)
if (PyObject_GetBuffer(arg, &prefix, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&prefix, 'C')) {
- _PyArg_BadArgument("removeprefix", "argument", "contiguous buffer", arg);
- goto exit;
- }
return_value = bytearray_removeprefix_impl(self, &prefix);
exit:
@@ -201,10 +553,6 @@ bytearray_removesuffix(PyByteArrayObject *self, PyObject *arg)
if (PyObject_GetBuffer(arg, &suffix, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&suffix, 'C')) {
- _PyArg_BadArgument("removesuffix", "argument", "contiguous buffer", arg);
- goto exit;
- }
return_value = bytearray_removesuffix_impl(self, &suffix);
exit:
@@ -289,7 +637,7 @@ PyDoc_STRVAR(bytearray_maketrans__doc__,
"maketrans(frm, to, /)\n"
"--\n"
"\n"
-"Return a translation table useable for the bytes or bytearray translate method.\n"
+"Return a translation table usable for the bytes or bytearray translate method.\n"
"\n"
"The returned table will be one where each byte in frm is mapped to the byte at\n"
"the same position in to.\n"
@@ -315,17 +663,9 @@ bytearray_maketrans(void *null, PyObject *const *args, Py_ssize_t nargs)
if (PyObject_GetBuffer(args[0], &frm, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&frm, 'C')) {
- _PyArg_BadArgument("maketrans", "argument 1", "contiguous buffer", args[0]);
- goto exit;
- }
if (PyObject_GetBuffer(args[1], &to, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&to, 'C')) {
- _PyArg_BadArgument("maketrans", "argument 2", "contiguous buffer", args[1]);
- goto exit;
- }
return_value = bytearray_maketrans_impl(&frm, &to);
exit:
@@ -375,17 +715,9 @@ bytearray_replace(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nar
if (PyObject_GetBuffer(args[0], &old, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&old, 'C')) {
- _PyArg_BadArgument("replace", "argument 1", "contiguous buffer", args[0]);
- goto exit;
- }
if (PyObject_GetBuffer(args[1], &new, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&new, 'C')) {
- _PyArg_BadArgument("replace", "argument 2", "contiguous buffer", args[1]);
- goto exit;
- }
if (nargs < 3) {
goto skip_optional;
}
@@ -1120,9 +1452,6 @@ bytearray_fromhex(PyTypeObject *type, PyObject *arg)
_PyArg_BadArgument("fromhex", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
string = arg;
return_value = bytearray_fromhex_impl(type, string);
@@ -1206,7 +1535,7 @@ bytearray_hex(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t nargs,
goto skip_optional_pos;
}
}
- bytes_per_sep = _PyLong_AsInt(args[1]);
+ bytes_per_sep = PyLong_AsInt(args[1]);
if (bytes_per_sep == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -1259,7 +1588,7 @@ bytearray_reduce_ex(PyByteArrayObject *self, PyObject *const *args, Py_ssize_t n
if (nargs < 1) {
goto skip_optional;
}
- proto = _PyLong_AsInt(args[0]);
+ proto = PyLong_AsInt(args[0]);
if (proto == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -1287,4 +1616,4 @@ bytearray_sizeof(PyByteArrayObject *self, PyObject *Py_UNUSED(ignored))
{
return bytearray_sizeof_impl(self);
}
-/*[clinic end generated code: output=022698e8b0faa272 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=5f861b02e3fa278b input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/bytesobject.c.h b/contrib/tools/python3/Objects/clinic/bytesobject.c.h
index 060056dafbd..0b4b3750173 100644
--- a/contrib/tools/python3/Objects/clinic/bytesobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/bytesobject.c.h
@@ -3,10 +3,11 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_abstract.h" // _PyNumber_Index()
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(bytes___bytes____doc__,
"__bytes__($self, /)\n"
@@ -140,10 +141,6 @@ bytes_partition(PyBytesObject *self, PyObject *arg)
if (PyObject_GetBuffer(arg, &sep, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&sep, 'C')) {
- _PyArg_BadArgument("partition", "argument", "contiguous buffer", arg);
- goto exit;
- }
return_value = bytes_partition_impl(self, &sep);
exit:
@@ -183,10 +180,6 @@ bytes_rpartition(PyBytesObject *self, PyObject *arg)
if (PyObject_GetBuffer(arg, &sep, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&sep, 'C')) {
- _PyArg_BadArgument("rpartition", "argument", "contiguous buffer", arg);
- goto exit;
- }
return_value = bytes_rpartition_impl(self, &sep);
exit:
@@ -301,6 +294,210 @@ PyDoc_STRVAR(bytes_join__doc__,
#define BYTES_JOIN_METHODDEF \
{"join", (PyCFunction)bytes_join, METH_O, bytes_join__doc__},
+PyDoc_STRVAR(bytes_find__doc__,
+"find($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the lowest index in B where subsection \'sub\' is found, such that \'sub\' is contained within B[start,end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.\n"
+"\n"
+"Return -1 on failure.");
+
+#define BYTES_FIND_METHODDEF \
+ {"find", _PyCFunction_CAST(bytes_find), METH_FASTCALL, bytes_find__doc__},
+
+static PyObject *
+bytes_find_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+bytes_find(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("find", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytes_find_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytes_index__doc__,
+"index($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the lowest index in B where subsection \'sub\' is found, such that \'sub\' is contained within B[start,end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.\n"
+"\n"
+"Raise ValueError if the subsection is not found.");
+
+#define BYTES_INDEX_METHODDEF \
+ {"index", _PyCFunction_CAST(bytes_index), METH_FASTCALL, bytes_index__doc__},
+
+static PyObject *
+bytes_index_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+bytes_index(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("index", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytes_index_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytes_rfind__doc__,
+"rfind($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the highest index in B where subsection \'sub\' is found, such that \'sub\' is contained within B[start,end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.\n"
+"\n"
+"Return -1 on failure.");
+
+#define BYTES_RFIND_METHODDEF \
+ {"rfind", _PyCFunction_CAST(bytes_rfind), METH_FASTCALL, bytes_rfind__doc__},
+
+static PyObject *
+bytes_rfind_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+bytes_rfind(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("rfind", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytes_rfind_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytes_rindex__doc__,
+"rindex($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the highest index in B where subsection \'sub\' is found, such that \'sub\' is contained within B[start,end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.\n"
+"\n"
+"Raise ValueError if the subsection is not found.");
+
+#define BYTES_RINDEX_METHODDEF \
+ {"rindex", _PyCFunction_CAST(bytes_rindex), METH_FASTCALL, bytes_rindex__doc__},
+
+static PyObject *
+bytes_rindex_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+bytes_rindex(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("rindex", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytes_rindex_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(bytes_strip__doc__,
"strip($self, bytes=None, /)\n"
"--\n"
@@ -403,6 +600,55 @@ exit:
return return_value;
}
+PyDoc_STRVAR(bytes_count__doc__,
+"count($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the number of non-overlapping occurrences of subsection \'sub\' in bytes B[start:end].\n"
+"\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.");
+
+#define BYTES_COUNT_METHODDEF \
+ {"count", _PyCFunction_CAST(bytes_count), METH_FASTCALL, bytes_count__doc__},
+
+static PyObject *
+bytes_count_impl(PyBytesObject *self, PyObject *sub, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+bytes_count(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *sub;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("count", nargs, 1, 3)) {
+ goto exit;
+ }
+ sub = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytes_count_impl(self, sub, start, end);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(bytes_translate__doc__,
"translate($self, table, /, delete=b\'\')\n"
"--\n"
@@ -476,7 +722,7 @@ PyDoc_STRVAR(bytes_maketrans__doc__,
"maketrans(frm, to, /)\n"
"--\n"
"\n"
-"Return a translation table useable for the bytes or bytearray translate method.\n"
+"Return a translation table usable for the bytes or bytearray translate method.\n"
"\n"
"The returned table will be one where each byte in frm is mapped to the byte at\n"
"the same position in to.\n"
@@ -502,17 +748,9 @@ bytes_maketrans(void *null, PyObject *const *args, Py_ssize_t nargs)
if (PyObject_GetBuffer(args[0], &frm, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&frm, 'C')) {
- _PyArg_BadArgument("maketrans", "argument 1", "contiguous buffer", args[0]);
- goto exit;
- }
if (PyObject_GetBuffer(args[1], &to, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&to, 'C')) {
- _PyArg_BadArgument("maketrans", "argument 2", "contiguous buffer", args[1]);
- goto exit;
- }
return_value = bytes_maketrans_impl(&frm, &to);
exit:
@@ -562,17 +800,9 @@ bytes_replace(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs)
if (PyObject_GetBuffer(args[0], &old, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&old, 'C')) {
- _PyArg_BadArgument("replace", "argument 1", "contiguous buffer", args[0]);
- goto exit;
- }
if (PyObject_GetBuffer(args[1], &new, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&new, 'C')) {
- _PyArg_BadArgument("replace", "argument 2", "contiguous buffer", args[1]);
- goto exit;
- }
if (nargs < 3) {
goto skip_optional;
}
@@ -628,10 +858,6 @@ bytes_removeprefix(PyBytesObject *self, PyObject *arg)
if (PyObject_GetBuffer(arg, &prefix, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&prefix, 'C')) {
- _PyArg_BadArgument("removeprefix", "argument", "contiguous buffer", arg);
- goto exit;
- }
return_value = bytes_removeprefix_impl(self, &prefix);
exit:
@@ -668,10 +894,6 @@ bytes_removesuffix(PyBytesObject *self, PyObject *arg)
if (PyObject_GetBuffer(arg, &suffix, PyBUF_SIMPLE) != 0) {
goto exit;
}
- if (!PyBuffer_IsContiguous(&suffix, 'C')) {
- _PyArg_BadArgument("removesuffix", "argument", "contiguous buffer", arg);
- goto exit;
- }
return_value = bytes_removesuffix_impl(self, &suffix);
exit:
@@ -683,6 +905,108 @@ exit:
return return_value;
}
+PyDoc_STRVAR(bytes_startswith__doc__,
+"startswith($self, prefix[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return True if the bytes starts with the specified prefix, False otherwise.\n"
+"\n"
+" prefix\n"
+" A bytes or a tuple of bytes to try.\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.");
+
+#define BYTES_STARTSWITH_METHODDEF \
+ {"startswith", _PyCFunction_CAST(bytes_startswith), METH_FASTCALL, bytes_startswith__doc__},
+
+static PyObject *
+bytes_startswith_impl(PyBytesObject *self, PyObject *subobj,
+ Py_ssize_t start, Py_ssize_t end);
+
+static PyObject *
+bytes_startswith(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *subobj;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("startswith", nargs, 1, 3)) {
+ goto exit;
+ }
+ subobj = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytes_startswith_impl(self, subobj, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(bytes_endswith__doc__,
+"endswith($self, suffix[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return True if the bytes ends with the specified suffix, False otherwise.\n"
+"\n"
+" suffix\n"
+" A bytes or a tuple of bytes to try.\n"
+" start\n"
+" Optional start position. Default: start of the bytes.\n"
+" end\n"
+" Optional stop position. Default: end of the bytes.");
+
+#define BYTES_ENDSWITH_METHODDEF \
+ {"endswith", _PyCFunction_CAST(bytes_endswith), METH_FASTCALL, bytes_endswith__doc__},
+
+static PyObject *
+bytes_endswith_impl(PyBytesObject *self, PyObject *subobj, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+bytes_endswith(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *subobj;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("endswith", nargs, 1, 3)) {
+ goto exit;
+ }
+ subobj = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = bytes_endswith_impl(self, subobj, start, end);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(bytes_decode__doc__,
"decode($self, /, encoding=\'utf-8\', errors=\'strict\')\n"
"--\n"
@@ -875,9 +1199,6 @@ bytes_fromhex(PyTypeObject *type, PyObject *arg)
_PyArg_BadArgument("fromhex", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
string = arg;
return_value = bytes_fromhex_impl(type, string);
@@ -961,7 +1282,7 @@ bytes_hex(PyBytesObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject
goto skip_optional_pos;
}
}
- bytes_per_sep = _PyLong_AsInt(args[1]);
+ bytes_per_sep = PyLong_AsInt(args[1]);
if (bytes_per_sep == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -1063,4 +1384,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=31a9e4af85562612 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d6801c6001e57f91 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/classobject.c.h b/contrib/tools/python3/Objects/clinic/classobject.c.h
index a7bac63052b..3e149c97324 100644
--- a/contrib/tools/python3/Objects/clinic/classobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/classobject.c.h
@@ -2,11 +2,7 @@
preserve
[clinic start generated code]*/
-#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
-#endif
-
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(method___reduce____doc__,
"__reduce__($self, /)\n"
@@ -86,4 +82,4 @@ instancemethod_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=2a5e7fa5947a86cb input=a9049054013a1b77]*/
+/*[clinic end generated code: output=5a5e3f2d0726f189 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/codeobject.c.h b/contrib/tools/python3/Objects/clinic/codeobject.c.h
index 1034627edd7..68e2d7f528c 100644
--- a/contrib/tools/python3/Objects/clinic/codeobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/codeobject.c.h
@@ -3,10 +3,10 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(code_new__doc__,
"code(argcount, posonlyargcount, kwonlyargcount, nlocals, stacksize,\n"
@@ -57,27 +57,27 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
if (!_PyArg_CheckPositional("code", PyTuple_GET_SIZE(args), 16, 18)) {
goto exit;
}
- argcount = _PyLong_AsInt(PyTuple_GET_ITEM(args, 0));
+ argcount = PyLong_AsInt(PyTuple_GET_ITEM(args, 0));
if (argcount == -1 && PyErr_Occurred()) {
goto exit;
}
- posonlyargcount = _PyLong_AsInt(PyTuple_GET_ITEM(args, 1));
+ posonlyargcount = PyLong_AsInt(PyTuple_GET_ITEM(args, 1));
if (posonlyargcount == -1 && PyErr_Occurred()) {
goto exit;
}
- kwonlyargcount = _PyLong_AsInt(PyTuple_GET_ITEM(args, 2));
+ kwonlyargcount = PyLong_AsInt(PyTuple_GET_ITEM(args, 2));
if (kwonlyargcount == -1 && PyErr_Occurred()) {
goto exit;
}
- nlocals = _PyLong_AsInt(PyTuple_GET_ITEM(args, 3));
+ nlocals = PyLong_AsInt(PyTuple_GET_ITEM(args, 3));
if (nlocals == -1 && PyErr_Occurred()) {
goto exit;
}
- stacksize = _PyLong_AsInt(PyTuple_GET_ITEM(args, 4));
+ stacksize = PyLong_AsInt(PyTuple_GET_ITEM(args, 4));
if (stacksize == -1 && PyErr_Occurred()) {
goto exit;
}
- flags = _PyLong_AsInt(PyTuple_GET_ITEM(args, 5));
+ flags = PyLong_AsInt(PyTuple_GET_ITEM(args, 5));
if (flags == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -105,27 +105,18 @@ code_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
_PyArg_BadArgument("code", "argument 11", "str", PyTuple_GET_ITEM(args, 10));
goto exit;
}
- if (PyUnicode_READY(PyTuple_GET_ITEM(args, 10)) == -1) {
- goto exit;
- }
filename = PyTuple_GET_ITEM(args, 10);
if (!PyUnicode_Check(PyTuple_GET_ITEM(args, 11))) {
_PyArg_BadArgument("code", "argument 12", "str", PyTuple_GET_ITEM(args, 11));
goto exit;
}
- if (PyUnicode_READY(PyTuple_GET_ITEM(args, 11)) == -1) {
- goto exit;
- }
name = PyTuple_GET_ITEM(args, 11);
if (!PyUnicode_Check(PyTuple_GET_ITEM(args, 12))) {
_PyArg_BadArgument("code", "argument 13", "str", PyTuple_GET_ITEM(args, 12));
goto exit;
}
- if (PyUnicode_READY(PyTuple_GET_ITEM(args, 12)) == -1) {
- goto exit;
- }
qualname = PyTuple_GET_ITEM(args, 12);
- firstlineno = _PyLong_AsInt(PyTuple_GET_ITEM(args, 13));
+ firstlineno = PyLong_AsInt(PyTuple_GET_ITEM(args, 13));
if (firstlineno == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -240,7 +231,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
goto skip_optional_kwonly;
}
if (args[0]) {
- co_argcount = _PyLong_AsInt(args[0]);
+ co_argcount = PyLong_AsInt(args[0]);
if (co_argcount == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -249,7 +240,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
}
}
if (args[1]) {
- co_posonlyargcount = _PyLong_AsInt(args[1]);
+ co_posonlyargcount = PyLong_AsInt(args[1]);
if (co_posonlyargcount == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -258,7 +249,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
}
}
if (args[2]) {
- co_kwonlyargcount = _PyLong_AsInt(args[2]);
+ co_kwonlyargcount = PyLong_AsInt(args[2]);
if (co_kwonlyargcount == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -267,7 +258,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
}
}
if (args[3]) {
- co_nlocals = _PyLong_AsInt(args[3]);
+ co_nlocals = PyLong_AsInt(args[3]);
if (co_nlocals == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -276,7 +267,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
}
}
if (args[4]) {
- co_stacksize = _PyLong_AsInt(args[4]);
+ co_stacksize = PyLong_AsInt(args[4]);
if (co_stacksize == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -285,7 +276,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
}
}
if (args[5]) {
- co_flags = _PyLong_AsInt(args[5]);
+ co_flags = PyLong_AsInt(args[5]);
if (co_flags == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -294,7 +285,7 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
}
}
if (args[6]) {
- co_firstlineno = _PyLong_AsInt(args[6]);
+ co_firstlineno = PyLong_AsInt(args[6]);
if (co_firstlineno == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -367,9 +358,6 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
_PyArg_BadArgument("replace", "argument 'co_filename'", "str", args[13]);
goto exit;
}
- if (PyUnicode_READY(args[13]) == -1) {
- goto exit;
- }
co_filename = args[13];
if (!--noptargs) {
goto skip_optional_kwonly;
@@ -380,9 +368,6 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
_PyArg_BadArgument("replace", "argument 'co_name'", "str", args[14]);
goto exit;
}
- if (PyUnicode_READY(args[14]) == -1) {
- goto exit;
- }
co_name = args[14];
if (!--noptargs) {
goto skip_optional_kwonly;
@@ -393,9 +378,6 @@ code_replace(PyCodeObject *self, PyObject *const *args, Py_ssize_t nargs, PyObje
_PyArg_BadArgument("replace", "argument 'co_qualname'", "str", args[15]);
goto exit;
}
- if (PyUnicode_READY(args[15]) == -1) {
- goto exit;
- }
co_qualname = args[15];
if (!--noptargs) {
goto skip_optional_kwonly;
@@ -473,7 +455,7 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n
if (!args) {
goto exit;
}
- oparg = _PyLong_AsInt(args[0]);
+ oparg = PyLong_AsInt(args[0]);
if (oparg == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -482,4 +464,4 @@ code__varname_from_oparg(PyCodeObject *self, PyObject *const *args, Py_ssize_t n
exit:
return return_value;
}
-/*[clinic end generated code: output=ff40f7bdd3851de3 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d604263a3ca72a0f input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/complexobject.c.h b/contrib/tools/python3/Objects/clinic/complexobject.c.h
index f0c841c12a3..46c3b352562 100644
--- a/contrib/tools/python3/Objects/clinic/complexobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/complexobject.c.h
@@ -3,10 +3,10 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_modsupport.h" // _PyArg_BadArgument()
PyDoc_STRVAR(complex_conjugate__doc__,
"conjugate($self, /)\n"
@@ -65,9 +65,6 @@ complex___format__(PyComplexObject *self, PyObject *arg)
_PyArg_BadArgument("__format__", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
format_spec = arg;
return_value = complex___format___impl(self, format_spec);
@@ -163,4 +160,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=9558b8449db17dc1 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=295ecfd71389d7fe input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/descrobject.c.h b/contrib/tools/python3/Objects/clinic/descrobject.c.h
index 75706437df8..d79be80d3ec 100644
--- a/contrib/tools/python3/Objects/clinic/descrobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/descrobject.c.h
@@ -3,10 +3,16 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
+PyDoc_STRVAR(mappingproxy_new__doc__,
+"mappingproxy(mapping)\n"
+"--\n"
+"\n"
+"Read-only proxy of a mapping.");
static PyObject *
mappingproxy_new_impl(PyTypeObject *type, PyObject *mapping);
@@ -167,4 +173,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=8dc1ddfcf764ac8e input=a9049054013a1b77]*/
+/*[clinic end generated code: output=050e331316a04207 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/dictobject.c.h b/contrib/tools/python3/Objects/clinic/dictobject.c.h
index bc2452330e4..fb46c4c6433 100644
--- a/contrib/tools/python3/Objects/clinic/dictobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/dictobject.c.h
@@ -2,11 +2,8 @@
preserve
[clinic start generated code]*/
-#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
-#endif
-
+#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(dict_fromkeys__doc__,
"fromkeys($type, iterable, value=None, /)\n"
@@ -42,6 +39,24 @@ exit:
return return_value;
}
+PyDoc_STRVAR(dict_copy__doc__,
+"copy($self, /)\n"
+"--\n"
+"\n"
+"Return a shallow copy of the dict.");
+
+#define DICT_COPY_METHODDEF \
+ {"copy", (PyCFunction)dict_copy, METH_NOARGS, dict_copy__doc__},
+
+static PyObject *
+dict_copy_impl(PyDictObject *self);
+
+static PyObject *
+dict_copy(PyDictObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return dict_copy_impl(self);
+}
+
PyDoc_STRVAR(dict___contains____doc__,
"__contains__($self, key, /)\n"
"--\n"
@@ -79,7 +94,9 @@ dict_get(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs)
}
default_value = args[1];
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = dict_get_impl(self, key, default_value);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -116,12 +133,32 @@ dict_setdefault(PyDictObject *self, PyObject *const *args, Py_ssize_t nargs)
}
default_value = args[1];
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = dict_setdefault_impl(self, key, default_value);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
}
+PyDoc_STRVAR(dict_clear__doc__,
+"clear($self, /)\n"
+"--\n"
+"\n"
+"Remove all items from the dict.");
+
+#define DICT_CLEAR_METHODDEF \
+ {"clear", (PyCFunction)dict_clear, METH_NOARGS, dict_clear__doc__},
+
+static PyObject *
+dict_clear_impl(PyDictObject *self);
+
+static PyObject *
+dict_clear(PyDictObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return dict_clear_impl(self);
+}
+
PyDoc_STRVAR(dict_pop__doc__,
"pop($self, key, default=<unrepresentable>, /)\n"
"--\n"
@@ -177,7 +214,31 @@ dict_popitem_impl(PyDictObject *self);
static PyObject *
dict_popitem(PyDictObject *self, PyObject *Py_UNUSED(ignored))
{
- return dict_popitem_impl(self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = dict_popitem_impl(self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(dict___sizeof____doc__,
+"__sizeof__($self, /)\n"
+"--\n"
+"\n"
+"Return the size of the dict in memory, in bytes.");
+
+#define DICT___SIZEOF___METHODDEF \
+ {"__sizeof__", (PyCFunction)dict___sizeof__, METH_NOARGS, dict___sizeof____doc__},
+
+static PyObject *
+dict___sizeof___impl(PyDictObject *self);
+
+static PyObject *
+dict___sizeof__(PyDictObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return dict___sizeof___impl(self);
}
PyDoc_STRVAR(dict___reversed____doc__,
@@ -197,4 +258,58 @@ dict___reversed__(PyDictObject *self, PyObject *Py_UNUSED(ignored))
{
return dict___reversed___impl(self);
}
-/*[clinic end generated code: output=c0064abbea6091c5 input=a9049054013a1b77]*/
+
+PyDoc_STRVAR(dict_keys__doc__,
+"keys($self, /)\n"
+"--\n"
+"\n"
+"Return a set-like object providing a view on the dict\'s keys.");
+
+#define DICT_KEYS_METHODDEF \
+ {"keys", (PyCFunction)dict_keys, METH_NOARGS, dict_keys__doc__},
+
+static PyObject *
+dict_keys_impl(PyDictObject *self);
+
+static PyObject *
+dict_keys(PyDictObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return dict_keys_impl(self);
+}
+
+PyDoc_STRVAR(dict_items__doc__,
+"items($self, /)\n"
+"--\n"
+"\n"
+"Return a set-like object providing a view on the dict\'s items.");
+
+#define DICT_ITEMS_METHODDEF \
+ {"items", (PyCFunction)dict_items, METH_NOARGS, dict_items__doc__},
+
+static PyObject *
+dict_items_impl(PyDictObject *self);
+
+static PyObject *
+dict_items(PyDictObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return dict_items_impl(self);
+}
+
+PyDoc_STRVAR(dict_values__doc__,
+"values($self, /)\n"
+"--\n"
+"\n"
+"Return an object providing a view on the dict\'s values.");
+
+#define DICT_VALUES_METHODDEF \
+ {"values", (PyCFunction)dict_values, METH_NOARGS, dict_values__doc__},
+
+static PyObject *
+dict_values_impl(PyDictObject *self);
+
+static PyObject *
+dict_values(PyDictObject *self, PyObject *Py_UNUSED(ignored))
+{
+ return dict_values_impl(self);
+}
+/*[clinic end generated code: output=f3dd5f3fb8122aef input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/enumobject.c.h b/contrib/tools/python3/Objects/clinic/enumobject.c.h
index adf78efd0d6..09774a73c81 100644
--- a/contrib/tools/python3/Objects/clinic/enumobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/enumobject.c.h
@@ -3,10 +3,10 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(enum_new__doc__,
"enumerate(iterable, start=0)\n"
@@ -107,4 +107,4 @@ reversed_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=aba0ddbeab1601e3 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=5c48a9a482a52e91 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/floatobject.c.h b/contrib/tools/python3/Objects/clinic/floatobject.c.h
index ed166538af9..d104b071890 100644
--- a/contrib/tools/python3/Objects/clinic/floatobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/floatobject.c.h
@@ -2,11 +2,7 @@
preserve
[clinic start generated code]*/
-#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
-#endif
-
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(float_is_integer__doc__,
"is_integer($self, /)\n"
@@ -316,13 +312,10 @@ float___format__(PyObject *self, PyObject *arg)
_PyArg_BadArgument("__format__", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
format_spec = arg;
return_value = float___format___impl(self, format_spec);
exit:
return return_value;
}
-/*[clinic end generated code: output=e6e3f5f833b37eba input=a9049054013a1b77]*/
+/*[clinic end generated code: output=d8bbcd83977d516f input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/funcobject.c.h b/contrib/tools/python3/Objects/clinic/funcobject.c.h
index c3a3a8edc39..2dfa489927f 100644
--- a/contrib/tools/python3/Objects/clinic/funcobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/funcobject.c.h
@@ -3,13 +3,129 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
+#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
+PyDoc_STRVAR(function___annotations____doc__,
+"Dict of annotations in a function object.");
+#if defined(function___annotations___DOCSTR)
+# undef function___annotations___DOCSTR
+#endif
+#define function___annotations___DOCSTR function___annotations____doc__
+
+#if !defined(function___annotations___DOCSTR)
+# define function___annotations___DOCSTR NULL
+#endif
+#if defined(FUNCTION___ANNOTATIONS___GETSETDEF)
+# undef FUNCTION___ANNOTATIONS___GETSETDEF
+# define FUNCTION___ANNOTATIONS___GETSETDEF {"__annotations__", (getter)function___annotations___get, (setter)function___annotations___set, function___annotations___DOCSTR},
+#else
+# define FUNCTION___ANNOTATIONS___GETSETDEF {"__annotations__", (getter)function___annotations___get, NULL, function___annotations___DOCSTR},
+#endif
+
+static PyObject *
+function___annotations___get_impl(PyFunctionObject *self);
+
+static PyObject *
+function___annotations___get(PyFunctionObject *self, void *Py_UNUSED(context))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = function___annotations___get_impl(self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+#if !defined(function___annotations___DOCSTR)
+# define function___annotations___DOCSTR NULL
+#endif
+#if defined(FUNCTION___ANNOTATIONS___GETSETDEF)
+# undef FUNCTION___ANNOTATIONS___GETSETDEF
+# define FUNCTION___ANNOTATIONS___GETSETDEF {"__annotations__", (getter)function___annotations___get, (setter)function___annotations___set, function___annotations___DOCSTR},
+#else
+# define FUNCTION___ANNOTATIONS___GETSETDEF {"__annotations__", NULL, (setter)function___annotations___set, NULL},
+#endif
+
+static int
+function___annotations___set_impl(PyFunctionObject *self, PyObject *value);
+
+static int
+function___annotations___set(PyFunctionObject *self, PyObject *value, void *Py_UNUSED(context))
+{
+ int return_value;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = function___annotations___set_impl(self, value);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(function___type_params____doc__,
+"Get the declared type parameters for a function.");
+#if defined(function___type_params___DOCSTR)
+# undef function___type_params___DOCSTR
+#endif
+#define function___type_params___DOCSTR function___type_params____doc__
+
+#if !defined(function___type_params___DOCSTR)
+# define function___type_params___DOCSTR NULL
+#endif
+#if defined(FUNCTION___TYPE_PARAMS___GETSETDEF)
+# undef FUNCTION___TYPE_PARAMS___GETSETDEF
+# define FUNCTION___TYPE_PARAMS___GETSETDEF {"__type_params__", (getter)function___type_params___get, (setter)function___type_params___set, function___type_params___DOCSTR},
+#else
+# define FUNCTION___TYPE_PARAMS___GETSETDEF {"__type_params__", (getter)function___type_params___get, NULL, function___type_params___DOCSTR},
+#endif
+
+static PyObject *
+function___type_params___get_impl(PyFunctionObject *self);
+
+static PyObject *
+function___type_params___get(PyFunctionObject *self, void *Py_UNUSED(context))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = function___type_params___get_impl(self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+#if !defined(function___type_params___DOCSTR)
+# define function___type_params___DOCSTR NULL
+#endif
+#if defined(FUNCTION___TYPE_PARAMS___GETSETDEF)
+# undef FUNCTION___TYPE_PARAMS___GETSETDEF
+# define FUNCTION___TYPE_PARAMS___GETSETDEF {"__type_params__", (getter)function___type_params___get, (setter)function___type_params___set, function___type_params___DOCSTR},
+#else
+# define FUNCTION___TYPE_PARAMS___GETSETDEF {"__type_params__", NULL, (setter)function___type_params___set, NULL},
+#endif
+
+static int
+function___type_params___set_impl(PyFunctionObject *self, PyObject *value);
+
+static int
+function___type_params___set(PyFunctionObject *self, PyObject *value, void *Py_UNUSED(context))
+{
+ int return_value;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = function___type_params___set_impl(self, value);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
PyDoc_STRVAR(func_new__doc__,
-"function(code, globals, name=None, argdefs=None, closure=None)\n"
+"function(code, globals, name=None, argdefs=None, closure=None,\n"
+" kwdefaults=None)\n"
"--\n"
"\n"
"Create a function object.\n"
@@ -23,11 +139,14 @@ PyDoc_STRVAR(func_new__doc__,
" argdefs\n"
" a tuple that specifies the default argument values\n"
" closure\n"
-" a tuple that supplies the bindings for free variables");
+" a tuple that supplies the bindings for free variables\n"
+" kwdefaults\n"
+" a dictionary that specifies the default keyword argument values");
static PyObject *
func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
- PyObject *name, PyObject *defaults, PyObject *closure);
+ PyObject *name, PyObject *defaults, PyObject *closure,
+ PyObject *kwdefaults);
static PyObject *
func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
@@ -35,14 +154,14 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
- #define NUM_KEYWORDS 5
+ #define NUM_KEYWORDS 6
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(code), &_Py_ID(globals), &_Py_ID(name), &_Py_ID(argdefs), &_Py_ID(closure), },
+ .ob_item = { &_Py_ID(code), &_Py_ID(globals), &_Py_ID(name), &_Py_ID(argdefs), &_Py_ID(closure), &_Py_ID(kwdefaults), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -51,14 +170,14 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"code", "globals", "name", "argdefs", "closure", NULL};
+ static const char * const _keywords[] = {"code", "globals", "name", "argdefs", "closure", "kwdefaults", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "function",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
- PyObject *argsbuf[5];
+ PyObject *argsbuf[6];
PyObject * const *fastargs;
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 2;
@@ -67,8 +186,9 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
PyObject *name = Py_None;
PyObject *defaults = Py_None;
PyObject *closure = Py_None;
+ PyObject *kwdefaults = Py_None;
- fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 5, 0, argsbuf);
+ fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 2, 6, 0, argsbuf);
if (!fastargs) {
goto exit;
}
@@ -97,11 +217,17 @@ func_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto skip_optional_pos;
}
}
- closure = fastargs[4];
+ if (fastargs[4]) {
+ closure = fastargs[4];
+ if (!--noptargs) {
+ goto skip_optional_pos;
+ }
+ }
+ kwdefaults = fastargs[5];
skip_optional_pos:
- return_value = func_new_impl(type, code, globals, name, defaults, closure);
+ return_value = func_new_impl(type, code, globals, name, defaults, closure, kwdefaults);
exit:
return return_value;
}
-/*[clinic end generated code: output=777cead7b1f6fad3 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=93e052c0f1ebb5f3 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/listobject.c.h b/contrib/tools/python3/Objects/clinic/listobject.c.h
index e3d6ffa9f76..588e021fb71 100644
--- a/contrib/tools/python3/Objects/clinic/listobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/listobject.c.h
@@ -3,10 +3,12 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_abstract.h" // _PyNumber_Index()
+#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(list_insert__doc__,
"insert($self, index, object, /)\n"
@@ -43,28 +45,36 @@ list_insert(PyListObject *self, PyObject *const *args, Py_ssize_t nargs)
index = ival;
}
object = args[1];
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = list_insert_impl(self, index, object);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
}
-PyDoc_STRVAR(list_clear__doc__,
+PyDoc_STRVAR(py_list_clear__doc__,
"clear($self, /)\n"
"--\n"
"\n"
"Remove all items from list.");
-#define LIST_CLEAR_METHODDEF \
- {"clear", (PyCFunction)list_clear, METH_NOARGS, list_clear__doc__},
+#define PY_LIST_CLEAR_METHODDEF \
+ {"clear", (PyCFunction)py_list_clear, METH_NOARGS, py_list_clear__doc__},
static PyObject *
-list_clear_impl(PyListObject *self);
+py_list_clear_impl(PyListObject *self);
static PyObject *
-list_clear(PyListObject *self, PyObject *Py_UNUSED(ignored))
+py_list_clear(PyListObject *self, PyObject *Py_UNUSED(ignored))
{
- return list_clear_impl(self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = py_list_clear_impl(self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(list_copy__doc__,
@@ -82,7 +92,13 @@ list_copy_impl(PyListObject *self);
static PyObject *
list_copy(PyListObject *self, PyObject *Py_UNUSED(ignored))
{
- return list_copy_impl(self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = list_copy_impl(self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(list_append__doc__,
@@ -94,6 +110,21 @@ PyDoc_STRVAR(list_append__doc__,
#define LIST_APPEND_METHODDEF \
{"append", (PyCFunction)list_append, METH_O, list_append__doc__},
+static PyObject *
+list_append_impl(PyListObject *self, PyObject *object);
+
+static PyObject *
+list_append(PyListObject *self, PyObject *object)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = list_append_impl(self, object);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
PyDoc_STRVAR(list_extend__doc__,
"extend($self, iterable, /)\n"
"--\n"
@@ -142,7 +173,9 @@ list_pop(PyListObject *self, PyObject *const *args, Py_ssize_t nargs)
index = ival;
}
skip_optional:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = list_pop_impl(self, index);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -220,7 +253,9 @@ list_sort(PyListObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject
goto exit;
}
skip_optional_kwonly:
+ Py_BEGIN_CRITICAL_SECTION(self);
return_value = list_sort_impl(self, keyfunc, reverse);
+ Py_END_CRITICAL_SECTION();
exit:
return return_value;
@@ -241,7 +276,13 @@ list_reverse_impl(PyListObject *self);
static PyObject *
list_reverse(PyListObject *self, PyObject *Py_UNUSED(ignored))
{
- return list_reverse_impl(self);
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = list_reverse_impl(self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
}
PyDoc_STRVAR(list_index__doc__,
@@ -310,6 +351,21 @@ PyDoc_STRVAR(list_remove__doc__,
#define LIST_REMOVE_METHODDEF \
{"remove", (PyCFunction)list_remove, METH_O, list_remove__doc__},
+static PyObject *
+list_remove_impl(PyListObject *self, PyObject *value);
+
+static PyObject *
+list_remove(PyListObject *self, PyObject *value)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = list_remove_impl(self, value);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
PyDoc_STRVAR(list___init____doc__,
"list(iterable=(), /)\n"
"--\n"
@@ -383,4 +439,4 @@ list___reversed__(PyListObject *self, PyObject *Py_UNUSED(ignored))
{
return list___reversed___impl(self);
}
-/*[clinic end generated code: output=2ca109d8acc775bc input=a9049054013a1b77]*/
+/*[clinic end generated code: output=854957a1d4a89bbd input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/longobject.c.h b/contrib/tools/python3/Objects/clinic/longobject.c.h
index c26ceafbc2b..56bc3864582 100644
--- a/contrib/tools/python3/Objects/clinic/longobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/longobject.c.h
@@ -3,10 +3,11 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_abstract.h" // _PyNumber_Index()
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
static PyObject *
long_new_impl(PyTypeObject *type, PyObject *x, PyObject *obase);
@@ -107,9 +108,6 @@ int___format__(PyObject *self, PyObject *arg)
_PyArg_BadArgument("__format__", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
format_spec = arg;
return_value = int___format___impl(self, format_spec);
@@ -269,7 +267,7 @@ PyDoc_STRVAR(int_to_bytes__doc__,
" the most significant byte is at the beginning of the byte array. If\n"
" byteorder is \'little\', the most significant byte is at the end of the\n"
" byte array. To request the native byte order of the host system, use\n"
-" `sys.byteorder\' as the byte order value. Default is to use \'big\'.\n"
+" sys.byteorder as the byte order value. Default is to use \'big\'.\n"
" signed\n"
" Determines whether two\'s complement is used to represent the integer.\n"
" If signed is False and a negative integer is given, an OverflowError\n"
@@ -346,9 +344,6 @@ int_to_bytes(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *
_PyArg_BadArgument("to_bytes", "argument 'byteorder'", "str", args[1]);
goto exit;
}
- if (PyUnicode_READY(args[1]) == -1) {
- goto exit;
- }
byteorder = args[1];
if (!--noptargs) {
goto skip_optional_pos;
@@ -385,7 +380,7 @@ PyDoc_STRVAR(int_from_bytes__doc__,
" the most significant byte is at the beginning of the byte array. If\n"
" byteorder is \'little\', the most significant byte is at the end of the\n"
" byte array. To request the native byte order of the host system, use\n"
-" `sys.byteorder\' as the byte order value. Default is to use \'big\'.\n"
+" sys.byteorder as the byte order value. Default is to use \'big\'.\n"
" signed\n"
" Indicates whether two\'s complement is used to represent the integer.");
@@ -444,9 +439,6 @@ int_from_bytes(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyOb
_PyArg_BadArgument("from_bytes", "argument 'byteorder'", "str", args[1]);
goto exit;
}
- if (PyUnicode_READY(args[1]) == -1) {
- goto exit;
- }
byteorder = args[1];
if (!--noptargs) {
goto skip_optional_pos;
@@ -484,4 +476,4 @@ int_is_integer(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return int_is_integer_impl(self);
}
-/*[clinic end generated code: output=cfdf35d916158d4f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=2ba2d8dcda9b99da input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/memoryobject.c.h b/contrib/tools/python3/Objects/clinic/memoryobject.c.h
index 25a22341185..f199434dacb 100644
--- a/contrib/tools/python3/Objects/clinic/memoryobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/memoryobject.c.h
@@ -3,10 +3,10 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(memoryview__doc__,
"memoryview(object)\n"
@@ -112,7 +112,7 @@ memoryview__from_flags(PyTypeObject *type, PyObject *const *args, Py_ssize_t nar
goto exit;
}
object = args[0];
- flags = _PyLong_AsInt(args[1]);
+ flags = PyLong_AsInt(args[1]);
if (flags == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -195,9 +195,6 @@ memoryview_cast(PyMemoryViewObject *self, PyObject *const *args, Py_ssize_t narg
_PyArg_BadArgument("cast", "argument 'format'", "str", args[0]);
goto exit;
}
- if (PyUnicode_READY(args[0]) == -1) {
- goto exit;
- }
format = args[0];
if (!noptargs) {
goto skip_optional_pos;
@@ -406,7 +403,7 @@ memoryview_hex(PyMemoryViewObject *self, PyObject *const *args, Py_ssize_t nargs
goto skip_optional_pos;
}
}
- bytes_per_sep = _PyLong_AsInt(args[1]);
+ bytes_per_sep = PyLong_AsInt(args[1]);
if (bytes_per_sep == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -416,4 +413,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=01613814112cedd7 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=7e76a09106921ba2 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/moduleobject.c.h b/contrib/tools/python3/Objects/clinic/moduleobject.c.h
index 861bcea6215..3c0bbe22d5e 100644
--- a/contrib/tools/python3/Objects/clinic/moduleobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/moduleobject.c.h
@@ -3,10 +3,10 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(module___init____doc__,
"module(name, doc=None)\n"
@@ -63,9 +63,6 @@ module___init__(PyObject *self, PyObject *args, PyObject *kwargs)
_PyArg_BadArgument("module", "argument 'name'", "str", fastargs[0]);
goto exit;
}
- if (PyUnicode_READY(fastargs[0]) == -1) {
- goto exit;
- }
name = fastargs[0];
if (!noptargs) {
goto skip_optional_pos;
@@ -77,4 +74,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=2f897c9e4721f03f input=a9049054013a1b77]*/
+/*[clinic end generated code: output=e8a71bfbed774c15 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/odictobject.c.h b/contrib/tools/python3/Objects/clinic/odictobject.c.h
index 115a134e3f7..5ef53806569 100644
--- a/contrib/tools/python3/Objects/clinic/odictobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/odictobject.c.h
@@ -3,10 +3,10 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(OrderedDict_fromkeys__doc__,
"fromkeys($type, /, iterable, value=None)\n"
@@ -332,4 +332,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=76d85a9162d62ca8 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=eff78d2a3f9379bd input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/setobject.c.h b/contrib/tools/python3/Objects/clinic/setobject.c.h
new file mode 100644
index 00000000000..3853ce3bce6
--- /dev/null
+++ b/contrib/tools/python3/Objects/clinic/setobject.c.h
@@ -0,0 +1,571 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
+
+PyDoc_STRVAR(set_pop__doc__,
+"pop($self, /)\n"
+"--\n"
+"\n"
+"Remove and return an arbitrary set element.\n"
+"\n"
+"Raises KeyError if the set is empty.");
+
+#define SET_POP_METHODDEF \
+ {"pop", (PyCFunction)set_pop, METH_NOARGS, set_pop__doc__},
+
+static PyObject *
+set_pop_impl(PySetObject *so);
+
+static PyObject *
+set_pop(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set_pop_impl(so);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_update__doc__,
+"update($self, /, *others)\n"
+"--\n"
+"\n"
+"Update the set, adding elements from all others.");
+
+#define SET_UPDATE_METHODDEF \
+ {"update", _PyCFunction_CAST(set_update), METH_FASTCALL, set_update__doc__},
+
+static PyObject *
+set_update_impl(PySetObject *so, PyObject *args);
+
+static PyObject *
+set_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *__clinic_args = NULL;
+
+ if (!_PyArg_CheckPositional("update", nargs, 0, PY_SSIZE_T_MAX)) {
+ goto exit;
+ }
+ __clinic_args = PyTuple_New(nargs - 0);
+ if (!__clinic_args) {
+ goto exit;
+ }
+ for (Py_ssize_t i = 0; i < nargs - 0; ++i) {
+ PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i]));
+ }
+ return_value = set_update_impl(so, __clinic_args);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(set_copy__doc__,
+"copy($self, /)\n"
+"--\n"
+"\n"
+"Return a shallow copy of a set.");
+
+#define SET_COPY_METHODDEF \
+ {"copy", (PyCFunction)set_copy, METH_NOARGS, set_copy__doc__},
+
+static PyObject *
+set_copy_impl(PySetObject *so);
+
+static PyObject *
+set_copy(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set_copy_impl(so);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(frozenset_copy__doc__,
+"copy($self, /)\n"
+"--\n"
+"\n"
+"Return a shallow copy of a set.");
+
+#define FROZENSET_COPY_METHODDEF \
+ {"copy", (PyCFunction)frozenset_copy, METH_NOARGS, frozenset_copy__doc__},
+
+static PyObject *
+frozenset_copy_impl(PySetObject *so);
+
+static PyObject *
+frozenset_copy(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = frozenset_copy_impl(so);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_clear__doc__,
+"clear($self, /)\n"
+"--\n"
+"\n"
+"Remove all elements from this set.");
+
+#define SET_CLEAR_METHODDEF \
+ {"clear", (PyCFunction)set_clear, METH_NOARGS, set_clear__doc__},
+
+static PyObject *
+set_clear_impl(PySetObject *so);
+
+static PyObject *
+set_clear(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set_clear_impl(so);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_union__doc__,
+"union($self, /, *others)\n"
+"--\n"
+"\n"
+"Return a new set with elements from the set and all others.");
+
+#define SET_UNION_METHODDEF \
+ {"union", _PyCFunction_CAST(set_union), METH_FASTCALL, set_union__doc__},
+
+static PyObject *
+set_union_impl(PySetObject *so, PyObject *args);
+
+static PyObject *
+set_union(PySetObject *so, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *__clinic_args = NULL;
+
+ if (!_PyArg_CheckPositional("union", nargs, 0, PY_SSIZE_T_MAX)) {
+ goto exit;
+ }
+ __clinic_args = PyTuple_New(nargs - 0);
+ if (!__clinic_args) {
+ goto exit;
+ }
+ for (Py_ssize_t i = 0; i < nargs - 0; ++i) {
+ PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i]));
+ }
+ return_value = set_union_impl(so, __clinic_args);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(set_intersection_multi__doc__,
+"intersection($self, /, *others)\n"
+"--\n"
+"\n"
+"Return a new set with elements common to the set and all others.");
+
+#define SET_INTERSECTION_MULTI_METHODDEF \
+ {"intersection", _PyCFunction_CAST(set_intersection_multi), METH_FASTCALL, set_intersection_multi__doc__},
+
+static PyObject *
+set_intersection_multi_impl(PySetObject *so, PyObject *args);
+
+static PyObject *
+set_intersection_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *__clinic_args = NULL;
+
+ if (!_PyArg_CheckPositional("intersection", nargs, 0, PY_SSIZE_T_MAX)) {
+ goto exit;
+ }
+ __clinic_args = PyTuple_New(nargs - 0);
+ if (!__clinic_args) {
+ goto exit;
+ }
+ for (Py_ssize_t i = 0; i < nargs - 0; ++i) {
+ PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i]));
+ }
+ return_value = set_intersection_multi_impl(so, __clinic_args);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(set_intersection_update_multi__doc__,
+"intersection_update($self, /, *others)\n"
+"--\n"
+"\n"
+"Update the set, keeping only elements found in it and all others.");
+
+#define SET_INTERSECTION_UPDATE_MULTI_METHODDEF \
+ {"intersection_update", _PyCFunction_CAST(set_intersection_update_multi), METH_FASTCALL, set_intersection_update_multi__doc__},
+
+static PyObject *
+set_intersection_update_multi_impl(PySetObject *so, PyObject *args);
+
+static PyObject *
+set_intersection_update_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *__clinic_args = NULL;
+
+ if (!_PyArg_CheckPositional("intersection_update", nargs, 0, PY_SSIZE_T_MAX)) {
+ goto exit;
+ }
+ __clinic_args = PyTuple_New(nargs - 0);
+ if (!__clinic_args) {
+ goto exit;
+ }
+ for (Py_ssize_t i = 0; i < nargs - 0; ++i) {
+ PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i]));
+ }
+ return_value = set_intersection_update_multi_impl(so, __clinic_args);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(set_isdisjoint__doc__,
+"isdisjoint($self, other, /)\n"
+"--\n"
+"\n"
+"Return True if two sets have a null intersection.");
+
+#define SET_ISDISJOINT_METHODDEF \
+ {"isdisjoint", (PyCFunction)set_isdisjoint, METH_O, set_isdisjoint__doc__},
+
+static PyObject *
+set_isdisjoint_impl(PySetObject *so, PyObject *other);
+
+static PyObject *
+set_isdisjoint(PySetObject *so, PyObject *other)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ return_value = set_isdisjoint_impl(so, other);
+ Py_END_CRITICAL_SECTION2();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_difference_update__doc__,
+"difference_update($self, /, *others)\n"
+"--\n"
+"\n"
+"Update the set, removing elements found in others.");
+
+#define SET_DIFFERENCE_UPDATE_METHODDEF \
+ {"difference_update", _PyCFunction_CAST(set_difference_update), METH_FASTCALL, set_difference_update__doc__},
+
+static PyObject *
+set_difference_update_impl(PySetObject *so, PyObject *args);
+
+static PyObject *
+set_difference_update(PySetObject *so, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *__clinic_args = NULL;
+
+ if (!_PyArg_CheckPositional("difference_update", nargs, 0, PY_SSIZE_T_MAX)) {
+ goto exit;
+ }
+ __clinic_args = PyTuple_New(nargs - 0);
+ if (!__clinic_args) {
+ goto exit;
+ }
+ for (Py_ssize_t i = 0; i < nargs - 0; ++i) {
+ PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i]));
+ }
+ return_value = set_difference_update_impl(so, __clinic_args);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(set_difference_multi__doc__,
+"difference($self, /, *others)\n"
+"--\n"
+"\n"
+"Return a new set with elements in the set that are not in the others.");
+
+#define SET_DIFFERENCE_MULTI_METHODDEF \
+ {"difference", _PyCFunction_CAST(set_difference_multi), METH_FASTCALL, set_difference_multi__doc__},
+
+static PyObject *
+set_difference_multi_impl(PySetObject *so, PyObject *args);
+
+static PyObject *
+set_difference_multi(PySetObject *so, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *__clinic_args = NULL;
+
+ if (!_PyArg_CheckPositional("difference", nargs, 0, PY_SSIZE_T_MAX)) {
+ goto exit;
+ }
+ __clinic_args = PyTuple_New(nargs - 0);
+ if (!__clinic_args) {
+ goto exit;
+ }
+ for (Py_ssize_t i = 0; i < nargs - 0; ++i) {
+ PyTuple_SET_ITEM(__clinic_args, i, Py_NewRef(args[0 + i]));
+ }
+ return_value = set_difference_multi_impl(so, __clinic_args);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(set_symmetric_difference_update__doc__,
+"symmetric_difference_update($self, other, /)\n"
+"--\n"
+"\n"
+"Update the set, keeping only elements found in either set, but not in both.");
+
+#define SET_SYMMETRIC_DIFFERENCE_UPDATE_METHODDEF \
+ {"symmetric_difference_update", (PyCFunction)set_symmetric_difference_update, METH_O, set_symmetric_difference_update__doc__},
+
+PyDoc_STRVAR(set_symmetric_difference__doc__,
+"symmetric_difference($self, other, /)\n"
+"--\n"
+"\n"
+"Return a new set with elements in either the set or other but not both.");
+
+#define SET_SYMMETRIC_DIFFERENCE_METHODDEF \
+ {"symmetric_difference", (PyCFunction)set_symmetric_difference, METH_O, set_symmetric_difference__doc__},
+
+static PyObject *
+set_symmetric_difference_impl(PySetObject *so, PyObject *other);
+
+static PyObject *
+set_symmetric_difference(PySetObject *so, PyObject *other)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ return_value = set_symmetric_difference_impl(so, other);
+ Py_END_CRITICAL_SECTION2();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_issubset__doc__,
+"issubset($self, other, /)\n"
+"--\n"
+"\n"
+"Report whether another set contains this set.");
+
+#define SET_ISSUBSET_METHODDEF \
+ {"issubset", (PyCFunction)set_issubset, METH_O, set_issubset__doc__},
+
+static PyObject *
+set_issubset_impl(PySetObject *so, PyObject *other);
+
+static PyObject *
+set_issubset(PySetObject *so, PyObject *other)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ return_value = set_issubset_impl(so, other);
+ Py_END_CRITICAL_SECTION2();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_issuperset__doc__,
+"issuperset($self, other, /)\n"
+"--\n"
+"\n"
+"Report whether this set contains another set.");
+
+#define SET_ISSUPERSET_METHODDEF \
+ {"issuperset", (PyCFunction)set_issuperset, METH_O, set_issuperset__doc__},
+
+static PyObject *
+set_issuperset_impl(PySetObject *so, PyObject *other);
+
+static PyObject *
+set_issuperset(PySetObject *so, PyObject *other)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ return_value = set_issuperset_impl(so, other);
+ Py_END_CRITICAL_SECTION2();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_add__doc__,
+"add($self, object, /)\n"
+"--\n"
+"\n"
+"Add an element to a set.\n"
+"\n"
+"This has no effect if the element is already present.");
+
+#define SET_ADD_METHODDEF \
+ {"add", (PyCFunction)set_add, METH_O, set_add__doc__},
+
+static PyObject *
+set_add_impl(PySetObject *so, PyObject *key);
+
+static PyObject *
+set_add(PySetObject *so, PyObject *key)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set_add_impl(so, key);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set___contains____doc__,
+"__contains__($self, object, /)\n"
+"--\n"
+"\n"
+"x.__contains__(y) <==> y in x.");
+
+#define SET___CONTAINS___METHODDEF \
+ {"__contains__", (PyCFunction)set___contains__, METH_O|METH_COEXIST, set___contains____doc__},
+
+static PyObject *
+set___contains___impl(PySetObject *so, PyObject *key);
+
+static PyObject *
+set___contains__(PySetObject *so, PyObject *key)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set___contains___impl(so, key);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_remove__doc__,
+"remove($self, object, /)\n"
+"--\n"
+"\n"
+"Remove an element from a set; it must be a member.\n"
+"\n"
+"If the element is not a member, raise a KeyError.");
+
+#define SET_REMOVE_METHODDEF \
+ {"remove", (PyCFunction)set_remove, METH_O, set_remove__doc__},
+
+static PyObject *
+set_remove_impl(PySetObject *so, PyObject *key);
+
+static PyObject *
+set_remove(PySetObject *so, PyObject *key)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set_remove_impl(so, key);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set_discard__doc__,
+"discard($self, object, /)\n"
+"--\n"
+"\n"
+"Remove an element from a set if it is a member.\n"
+"\n"
+"Unlike set.remove(), the discard() method does not raise\n"
+"an exception when an element is missing from the set.");
+
+#define SET_DISCARD_METHODDEF \
+ {"discard", (PyCFunction)set_discard, METH_O, set_discard__doc__},
+
+static PyObject *
+set_discard_impl(PySetObject *so, PyObject *key);
+
+static PyObject *
+set_discard(PySetObject *so, PyObject *key)
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set_discard_impl(so, key);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set___reduce____doc__,
+"__reduce__($self, /)\n"
+"--\n"
+"\n"
+"Return state information for pickling.");
+
+#define SET___REDUCE___METHODDEF \
+ {"__reduce__", (PyCFunction)set___reduce__, METH_NOARGS, set___reduce____doc__},
+
+static PyObject *
+set___reduce___impl(PySetObject *so);
+
+static PyObject *
+set___reduce__(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set___reduce___impl(so);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+PyDoc_STRVAR(set___sizeof____doc__,
+"__sizeof__($self, /)\n"
+"--\n"
+"\n"
+"S.__sizeof__() -> size of S in memory, in bytes.");
+
+#define SET___SIZEOF___METHODDEF \
+ {"__sizeof__", (PyCFunction)set___sizeof__, METH_NOARGS, set___sizeof____doc__},
+
+static PyObject *
+set___sizeof___impl(PySetObject *so);
+
+static PyObject *
+set___sizeof__(PySetObject *so, PyObject *Py_UNUSED(ignored))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ return_value = set___sizeof___impl(so);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+/*[clinic end generated code: output=de4ee725bd29f758 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/structseq.c.h b/contrib/tools/python3/Objects/clinic/structseq.c.h
index 40ba18a544f..cec49ebcccc 100644
--- a/contrib/tools/python3/Objects/clinic/structseq.c.h
+++ b/contrib/tools/python3/Objects/clinic/structseq.c.h
@@ -3,10 +3,10 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
static PyObject *
structseq_new_impl(PyTypeObject *type, PyObject *arg, PyObject *dict);
@@ -62,4 +62,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=802d5663c7d01024 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=5bf39b3f06a34ce4 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/tupleobject.c.h b/contrib/tools/python3/Objects/clinic/tupleobject.c.h
index 3de95759a13..5d6a2c481a5 100644
--- a/contrib/tools/python3/Objects/clinic/tupleobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/tupleobject.c.h
@@ -2,11 +2,7 @@
preserve
[clinic start generated code]*/
-#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
-#endif
-
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(tuple_index__doc__,
"index($self, value, start=0, stop=sys.maxsize, /)\n"
@@ -118,4 +114,4 @@ tuple___getnewargs__(PyTupleObject *self, PyObject *Py_UNUSED(ignored))
{
return tuple___getnewargs___impl(self);
}
-/*[clinic end generated code: output=48a9e0834b300ac3 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=a6a9abba5d121f4c input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/typeobject.c.h b/contrib/tools/python3/Objects/clinic/typeobject.c.h
index dc9746abfbe..1fa153598db 100644
--- a/contrib/tools/python3/Objects/clinic/typeobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/typeobject.c.h
@@ -2,11 +2,7 @@
preserve
[clinic start generated code]*/
-#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
-#endif
-
+#include "pycore_modsupport.h" // _PyArg_BadArgument()
PyDoc_STRVAR(type___instancecheck____doc__,
"__instancecheck__($self, instance, /)\n"
@@ -190,7 +186,7 @@ object___reduce_ex__(PyObject *self, PyObject *arg)
PyObject *return_value = NULL;
int protocol;
- protocol = _PyLong_AsInt(arg);
+ protocol = PyLong_AsInt(arg);
if (protocol == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -224,9 +220,6 @@ object___format__(PyObject *self, PyObject *arg)
_PyArg_BadArgument("__format__", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
format_spec = arg;
return_value = object___format___impl(self, format_spec);
@@ -269,4 +262,4 @@ object___dir__(PyObject *self, PyObject *Py_UNUSED(ignored))
{
return object___dir___impl(self);
}
-/*[clinic end generated code: output=d2fc52440a89f2fa input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b56c87f9cace1921 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/typevarobject.c.h b/contrib/tools/python3/Objects/clinic/typevarobject.c.h
index 54189b98446..0ba4ff48bc8 100644
--- a/contrib/tools/python3/Objects/clinic/typevarobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/typevarobject.c.h
@@ -3,22 +3,22 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywordsWithVararg()
PyDoc_STRVAR(typevar_new__doc__,
-"typevar(name, *constraints, *, bound=None, covariant=False,\n"
-" contravariant=False, infer_variance=False)\n"
+"typevar(name, *constraints, bound=None, default=typing.NoDefault,\n"
+" covariant=False, contravariant=False, infer_variance=False)\n"
"--\n"
"\n"
"Create a TypeVar.");
static PyObject *
typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints,
- PyObject *bound, int covariant, int contravariant,
- int infer_variance);
+ PyObject *bound, PyObject *default_value, int covariant,
+ int contravariant, int infer_variance);
static PyObject *
typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
@@ -26,14 +26,14 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
- #define NUM_KEYWORDS 5
+ #define NUM_KEYWORDS 6
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), },
+ .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(default), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -42,20 +42,21 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"name", "bound", "covariant", "contravariant", "infer_variance", NULL};
+ static const char * const _keywords[] = {"name", "bound", "default", "covariant", "contravariant", "infer_variance", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "typevar",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
- PyObject *argsbuf[6];
+ PyObject *argsbuf[7];
PyObject * const *fastargs;
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1;
PyObject *name;
PyObject *constraints = NULL;
PyObject *bound = Py_None;
+ PyObject *default_value = &_Py_NoDefaultStruct;
int covariant = 0;
int contravariant = 0;
int infer_variance = 0;
@@ -80,7 +81,13 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
}
}
if (fastargs[3]) {
- covariant = PyObject_IsTrue(fastargs[3]);
+ default_value = fastargs[3];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (fastargs[4]) {
+ covariant = PyObject_IsTrue(fastargs[4]);
if (covariant < 0) {
goto exit;
}
@@ -88,8 +95,8 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto skip_optional_kwonly;
}
}
- if (fastargs[4]) {
- contravariant = PyObject_IsTrue(fastargs[4]);
+ if (fastargs[5]) {
+ contravariant = PyObject_IsTrue(fastargs[5]);
if (contravariant < 0) {
goto exit;
}
@@ -97,12 +104,12 @@ typevar_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto skip_optional_kwonly;
}
}
- infer_variance = PyObject_IsTrue(fastargs[5]);
+ infer_variance = PyObject_IsTrue(fastargs[6]);
if (infer_variance < 0) {
goto exit;
}
skip_optional_kwonly:
- return_value = typevar_new_impl(type, name, constraints, bound, covariant, contravariant, infer_variance);
+ return_value = typevar_new_impl(type, name, constraints, bound, default_value, covariant, contravariant, infer_variance);
exit:
Py_XDECREF(constraints);
@@ -110,54 +117,38 @@ exit:
}
PyDoc_STRVAR(typevar_typing_subst__doc__,
-"__typing_subst__($self, /, arg)\n"
+"__typing_subst__($self, arg, /)\n"
"--\n"
"\n");
#define TYPEVAR_TYPING_SUBST_METHODDEF \
- {"__typing_subst__", _PyCFunction_CAST(typevar_typing_subst), METH_FASTCALL|METH_KEYWORDS, typevar_typing_subst__doc__},
+ {"__typing_subst__", (PyCFunction)typevar_typing_subst, METH_O, typevar_typing_subst__doc__},
+
+PyDoc_STRVAR(typevar_typing_prepare_subst__doc__,
+"__typing_prepare_subst__($self, alias, args, /)\n"
+"--\n"
+"\n");
+
+#define TYPEVAR_TYPING_PREPARE_SUBST_METHODDEF \
+ {"__typing_prepare_subst__", _PyCFunction_CAST(typevar_typing_prepare_subst), METH_FASTCALL, typevar_typing_prepare_subst__doc__},
static PyObject *
-typevar_typing_subst_impl(typevarobject *self, PyObject *arg);
+typevar_typing_prepare_subst_impl(typevarobject *self, PyObject *alias,
+ PyObject *args);
static PyObject *
-typevar_typing_subst(typevarobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+typevar_typing_prepare_subst(typevarobject *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
- #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-
- #define NUM_KEYWORDS 1
- static struct {
- PyGC_Head _this_is_not_used;
- PyObject_VAR_HEAD
- PyObject *ob_item[NUM_KEYWORDS];
- } _kwtuple = {
- .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(arg), },
- };
- #undef NUM_KEYWORDS
- #define KWTUPLE (&_kwtuple.ob_base.ob_base)
-
- #else // !Py_BUILD_CORE
- # define KWTUPLE NULL
- #endif // !Py_BUILD_CORE
-
- static const char * const _keywords[] = {"arg", NULL};
- static _PyArg_Parser _parser = {
- .keywords = _keywords,
- .fname = "__typing_subst__",
- .kwtuple = KWTUPLE,
- };
- #undef KWTUPLE
- PyObject *argsbuf[1];
- PyObject *arg;
+ PyObject *alias;
+ PyObject *__clinic_args;
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
- if (!args) {
+ if (!_PyArg_CheckPositional("__typing_prepare_subst__", nargs, 2, 2)) {
goto exit;
}
- arg = args[0];
- return_value = typevar_typing_subst_impl(self, arg);
+ alias = args[0];
+ __clinic_args = args[1];
+ return_value = typevar_typing_prepare_subst_impl(self, alias, __clinic_args);
exit:
return return_value;
@@ -180,6 +171,23 @@ typevar_reduce(typevarobject *self, PyObject *Py_UNUSED(ignored))
return typevar_reduce_impl(self);
}
+PyDoc_STRVAR(typevar_has_default__doc__,
+"has_default($self, /)\n"
+"--\n"
+"\n");
+
+#define TYPEVAR_HAS_DEFAULT_METHODDEF \
+ {"has_default", (PyCFunction)typevar_has_default, METH_NOARGS, typevar_has_default__doc__},
+
+static PyObject *
+typevar_has_default_impl(typevarobject *self);
+
+static PyObject *
+typevar_has_default(typevarobject *self, PyObject *Py_UNUSED(ignored))
+{
+ return typevar_has_default_impl(self);
+}
+
PyDoc_STRVAR(paramspecargs_new__doc__,
"paramspecargs(origin)\n"
"--\n"
@@ -289,15 +297,16 @@ exit:
}
PyDoc_STRVAR(paramspec_new__doc__,
-"paramspec(name, *, bound=None, covariant=False, contravariant=False,\n"
-" infer_variance=False)\n"
+"paramspec(name, *, bound=None, default=typing.NoDefault,\n"
+" covariant=False, contravariant=False, infer_variance=False)\n"
"--\n"
"\n"
"Create a ParamSpec object.");
static PyObject *
paramspec_new_impl(PyTypeObject *type, PyObject *name, PyObject *bound,
- int covariant, int contravariant, int infer_variance);
+ PyObject *default_value, int covariant, int contravariant,
+ int infer_variance);
static PyObject *
paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
@@ -305,14 +314,14 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
- #define NUM_KEYWORDS 5
+ #define NUM_KEYWORDS 6
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), },
+ .ob_item = { &_Py_ID(name), &_Py_ID(bound), &_Py_ID(default), &_Py_ID(covariant), &_Py_ID(contravariant), &_Py_ID(infer_variance), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -321,19 +330,20 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"name", "bound", "covariant", "contravariant", "infer_variance", NULL};
+ static const char * const _keywords[] = {"name", "bound", "default", "covariant", "contravariant", "infer_variance", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "paramspec",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
- PyObject *argsbuf[5];
+ PyObject *argsbuf[6];
PyObject * const *fastargs;
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1;
PyObject *name;
PyObject *bound = Py_None;
+ PyObject *default_value = &_Py_NoDefaultStruct;
int covariant = 0;
int contravariant = 0;
int infer_variance = 0;
@@ -357,7 +367,13 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
}
}
if (fastargs[2]) {
- covariant = PyObject_IsTrue(fastargs[2]);
+ default_value = fastargs[2];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ if (fastargs[3]) {
+ covariant = PyObject_IsTrue(fastargs[3]);
if (covariant < 0) {
goto exit;
}
@@ -365,8 +381,8 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto skip_optional_kwonly;
}
}
- if (fastargs[3]) {
- contravariant = PyObject_IsTrue(fastargs[3]);
+ if (fastargs[4]) {
+ contravariant = PyObject_IsTrue(fastargs[4]);
if (contravariant < 0) {
goto exit;
}
@@ -374,118 +390,45 @@ paramspec_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto skip_optional_kwonly;
}
}
- infer_variance = PyObject_IsTrue(fastargs[4]);
+ infer_variance = PyObject_IsTrue(fastargs[5]);
if (infer_variance < 0) {
goto exit;
}
skip_optional_kwonly:
- return_value = paramspec_new_impl(type, name, bound, covariant, contravariant, infer_variance);
+ return_value = paramspec_new_impl(type, name, bound, default_value, covariant, contravariant, infer_variance);
exit:
return return_value;
}
PyDoc_STRVAR(paramspec_typing_subst__doc__,
-"__typing_subst__($self, /, arg)\n"
+"__typing_subst__($self, arg, /)\n"
"--\n"
"\n");
#define PARAMSPEC_TYPING_SUBST_METHODDEF \
- {"__typing_subst__", _PyCFunction_CAST(paramspec_typing_subst), METH_FASTCALL|METH_KEYWORDS, paramspec_typing_subst__doc__},
-
-static PyObject *
-paramspec_typing_subst_impl(paramspecobject *self, PyObject *arg);
-
-static PyObject *
-paramspec_typing_subst(paramspecobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
-{
- PyObject *return_value = NULL;
- #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-
- #define NUM_KEYWORDS 1
- static struct {
- PyGC_Head _this_is_not_used;
- PyObject_VAR_HEAD
- PyObject *ob_item[NUM_KEYWORDS];
- } _kwtuple = {
- .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(arg), },
- };
- #undef NUM_KEYWORDS
- #define KWTUPLE (&_kwtuple.ob_base.ob_base)
-
- #else // !Py_BUILD_CORE
- # define KWTUPLE NULL
- #endif // !Py_BUILD_CORE
-
- static const char * const _keywords[] = {"arg", NULL};
- static _PyArg_Parser _parser = {
- .keywords = _keywords,
- .fname = "__typing_subst__",
- .kwtuple = KWTUPLE,
- };
- #undef KWTUPLE
- PyObject *argsbuf[1];
- PyObject *arg;
-
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
- if (!args) {
- goto exit;
- }
- arg = args[0];
- return_value = paramspec_typing_subst_impl(self, arg);
-
-exit:
- return return_value;
-}
+ {"__typing_subst__", (PyCFunction)paramspec_typing_subst, METH_O, paramspec_typing_subst__doc__},
PyDoc_STRVAR(paramspec_typing_prepare_subst__doc__,
-"__typing_prepare_subst__($self, /, alias, args)\n"
+"__typing_prepare_subst__($self, alias, args, /)\n"
"--\n"
"\n");
#define PARAMSPEC_TYPING_PREPARE_SUBST_METHODDEF \
- {"__typing_prepare_subst__", _PyCFunction_CAST(paramspec_typing_prepare_subst), METH_FASTCALL|METH_KEYWORDS, paramspec_typing_prepare_subst__doc__},
+ {"__typing_prepare_subst__", _PyCFunction_CAST(paramspec_typing_prepare_subst), METH_FASTCALL, paramspec_typing_prepare_subst__doc__},
static PyObject *
paramspec_typing_prepare_subst_impl(paramspecobject *self, PyObject *alias,
PyObject *args);
static PyObject *
-paramspec_typing_prepare_subst(paramspecobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+paramspec_typing_prepare_subst(paramspecobject *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
- #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-
- #define NUM_KEYWORDS 2
- static struct {
- PyGC_Head _this_is_not_used;
- PyObject_VAR_HEAD
- PyObject *ob_item[NUM_KEYWORDS];
- } _kwtuple = {
- .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(alias), &_Py_ID(args), },
- };
- #undef NUM_KEYWORDS
- #define KWTUPLE (&_kwtuple.ob_base.ob_base)
-
- #else // !Py_BUILD_CORE
- # define KWTUPLE NULL
- #endif // !Py_BUILD_CORE
-
- static const char * const _keywords[] = {"alias", "args", NULL};
- static _PyArg_Parser _parser = {
- .keywords = _keywords,
- .fname = "__typing_prepare_subst__",
- .kwtuple = KWTUPLE,
- };
- #undef KWTUPLE
- PyObject *argsbuf[2];
PyObject *alias;
PyObject *__clinic_args;
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf);
- if (!args) {
+ if (!_PyArg_CheckPositional("__typing_prepare_subst__", nargs, 2, 2)) {
goto exit;
}
alias = args[0];
@@ -513,14 +456,32 @@ paramspec_reduce(paramspecobject *self, PyObject *Py_UNUSED(ignored))
return paramspec_reduce_impl(self);
}
+PyDoc_STRVAR(paramspec_has_default__doc__,
+"has_default($self, /)\n"
+"--\n"
+"\n");
+
+#define PARAMSPEC_HAS_DEFAULT_METHODDEF \
+ {"has_default", (PyCFunction)paramspec_has_default, METH_NOARGS, paramspec_has_default__doc__},
+
+static PyObject *
+paramspec_has_default_impl(paramspecobject *self);
+
+static PyObject *
+paramspec_has_default(paramspecobject *self, PyObject *Py_UNUSED(ignored))
+{
+ return paramspec_has_default_impl(self);
+}
+
PyDoc_STRVAR(typevartuple__doc__,
-"typevartuple(name)\n"
+"typevartuple(name, *, default=typing.NoDefault)\n"
"--\n"
"\n"
"Create a new TypeVarTuple with the given name.");
static PyObject *
-typevartuple_impl(PyTypeObject *type, PyObject *name);
+typevartuple_impl(PyTypeObject *type, PyObject *name,
+ PyObject *default_value);
static PyObject *
typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs)
@@ -528,14 +489,14 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs)
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
- #define NUM_KEYWORDS 1
+ #define NUM_KEYWORDS 2
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(name), },
+ .ob_item = { &_Py_ID(name), &_Py_ID(default), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
@@ -544,17 +505,19 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs)
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
- static const char * const _keywords[] = {"name", NULL};
+ static const char * const _keywords[] = {"name", "default", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "typevartuple",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
- PyObject *argsbuf[1];
+ PyObject *argsbuf[2];
PyObject * const *fastargs;
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
+ Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 1;
PyObject *name;
+ PyObject *default_value = &_Py_NoDefaultStruct;
fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 1, 1, 0, argsbuf);
if (!fastargs) {
@@ -565,113 +528,45 @@ typevartuple(PyTypeObject *type, PyObject *args, PyObject *kwargs)
goto exit;
}
name = fastargs[0];
- return_value = typevartuple_impl(type, name);
+ if (!noptargs) {
+ goto skip_optional_kwonly;
+ }
+ default_value = fastargs[1];
+skip_optional_kwonly:
+ return_value = typevartuple_impl(type, name, default_value);
exit:
return return_value;
}
PyDoc_STRVAR(typevartuple_typing_subst__doc__,
-"__typing_subst__($self, /, arg)\n"
+"__typing_subst__($self, arg, /)\n"
"--\n"
"\n");
#define TYPEVARTUPLE_TYPING_SUBST_METHODDEF \
- {"__typing_subst__", _PyCFunction_CAST(typevartuple_typing_subst), METH_FASTCALL|METH_KEYWORDS, typevartuple_typing_subst__doc__},
-
-static PyObject *
-typevartuple_typing_subst_impl(typevartupleobject *self, PyObject *arg);
-
-static PyObject *
-typevartuple_typing_subst(typevartupleobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
-{
- PyObject *return_value = NULL;
- #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-
- #define NUM_KEYWORDS 1
- static struct {
- PyGC_Head _this_is_not_used;
- PyObject_VAR_HEAD
- PyObject *ob_item[NUM_KEYWORDS];
- } _kwtuple = {
- .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(arg), },
- };
- #undef NUM_KEYWORDS
- #define KWTUPLE (&_kwtuple.ob_base.ob_base)
-
- #else // !Py_BUILD_CORE
- # define KWTUPLE NULL
- #endif // !Py_BUILD_CORE
-
- static const char * const _keywords[] = {"arg", NULL};
- static _PyArg_Parser _parser = {
- .keywords = _keywords,
- .fname = "__typing_subst__",
- .kwtuple = KWTUPLE,
- };
- #undef KWTUPLE
- PyObject *argsbuf[1];
- PyObject *arg;
-
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, argsbuf);
- if (!args) {
- goto exit;
- }
- arg = args[0];
- return_value = typevartuple_typing_subst_impl(self, arg);
-
-exit:
- return return_value;
-}
+ {"__typing_subst__", (PyCFunction)typevartuple_typing_subst, METH_O, typevartuple_typing_subst__doc__},
PyDoc_STRVAR(typevartuple_typing_prepare_subst__doc__,
-"__typing_prepare_subst__($self, /, alias, args)\n"
+"__typing_prepare_subst__($self, alias, args, /)\n"
"--\n"
"\n");
#define TYPEVARTUPLE_TYPING_PREPARE_SUBST_METHODDEF \
- {"__typing_prepare_subst__", _PyCFunction_CAST(typevartuple_typing_prepare_subst), METH_FASTCALL|METH_KEYWORDS, typevartuple_typing_prepare_subst__doc__},
+ {"__typing_prepare_subst__", _PyCFunction_CAST(typevartuple_typing_prepare_subst), METH_FASTCALL, typevartuple_typing_prepare_subst__doc__},
static PyObject *
typevartuple_typing_prepare_subst_impl(typevartupleobject *self,
PyObject *alias, PyObject *args);
static PyObject *
-typevartuple_typing_prepare_subst(typevartupleobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+typevartuple_typing_prepare_subst(typevartupleobject *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
- #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-
- #define NUM_KEYWORDS 2
- static struct {
- PyGC_Head _this_is_not_used;
- PyObject_VAR_HEAD
- PyObject *ob_item[NUM_KEYWORDS];
- } _kwtuple = {
- .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
- .ob_item = { &_Py_ID(alias), &_Py_ID(args), },
- };
- #undef NUM_KEYWORDS
- #define KWTUPLE (&_kwtuple.ob_base.ob_base)
-
- #else // !Py_BUILD_CORE
- # define KWTUPLE NULL
- #endif // !Py_BUILD_CORE
-
- static const char * const _keywords[] = {"alias", "args", NULL};
- static _PyArg_Parser _parser = {
- .keywords = _keywords,
- .fname = "__typing_prepare_subst__",
- .kwtuple = KWTUPLE,
- };
- #undef KWTUPLE
- PyObject *argsbuf[2];
PyObject *alias;
PyObject *__clinic_args;
- args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf);
- if (!args) {
+ if (!_PyArg_CheckPositional("__typing_prepare_subst__", nargs, 2, 2)) {
goto exit;
}
alias = args[0];
@@ -699,6 +594,23 @@ typevartuple_reduce(typevartupleobject *self, PyObject *Py_UNUSED(ignored))
return typevartuple_reduce_impl(self);
}
+PyDoc_STRVAR(typevartuple_has_default__doc__,
+"has_default($self, /)\n"
+"--\n"
+"\n");
+
+#define TYPEVARTUPLE_HAS_DEFAULT_METHODDEF \
+ {"has_default", (PyCFunction)typevartuple_has_default, METH_NOARGS, typevartuple_has_default__doc__},
+
+static PyObject *
+typevartuple_has_default_impl(typevartupleobject *self);
+
+static PyObject *
+typevartuple_has_default(typevartupleobject *self, PyObject *Py_UNUSED(ignored))
+{
+ return typevartuple_has_default_impl(self);
+}
+
PyDoc_STRVAR(typealias_reduce__doc__,
"__reduce__($self, /)\n"
"--\n"
@@ -783,4 +695,4 @@ skip_optional_kwonly:
exit:
return return_value;
}
-/*[clinic end generated code: output=807bcd30ebd10ac3 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=73b39e550e4e336c input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/clinic/unicodeobject.c.h b/contrib/tools/python3/Objects/clinic/unicodeobject.c.h
index 27cbf9d1543..1db304e7063 100644
--- a/contrib/tools/python3/Objects/clinic/unicodeobject.c.h
+++ b/contrib/tools/python3/Objects/clinic/unicodeobject.c.h
@@ -3,10 +3,11 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_abstract.h" // _PyNumber_Index()
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
PyDoc_STRVAR(EncodingMap_size__doc__,
"size($self, /)\n"
@@ -135,6 +136,61 @@ exit:
return return_value;
}
+PyDoc_STRVAR(unicode_count__doc__,
+"count($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the number of non-overlapping occurrences of substring sub in string S[start:end].\n"
+"\n"
+"Optional arguments start and end are interpreted as in slice notation.");
+
+#define UNICODE_COUNT_METHODDEF \
+ {"count", _PyCFunction_CAST(unicode_count), METH_FASTCALL, unicode_count__doc__},
+
+static Py_ssize_t
+unicode_count_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+unicode_count(PyObject *str, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *substr;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+ Py_ssize_t _return_value;
+
+ if (!_PyArg_CheckPositional("count", nargs, 1, 3)) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("count", "argument 1", "str", args[0]);
+ goto exit;
+ }
+ substr = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ _return_value = unicode_count_impl(str, substr, start, end);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyLong_FromSsize_t(_return_value);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(unicode_encode__doc__,
"encode($self, /, encoding=\'utf-8\', errors=\'strict\')\n"
"--\n"
@@ -289,7 +345,7 @@ unicode_expandtabs(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyOb
if (!noptargs) {
goto skip_optional_pos;
}
- tabsize = _PyLong_AsInt(args[0]);
+ tabsize = PyLong_AsInt(args[0]);
if (tabsize == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -300,6 +356,118 @@ exit:
return return_value;
}
+PyDoc_STRVAR(unicode_find__doc__,
+"find($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].\n"
+"\n"
+"Optional arguments start and end are interpreted as in slice notation.\n"
+"Return -1 on failure.");
+
+#define UNICODE_FIND_METHODDEF \
+ {"find", _PyCFunction_CAST(unicode_find), METH_FASTCALL, unicode_find__doc__},
+
+static Py_ssize_t
+unicode_find_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+unicode_find(PyObject *str, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *substr;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+ Py_ssize_t _return_value;
+
+ if (!_PyArg_CheckPositional("find", nargs, 1, 3)) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("find", "argument 1", "str", args[0]);
+ goto exit;
+ }
+ substr = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ _return_value = unicode_find_impl(str, substr, start, end);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyLong_FromSsize_t(_return_value);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(unicode_index__doc__,
+"index($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].\n"
+"\n"
+"Optional arguments start and end are interpreted as in slice notation.\n"
+"Raises ValueError when the substring is not found.");
+
+#define UNICODE_INDEX_METHODDEF \
+ {"index", _PyCFunction_CAST(unicode_index), METH_FASTCALL, unicode_index__doc__},
+
+static Py_ssize_t
+unicode_index_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+unicode_index(PyObject *str, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *substr;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+ Py_ssize_t _return_value;
+
+ if (!_PyArg_CheckPositional("index", nargs, 1, 3)) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("index", "argument 1", "str", args[0]);
+ goto exit;
+ }
+ substr = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ _return_value = unicode_index_impl(str, substr, start, end);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyLong_FromSsize_t(_return_value);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(unicode_isascii__doc__,
"isascii($self, /)\n"
"--\n"
@@ -535,10 +703,9 @@ PyDoc_STRVAR(unicode_isprintable__doc__,
"isprintable($self, /)\n"
"--\n"
"\n"
-"Return True if the string is printable, False otherwise.\n"
+"Return True if all characters in the string are printable, False otherwise.\n"
"\n"
-"A string is printable if all of its characters are considered printable in\n"
-"repr() or if it is empty.");
+"A character is printable if repr() may use it in its output.");
#define UNICODE_ISPRINTABLE_METHODDEF \
{"isprintable", (PyCFunction)unicode_isprintable, METH_NOARGS, unicode_isprintable__doc__},
@@ -736,7 +903,7 @@ exit:
}
PyDoc_STRVAR(unicode_replace__doc__,
-"replace($self, old, new, count=-1, /)\n"
+"replace($self, old, new, /, count=-1)\n"
"--\n"
"\n"
"Return a copy with all occurrences of substring old replaced by new.\n"
@@ -749,41 +916,63 @@ PyDoc_STRVAR(unicode_replace__doc__,
"replaced.");
#define UNICODE_REPLACE_METHODDEF \
- {"replace", _PyCFunction_CAST(unicode_replace), METH_FASTCALL, unicode_replace__doc__},
+ {"replace", _PyCFunction_CAST(unicode_replace), METH_FASTCALL|METH_KEYWORDS, unicode_replace__doc__},
static PyObject *
unicode_replace_impl(PyObject *self, PyObject *old, PyObject *new,
Py_ssize_t count);
static PyObject *
-unicode_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+unicode_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
+ #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+ #define NUM_KEYWORDS 1
+ static struct {
+ PyGC_Head _this_is_not_used;
+ PyObject_VAR_HEAD
+ PyObject *ob_item[NUM_KEYWORDS];
+ } _kwtuple = {
+ .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+ .ob_item = { &_Py_ID(count), },
+ };
+ #undef NUM_KEYWORDS
+ #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+ #else // !Py_BUILD_CORE
+ # define KWTUPLE NULL
+ #endif // !Py_BUILD_CORE
+
+ static const char * const _keywords[] = {"", "", "count", NULL};
+ static _PyArg_Parser _parser = {
+ .keywords = _keywords,
+ .fname = "replace",
+ .kwtuple = KWTUPLE,
+ };
+ #undef KWTUPLE
+ PyObject *argsbuf[3];
+ Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2;
PyObject *old;
PyObject *new;
Py_ssize_t count = -1;
- if (!_PyArg_CheckPositional("replace", nargs, 2, 3)) {
+ args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 3, 0, argsbuf);
+ if (!args) {
goto exit;
}
if (!PyUnicode_Check(args[0])) {
_PyArg_BadArgument("replace", "argument 1", "str", args[0]);
goto exit;
}
- if (PyUnicode_READY(args[0]) == -1) {
- goto exit;
- }
old = args[0];
if (!PyUnicode_Check(args[1])) {
_PyArg_BadArgument("replace", "argument 2", "str", args[1]);
goto exit;
}
- if (PyUnicode_READY(args[1]) == -1) {
- goto exit;
- }
new = args[1];
- if (nargs < 3) {
- goto skip_optional;
+ if (!noptargs) {
+ goto skip_optional_pos;
}
{
Py_ssize_t ival = -1;
@@ -797,7 +986,7 @@ unicode_replace(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
}
count = ival;
}
-skip_optional:
+skip_optional_pos:
return_value = unicode_replace_impl(self, old, new, count);
exit:
@@ -829,9 +1018,6 @@ unicode_removeprefix(PyObject *self, PyObject *arg)
_PyArg_BadArgument("removeprefix", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
prefix = arg;
return_value = unicode_removeprefix_impl(self, prefix);
@@ -865,9 +1051,6 @@ unicode_removesuffix(PyObject *self, PyObject *arg)
_PyArg_BadArgument("removesuffix", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
suffix = arg;
return_value = unicode_removesuffix_impl(self, suffix);
@@ -875,6 +1058,118 @@ exit:
return return_value;
}
+PyDoc_STRVAR(unicode_rfind__doc__,
+"rfind($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].\n"
+"\n"
+"Optional arguments start and end are interpreted as in slice notation.\n"
+"Return -1 on failure.");
+
+#define UNICODE_RFIND_METHODDEF \
+ {"rfind", _PyCFunction_CAST(unicode_rfind), METH_FASTCALL, unicode_rfind__doc__},
+
+static Py_ssize_t
+unicode_rfind_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+unicode_rfind(PyObject *str, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *substr;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+ Py_ssize_t _return_value;
+
+ if (!_PyArg_CheckPositional("rfind", nargs, 1, 3)) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("rfind", "argument 1", "str", args[0]);
+ goto exit;
+ }
+ substr = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ _return_value = unicode_rfind_impl(str, substr, start, end);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyLong_FromSsize_t(_return_value);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(unicode_rindex__doc__,
+"rindex($self, sub[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].\n"
+"\n"
+"Optional arguments start and end are interpreted as in slice notation.\n"
+"Raises ValueError when the substring is not found.");
+
+#define UNICODE_RINDEX_METHODDEF \
+ {"rindex", _PyCFunction_CAST(unicode_rindex), METH_FASTCALL, unicode_rindex__doc__},
+
+static Py_ssize_t
+unicode_rindex_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+unicode_rindex(PyObject *str, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *substr;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+ Py_ssize_t _return_value;
+
+ if (!_PyArg_CheckPositional("rindex", nargs, 1, 3)) {
+ goto exit;
+ }
+ if (!PyUnicode_Check(args[0])) {
+ _PyArg_BadArgument("rindex", "argument 1", "str", args[0]);
+ goto exit;
+ }
+ substr = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ _return_value = unicode_rindex_impl(str, substr, start, end);
+ if ((_return_value == -1) && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = PyLong_FromSsize_t(_return_value);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(unicode_rjust__doc__,
"rjust($self, width, fillchar=\' \', /)\n"
"--\n"
@@ -1263,9 +1558,6 @@ unicode_maketrans(void *null, PyObject *const *args, Py_ssize_t nargs)
_PyArg_BadArgument("maketrans", "argument 2", "str", args[1]);
goto exit;
}
- if (PyUnicode_READY(args[1]) == -1) {
- goto exit;
- }
y = args[1];
if (nargs < 3) {
goto skip_optional;
@@ -1274,9 +1566,6 @@ unicode_maketrans(void *null, PyObject *const *args, Py_ssize_t nargs)
_PyArg_BadArgument("maketrans", "argument 3", "str", args[2]);
goto exit;
}
- if (PyUnicode_READY(args[2]) == -1) {
- goto exit;
- }
z = args[2];
skip_optional:
return_value = unicode_maketrans_impl(x, y, z);
@@ -1358,6 +1647,108 @@ exit:
return return_value;
}
+PyDoc_STRVAR(unicode_startswith__doc__,
+"startswith($self, prefix[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return True if the string starts with the specified prefix, False otherwise.\n"
+"\n"
+" prefix\n"
+" A string or a tuple of strings to try.\n"
+" start\n"
+" Optional start position. Default: start of the string.\n"
+" end\n"
+" Optional stop position. Default: end of the string.");
+
+#define UNICODE_STARTSWITH_METHODDEF \
+ {"startswith", _PyCFunction_CAST(unicode_startswith), METH_FASTCALL, unicode_startswith__doc__},
+
+static PyObject *
+unicode_startswith_impl(PyObject *self, PyObject *subobj, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+unicode_startswith(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *subobj;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("startswith", nargs, 1, 3)) {
+ goto exit;
+ }
+ subobj = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = unicode_startswith_impl(self, subobj, start, end);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(unicode_endswith__doc__,
+"endswith($self, suffix[, start[, end]], /)\n"
+"--\n"
+"\n"
+"Return True if the string ends with the specified suffix, False otherwise.\n"
+"\n"
+" suffix\n"
+" A string or a tuple of strings to try.\n"
+" start\n"
+" Optional start position. Default: start of the string.\n"
+" end\n"
+" Optional stop position. Default: end of the string.");
+
+#define UNICODE_ENDSWITH_METHODDEF \
+ {"endswith", _PyCFunction_CAST(unicode_endswith), METH_FASTCALL, unicode_endswith__doc__},
+
+static PyObject *
+unicode_endswith_impl(PyObject *self, PyObject *subobj, Py_ssize_t start,
+ Py_ssize_t end);
+
+static PyObject *
+unicode_endswith(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *subobj;
+ Py_ssize_t start = 0;
+ Py_ssize_t end = PY_SSIZE_T_MAX;
+
+ if (!_PyArg_CheckPositional("endswith", nargs, 1, 3)) {
+ goto exit;
+ }
+ subobj = args[0];
+ if (nargs < 2) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[1], &start)) {
+ goto exit;
+ }
+ if (nargs < 3) {
+ goto skip_optional;
+ }
+ if (!_PyEval_SliceIndex(args[2], &end)) {
+ goto exit;
+ }
+skip_optional:
+ return_value = unicode_endswith_impl(self, subobj, start, end);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(unicode___format____doc__,
"__format__($self, format_spec, /)\n"
"--\n"
@@ -1380,9 +1771,6 @@ unicode___format__(PyObject *self, PyObject *arg)
_PyArg_BadArgument("__format__", "argument", "str", arg);
goto exit;
}
- if (PyUnicode_READY(arg) == -1) {
- goto exit;
- }
format_spec = arg;
return_value = unicode___format___impl(self, format_spec);
@@ -1499,4 +1887,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=d8f67f37fdbe21c4 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b7d75c4898e8198d input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/codeobject.c b/contrib/tools/python3/Objects/codeobject.c
index 1681d97613e..8a0b1027b94 100644
--- a/contrib/tools/python3/Objects/codeobject.c
+++ b/contrib/tools/python3/Objects/codeobject.c
@@ -1,17 +1,21 @@
-#include <stdbool.h>
-
#include "Python.h"
#include "opcode.h"
-#include "structmember.h" // PyMemberDef
+
#include "pycore_code.h" // _PyCodeConstructor
#include "pycore_frame.h" // FRAME_SPECIALS_SIZE
+#include "pycore_hashtable.h" // _Py_hashtable_t
+#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_interp.h" // PyInterpreterState.co_extra_freefuncs
-#include "pycore_opcode.h" // _PyOpcode_Deopt
+#include "pycore_object.h" // _PyObject_SetDeferredRefcount
+#include "pycore_opcode_metadata.h" // _PyOpcode_Deopt, _PyOpcode_Caches
+#include "pycore_opcode_utils.h" // RESUME_AT_FUNC_START
#include "pycore_pystate.h" // _PyInterpreterState_GET()
+#include "pycore_setobject.h" // _PySet_NextEntry()
#include "pycore_tuple.h" // _PyTuple_ITEMS()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#include "clinic/codeobject.c.h"
-static PyObject* code_repr(PyCodeObject *co);
+#include <stdbool.h>
static const char *
code_event_name(PyCodeEvent event) {
@@ -40,21 +44,9 @@ notify_code_watchers(PyCodeEvent event, PyCodeObject *co)
// callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, co) < 0) {
- // Don't risk resurrecting the object if an unraisablehook keeps
- // a reference; pass a string as context.
- PyObject *context = NULL;
- PyObject *repr = code_repr(co);
- if (repr) {
- context = PyUnicode_FromFormat(
- "%s watcher callback for %U",
- code_event_name(event), repr);
- Py_DECREF(repr);
- }
- if (context == NULL) {
- context = Py_NewRef(Py_None);
- }
- PyErr_WriteUnraisable(context);
- Py_DECREF(context);
+ PyErr_FormatUnraisable(
+ "Exception ignored in %s watcher callback for %R",
+ code_event_name(event), co);
}
}
i++;
@@ -111,10 +103,20 @@ PyCode_ClearWatcher(int watcher_id)
* generic helpers
******************/
-/* all_name_chars(s): true iff s matches [a-zA-Z0-9_]* */
static int
-all_name_chars(PyObject *o)
+should_intern_string(PyObject *o)
{
+#ifdef Py_GIL_DISABLED
+ // The free-threaded build interns (and immortalizes) all string constants
+ // unless we've disabled immortalizing objects that use deferred reference
+ // counting.
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (_Py_atomic_load_int(&interp->gc.immortalize) < 0) {
+ return 1;
+ }
+#endif
+
+ // compute if s matches [a-zA-Z0-9_]
const unsigned char *s, *e;
if (!PyUnicode_IS_ASCII(o))
@@ -129,6 +131,42 @@ all_name_chars(PyObject *o)
return 1;
}
+#ifdef Py_GIL_DISABLED
+static PyObject *intern_one_constant(PyObject *op);
+
+// gh-130851: In the free threading build, we intern and immortalize most
+// constants, except code objects. However, users can generate code objects
+// with arbitrary co_consts. We don't want to immortalize or intern unexpected
+// constants or tuples/sets containing unexpected constants.
+static int
+should_immortalize_constant(PyObject *v)
+{
+ // Only immortalize containers if we've already immortalized all their
+ // elements.
+ if (PyTuple_CheckExact(v)) {
+ for (Py_ssize_t i = PyTuple_GET_SIZE(v); --i >= 0; ) {
+ if (!_Py_IsImmortal(PyTuple_GET_ITEM(v, i))) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ else if (PyFrozenSet_CheckExact(v)) {
+ PyObject *item;
+ Py_hash_t hash;
+ Py_ssize_t pos = 0;
+ while (_PySet_NextEntry(v, &pos, &item, &hash)) {
+ if (!_Py_IsImmortal(item)) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ return (PyLong_CheckExact(v) || PyFloat_CheckExact(v) ||
+ PyComplex_Check(v) || PyBytes_CheckExact(v));
+}
+#endif
+
static int
intern_strings(PyObject *tuple)
{
@@ -147,19 +185,17 @@ intern_strings(PyObject *tuple)
return 0;
}
-/* Intern selected string constants */
+/* Intern constants. In the default build, this interns selected string
+ constants. In the free-threaded build, this also interns non-string
+ constants. */
static int
-intern_string_constants(PyObject *tuple, int *modified)
+intern_constants(PyObject *tuple, int *modified)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
for (Py_ssize_t i = PyTuple_GET_SIZE(tuple); --i >= 0; ) {
PyObject *v = PyTuple_GET_ITEM(tuple, i);
if (PyUnicode_CheckExact(v)) {
- if (PyUnicode_READY(v) == -1) {
- return -1;
- }
-
- if (all_name_chars(v)) {
+ if (should_intern_string(v)) {
PyObject *w = v;
_PyUnicode_InternMortal(interp, &v);
if (w != v) {
@@ -171,7 +207,7 @@ intern_string_constants(PyObject *tuple, int *modified)
}
}
else if (PyTuple_CheckExact(v)) {
- if (intern_string_constants(v, NULL) < 0) {
+ if (intern_constants(v, NULL) < 0) {
return -1;
}
}
@@ -182,7 +218,7 @@ intern_string_constants(PyObject *tuple, int *modified)
return -1;
}
int tmp_modified = 0;
- if (intern_string_constants(tmp, &tmp_modified) < 0) {
+ if (intern_constants(tmp, &tmp_modified) < 0) {
Py_DECREF(tmp);
return -1;
}
@@ -201,6 +237,59 @@ intern_string_constants(PyObject *tuple, int *modified)
}
Py_DECREF(tmp);
}
+#ifdef Py_GIL_DISABLED
+ else if (PySlice_Check(v)) {
+ PySliceObject *slice = (PySliceObject *)v;
+ PyObject *tmp = PyTuple_New(3);
+ if (tmp == NULL) {
+ return -1;
+ }
+ PyTuple_SET_ITEM(tmp, 0, Py_NewRef(slice->start));
+ PyTuple_SET_ITEM(tmp, 1, Py_NewRef(slice->stop));
+ PyTuple_SET_ITEM(tmp, 2, Py_NewRef(slice->step));
+ int tmp_modified = 0;
+ if (intern_constants(tmp, &tmp_modified) < 0) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ if (tmp_modified) {
+ v = PySlice_New(PyTuple_GET_ITEM(tmp, 0),
+ PyTuple_GET_ITEM(tmp, 1),
+ PyTuple_GET_ITEM(tmp, 2));
+ if (v == NULL) {
+ Py_DECREF(tmp);
+ return -1;
+ }
+ PyTuple_SET_ITEM(tuple, i, v);
+ Py_DECREF(slice);
+ if (modified) {
+ *modified = 1;
+ }
+ }
+ Py_DECREF(tmp);
+ }
+
+ // Intern non-string consants in the free-threaded build, but only if
+ // we are also immortalizing objects that use deferred reference
+ // counting.
+ PyThreadState *tstate = PyThreadState_GET();
+ if (!_Py_IsImmortal(v) && !PyUnicode_CheckExact(v) &&
+ should_immortalize_constant(v) &&
+ _Py_atomic_load_int(&tstate->interp->gc.immortalize) >= 0)
+ {
+ PyObject *interned = intern_one_constant(v);
+ if (interned == NULL) {
+ return -1;
+ }
+ else if (interned != v) {
+ PyTuple_SET_ITEM(tuple, i, interned);
+ Py_SETREF(v, interned);
+ if (modified) {
+ *modified = 1;
+ }
+ }
+ }
+#endif
}
return 0;
}
@@ -248,21 +337,32 @@ validate_and_copy_tuple(PyObject *tup)
}
static int
-init_co_cached(PyCodeObject *self) {
- if (self->_co_cached == NULL) {
- self->_co_cached = PyMem_New(_PyCoCached, 1);
- if (self->_co_cached == NULL) {
+init_co_cached(PyCodeObject *self)
+{
+ _PyCoCached *cached = FT_ATOMIC_LOAD_PTR(self->_co_cached);
+ if (cached != NULL) {
+ return 0;
+ }
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ cached = self->_co_cached;
+ if (cached == NULL) {
+ cached = PyMem_New(_PyCoCached, 1);
+ if (cached == NULL) {
PyErr_NoMemory();
- return -1;
}
- self->_co_cached->_co_code = NULL;
- self->_co_cached->_co_cellvars = NULL;
- self->_co_cached->_co_freevars = NULL;
- self->_co_cached->_co_varnames = NULL;
+ else {
+ cached->_co_code = NULL;
+ cached->_co_cellvars = NULL;
+ cached->_co_freevars = NULL;
+ cached->_co_varnames = NULL;
+ FT_ATOMIC_STORE_PTR(self->_co_cached, cached);
+ }
}
- return 0;
-
+ Py_END_CRITICAL_SECTION();
+ return cached != NULL ? 0 : -1;
}
+
/******************
* _PyCode_New()
******************/
@@ -400,10 +500,17 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
int nlocals, ncellvars, nfreevars;
get_localsplus_counts(con->localsplusnames, con->localspluskinds,
&nlocals, &ncellvars, &nfreevars);
+ if (con->stacksize == 0) {
+ con->stacksize = 1;
+ }
+ PyInterpreterState *interp = _PyInterpreterState_GET();
co->co_filename = Py_NewRef(con->filename);
co->co_name = Py_NewRef(con->name);
co->co_qualname = Py_NewRef(con->qualname);
+ _PyUnicode_InternMortal(interp, &co->co_filename);
+ _PyUnicode_InternMortal(interp, &co->co_name);
+ _PyUnicode_InternMortal(interp, &co->co_qualname);
co->co_flags = con->flags;
co->co_firstlineno = con->firstlineno;
@@ -429,16 +536,23 @@ init_code(PyCodeObject *co, struct _PyCodeConstructor *con)
co->co_framesize = nlocalsplus + con->stacksize + FRAME_SPECIALS_SIZE;
co->co_ncellvars = ncellvars;
co->co_nfreevars = nfreevars;
- co->co_version = _Py_next_func_version;
- if (_Py_next_func_version != 0) {
- _Py_next_func_version++;
+#ifdef Py_GIL_DISABLED
+ PyMutex_Lock(&interp->func_state.mutex);
+#endif
+ co->co_version = interp->func_state.next_version;
+ if (interp->func_state.next_version != 0) {
+ interp->func_state.next_version++;
}
+#ifdef Py_GIL_DISABLED
+ PyMutex_Unlock(&interp->func_state.mutex);
+#endif
co->_co_monitoring = NULL;
co->_co_instrumentation_version = 0;
/* not set */
co->co_weakreflist = NULL;
co->co_extra = NULL;
co->_co_cached = NULL;
+ co->co_executors = NULL;
memcpy(_PyCode_CODE(co), PyBytes_AS_STRING(con->code),
PyBytes_GET_SIZE(con->code));
@@ -543,29 +657,41 @@ remove_column_info(PyObject *locations)
return res;
}
-/* The caller is responsible for ensuring that the given data is valid. */
-
-PyCodeObject *
-_PyCode_New(struct _PyCodeConstructor *con)
+static int
+intern_code_constants(struct _PyCodeConstructor *con)
{
- /* Ensure that strings are ready Unicode string */
- if (PyUnicode_READY(con->name) < 0) {
- return NULL;
- }
- if (PyUnicode_READY(con->qualname) < 0) {
- return NULL;
- }
- if (PyUnicode_READY(con->filename) < 0) {
- return NULL;
- }
-
+#ifdef Py_GIL_DISABLED
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _py_code_state *state = &interp->code_state;
+ PyMutex_Lock(&state->mutex);
+#endif
if (intern_strings(con->names) < 0) {
- return NULL;
+ goto error;
}
- if (intern_string_constants(con->consts, NULL) < 0) {
- return NULL;
+ if (intern_constants(con->consts, NULL) < 0) {
+ goto error;
}
if (intern_strings(con->localsplusnames) < 0) {
+ goto error;
+ }
+#ifdef Py_GIL_DISABLED
+ PyMutex_Unlock(&state->mutex);
+#endif
+ return 0;
+
+error:
+#ifdef Py_GIL_DISABLED
+ PyMutex_Unlock(&state->mutex);
+#endif
+ return -1;
+}
+
+/* The caller is responsible for ensuring that the given data is valid. */
+
+PyCodeObject *
+_PyCode_New(struct _PyCodeConstructor *con)
+{
+ if (intern_code_constants(con) < 0) {
return NULL;
}
@@ -581,13 +707,22 @@ _PyCode_New(struct _PyCodeConstructor *con)
}
Py_ssize_t size = PyBytes_GET_SIZE(con->code) / sizeof(_Py_CODEUNIT);
- PyCodeObject *co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size);
+ PyCodeObject *co;
+#ifdef Py_GIL_DISABLED
+ co = PyObject_GC_NewVar(PyCodeObject, &PyCode_Type, size);
+#else
+ co = PyObject_NewVar(PyCodeObject, &PyCode_Type, size);
+#endif
if (co == NULL) {
Py_XDECREF(replacement_locations);
PyErr_NoMemory();
return NULL;
}
init_code(co, con);
+#ifdef Py_GIL_DISABLED
+ _PyObject_SetDeferredRefcount((PyObject *)co);
+ _PyObject_GC_TRACK(co);
+#endif
Py_XDECREF(replacement_locations);
return co;
}
@@ -773,7 +908,7 @@ PyUnstable_Code_New(int argcount, int kwonlyargcount,
// test.test_code.CodeLocationTest.test_code_new_empty to keep it in sync!
static const uint8_t assert0[6] = {
- RESUME, 0,
+ RESUME, RESUME_AT_FUNC_START,
LOAD_ASSERTION_ERROR, 0,
RAISE_VARARGS, 1
};
@@ -853,7 +988,27 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq)
if (addrq < 0) {
return co->co_firstlineno;
}
- assert(addrq >= 0 && addrq <= _PyCode_NBYTES(co));
+ if (co->_co_monitoring && co->_co_monitoring->lines) {
+ return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT));
+ }
+ // assert(addrq >= 0 && addrq < _PyCode_NBYTES(co));
+ PyCodeAddressRange bounds;
+ _PyCode_InitAddressRange(co, &bounds);
+ return _PyCode_CheckLineNumber(addrq, &bounds);
+}
+
+int
+_PyCode_SafeAddr2Line(PyCodeObject *co, int addrq)
+{
+ if (addrq < 0) {
+ return co->co_firstlineno;
+ }
+ if (co->_co_monitoring && co->_co_monitoring->lines) {
+ return _Py_Instrumentation_GetLine(co, addrq/sizeof(_Py_CODEUNIT));
+ }
+ if (!(addrq >= 0 && addrq < _PyCode_NBYTES(co))) {
+ return -1;
+ }
PyCodeAddressRange bounds;
_PyCode_InitAddressRange(co, &bounds);
return _PyCode_CheckLineNumber(addrq, &bounds);
@@ -1453,16 +1608,21 @@ get_cached_locals(PyCodeObject *co, PyObject **cached_field,
{
assert(cached_field != NULL);
assert(co->_co_cached != NULL);
- if (*cached_field != NULL) {
- return Py_NewRef(*cached_field);
+ PyObject *varnames = FT_ATOMIC_LOAD_PTR(*cached_field);
+ if (varnames != NULL) {
+ return Py_NewRef(varnames);
}
- assert(*cached_field == NULL);
- PyObject *varnames = get_localsplus_names(co, kind, num);
+
+ Py_BEGIN_CRITICAL_SECTION(co);
+ varnames = *cached_field;
if (varnames == NULL) {
- return NULL;
+ varnames = get_localsplus_names(co, kind, num);
+ if (varnames != NULL) {
+ FT_ATOMIC_STORE_PTR(*cached_field, varnames);
+ }
}
- *cached_field = Py_NewRef(varnames);
- return varnames;
+ Py_END_CRITICAL_SECTION();
+ return Py_XNewRef(varnames);
}
PyObject *
@@ -1510,12 +1670,42 @@ PyCode_GetFreevars(PyCodeObject *code)
return _PyCode_GetFreevars(code);
}
+#ifdef _Py_TIER2
+
+static void
+clear_executors(PyCodeObject *co)
+{
+ assert(co->co_executors);
+ for (int i = 0; i < co->co_executors->size; i++) {
+ if (co->co_executors->executors[i]) {
+ _Py_ExecutorDetach(co->co_executors->executors[i]);
+ assert(co->co_executors->executors[i] == NULL);
+ }
+ }
+ PyMem_Free(co->co_executors);
+ co->co_executors = NULL;
+}
+
+void
+_PyCode_Clear_Executors(PyCodeObject *code)
+{
+ clear_executors(code);
+}
+
+#endif
+
static void
deopt_code(PyCodeObject *code, _Py_CODEUNIT *instructions)
{
Py_ssize_t len = Py_SIZE(code);
for (int i = 0; i < len; i++) {
int opcode = _Py_GetBaseOpcode(code, i);
+ if (opcode == ENTER_EXECUTOR) {
+ _PyExecutorObject *exec = code->co_executors->executors[instructions[i].op.arg];
+ opcode = _PyOpcode_Deopt[exec->vm_data.opcode];
+ instructions[i].op.arg = exec->vm_data.oparg;
+ }
+ assert(opcode != ENTER_EXECUTOR);
int caches = _PyOpcode_Caches[opcode];
instructions[i].op.code = opcode;
for (int j = 1; j <= caches; j++) {
@@ -1531,18 +1721,26 @@ _PyCode_GetCode(PyCodeObject *co)
if (init_co_cached(co)) {
return NULL;
}
- if (co->_co_cached->_co_code != NULL) {
- return Py_NewRef(co->_co_cached->_co_code);
+
+ _PyCoCached *cached = co->_co_cached;
+ PyObject *code = FT_ATOMIC_LOAD_PTR(cached->_co_code);
+ if (code != NULL) {
+ return Py_NewRef(code);
}
- PyObject *code = PyBytes_FromStringAndSize((const char *)_PyCode_CODE(co),
- _PyCode_NBYTES(co));
+
+ Py_BEGIN_CRITICAL_SECTION(co);
+ code = cached->_co_code;
if (code == NULL) {
- return NULL;
+ code = PyBytes_FromStringAndSize((const char *)_PyCode_CODE(co),
+ _PyCode_NBYTES(co));
+ if (code != NULL) {
+ deopt_code(co, (_Py_CODEUNIT *)PyBytes_AS_STRING(code));
+ assert(cached->_co_code == NULL);
+ FT_ATOMIC_STORE_PTR(cached->_co_code, code);
+ }
}
- deopt_code(co, (_Py_CODEUNIT *)PyBytes_AS_STRING(code));
- assert(co->_co_cached->_co_code == NULL);
- co->_co_cached->_co_code = Py_NewRef(code);
- return code;
+ Py_END_CRITICAL_SECTION();
+ return Py_XNewRef(code);
}
PyObject *
@@ -1700,15 +1898,17 @@ free_monitoring_data(_PyCoMonitoringData *data)
static void
code_dealloc(PyCodeObject *co)
{
- assert(Py_REFCNT(co) == 0);
- Py_SET_REFCNT(co, 1);
+ _PyObject_ResurrectStart((PyObject *)co);
notify_code_watchers(PY_CODE_EVENT_DESTROY, co);
- if (Py_REFCNT(co) > 1) {
- Py_SET_REFCNT(co, Py_REFCNT(co) - 1);
+ if (_PyObject_ResurrectEnd((PyObject *)co)) {
return;
}
- Py_SET_REFCNT(co, 0);
+#ifdef Py_GIL_DISABLED
+ PyObject_GC_UnTrack(co);
+#endif
+
+ _PyFunction_ClearCodeByVersion(co->co_version);
if (co->co_extra != NULL) {
PyInterpreterState *interp = _PyInterpreterState_GET();
_PyCodeObjectExtra *co_extra = co->co_extra;
@@ -1723,6 +1923,11 @@ code_dealloc(PyCodeObject *co)
PyMem_Free(co_extra);
}
+#ifdef _Py_TIER2
+ if (co->co_executors != NULL) {
+ clear_executors(co);
+ }
+#endif
Py_XDECREF(co->co_consts);
Py_XDECREF(co->co_names);
@@ -1740,13 +1945,20 @@ code_dealloc(PyCodeObject *co)
Py_XDECREF(co->_co_cached->_co_varnames);
PyMem_Free(co->_co_cached);
}
- if (co->co_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject*)co);
- }
+ FT_CLEAR_WEAKREFS((PyObject*)co, co->co_weakreflist);
free_monitoring_data(co->_co_monitoring);
PyObject_Free(co);
}
+#ifdef Py_GIL_DISABLED
+static int
+code_traverse(PyCodeObject *co, visitproc visit, void *arg)
+{
+ Py_VISIT(co->co_consts);
+ return 0;
+}
+#endif
+
static PyObject *
code_repr(PyCodeObject *co)
{
@@ -1802,13 +2014,31 @@ code_richcompare(PyObject *self, PyObject *other, int op)
for (int i = 0; i < Py_SIZE(co); i++) {
_Py_CODEUNIT co_instr = _PyCode_CODE(co)[i];
_Py_CODEUNIT cp_instr = _PyCode_CODE(cp)[i];
- co_instr.op.code = _Py_GetBaseOpcode(co, i);
- cp_instr.op.code = _Py_GetBaseOpcode(cp, i);
- eq = co_instr.cache == cp_instr.cache;
- if (!eq) {
+ uint8_t co_code = _Py_GetBaseOpcode(co, i);
+ uint8_t co_arg = co_instr.op.arg;
+ uint8_t cp_code = _Py_GetBaseOpcode(cp, i);
+ uint8_t cp_arg = cp_instr.op.arg;
+
+ if (co_code == ENTER_EXECUTOR) {
+ const int exec_index = co_arg;
+ _PyExecutorObject *exec = co->co_executors->executors[exec_index];
+ co_code = _PyOpcode_Deopt[exec->vm_data.opcode];
+ co_arg = exec->vm_data.oparg;
+ }
+ assert(co_code != ENTER_EXECUTOR);
+
+ if (cp_code == ENTER_EXECUTOR) {
+ const int exec_index = cp_arg;
+ _PyExecutorObject *exec = cp->co_executors->executors[exec_index];
+ cp_code = _PyOpcode_Deopt[exec->vm_data.opcode];
+ cp_arg = exec->vm_data.oparg;
+ }
+ assert(cp_code != ENTER_EXECUTOR);
+
+ if (co_code != cp_code || co_arg != cp_arg) {
goto unequal;
}
- i += _PyOpcode_Caches[co_instr.op.code];
+ i += _PyOpcode_Caches[co_code];
}
/* compare constants */
@@ -1864,7 +2094,7 @@ code_hash(PyCodeObject *co)
Py_uhash_t uhash = 20221211;
#define SCRAMBLE_IN(H) do { \
uhash ^= (Py_uhash_t)(H); \
- uhash *= _PyHASH_MULTIPLIER; \
+ uhash *= PyHASH_MULTIPLIER; \
} while (0)
#define SCRAMBLE_IN_HASH(EXPR) do { \
Py_hash_t h = PyObject_Hash(EXPR); \
@@ -1887,10 +2117,22 @@ code_hash(PyCodeObject *co)
SCRAMBLE_IN(co->co_firstlineno);
SCRAMBLE_IN(Py_SIZE(co));
for (int i = 0; i < Py_SIZE(co); i++) {
- int deop = _Py_GetBaseOpcode(co, i);
- SCRAMBLE_IN(deop);
- SCRAMBLE_IN(_PyCode_CODE(co)[i].op.arg);
- i += _PyOpcode_Caches[deop];
+ _Py_CODEUNIT co_instr = _PyCode_CODE(co)[i];
+ uint8_t co_code = co_instr.op.code;
+ uint8_t co_arg = co_instr.op.arg;
+ if (co_code == ENTER_EXECUTOR) {
+ _PyExecutorObject *exec = co->co_executors->executors[co_arg];
+ assert(exec != NULL);
+ assert(exec->vm_data.opcode != ENTER_EXECUTOR);
+ co_code = _PyOpcode_Deopt[exec->vm_data.opcode];
+ co_arg = exec->vm_data.oparg;
+ }
+ else {
+ co_code = _Py_GetBaseOpcode(co, i);
+ }
+ SCRAMBLE_IN(co_code);
+ SCRAMBLE_IN(co_arg);
+ i += _PyOpcode_Caches[co_code];
}
if ((Py_hash_t)uhash == -1) {
return -2;
@@ -1902,20 +2144,20 @@ code_hash(PyCodeObject *co)
#define OFF(x) offsetof(PyCodeObject, x)
static PyMemberDef code_memberlist[] = {
- {"co_argcount", T_INT, OFF(co_argcount), READONLY},
- {"co_posonlyargcount", T_INT, OFF(co_posonlyargcount), READONLY},
- {"co_kwonlyargcount", T_INT, OFF(co_kwonlyargcount), READONLY},
- {"co_stacksize", T_INT, OFF(co_stacksize), READONLY},
- {"co_flags", T_INT, OFF(co_flags), READONLY},
- {"co_nlocals", T_INT, OFF(co_nlocals), READONLY},
- {"co_consts", T_OBJECT, OFF(co_consts), READONLY},
- {"co_names", T_OBJECT, OFF(co_names), READONLY},
- {"co_filename", T_OBJECT, OFF(co_filename), READONLY},
- {"co_name", T_OBJECT, OFF(co_name), READONLY},
- {"co_qualname", T_OBJECT, OFF(co_qualname), READONLY},
- {"co_firstlineno", T_INT, OFF(co_firstlineno), READONLY},
- {"co_linetable", T_OBJECT, OFF(co_linetable), READONLY},
- {"co_exceptiontable", T_OBJECT, OFF(co_exceptiontable), READONLY},
+ {"co_argcount", Py_T_INT, OFF(co_argcount), Py_READONLY},
+ {"co_posonlyargcount", Py_T_INT, OFF(co_posonlyargcount), Py_READONLY},
+ {"co_kwonlyargcount", Py_T_INT, OFF(co_kwonlyargcount), Py_READONLY},
+ {"co_stacksize", Py_T_INT, OFF(co_stacksize), Py_READONLY},
+ {"co_flags", Py_T_INT, OFF(co_flags), Py_READONLY},
+ {"co_nlocals", Py_T_INT, OFF(co_nlocals), Py_READONLY},
+ {"co_consts", _Py_T_OBJECT, OFF(co_consts), Py_READONLY},
+ {"co_names", _Py_T_OBJECT, OFF(co_names), Py_READONLY},
+ {"co_filename", _Py_T_OBJECT, OFF(co_filename), Py_READONLY},
+ {"co_name", _Py_T_OBJECT, OFF(co_name), Py_READONLY},
+ {"co_qualname", _Py_T_OBJECT, OFF(co_qualname), Py_READONLY},
+ {"co_firstlineno", Py_T_INT, OFF(co_firstlineno), Py_READONLY},
+ {"co_linetable", _Py_T_OBJECT, OFF(co_linetable), Py_READONLY},
+ {"co_exceptiontable", _Py_T_OBJECT, OFF(co_exceptiontable), Py_READONLY},
{NULL} /* Sentinel */
};
@@ -2135,6 +2377,8 @@ static struct PyMethodDef code_methods[] = {
{"co_positions", (PyCFunction)code_positionsiterator, METH_NOARGS},
CODE_REPLACE_METHODDEF
CODE__VARNAME_FROM_OPARG_METHODDEF
+ {"__replace__", _PyCFunction_CAST(code_replace), METH_FASTCALL|METH_KEYWORDS,
+ PyDoc_STR("__replace__($self, /, **changes)\n--\n\nThe same as replace().")},
{NULL, NULL} /* sentinel */
};
@@ -2159,9 +2403,17 @@ PyTypeObject PyCode_Type = {
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
+#ifdef Py_GIL_DISABLED
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
+#else
Py_TPFLAGS_DEFAULT, /* tp_flags */
+#endif
code_new__doc__, /* tp_doc */
+#ifdef Py_GIL_DISABLED
+ (traverseproc)code_traverse, /* tp_traverse */
+#else
0, /* tp_traverse */
+#endif
0, /* tp_clear */
code_richcompare, /* tp_richcompare */
offsetof(PyCodeObject, co_weakreflist), /* tp_weaklistoffset */
@@ -2314,118 +2566,188 @@ _PyCode_ConstantKey(PyObject *op)
return key;
}
-void
-_PyStaticCode_Fini(PyCodeObject *co)
+#ifdef Py_GIL_DISABLED
+static PyObject *
+intern_one_constant(PyObject *op)
{
- deopt_code(co, _PyCode_CODE(co));
- PyMem_Free(co->co_extra);
- if (co->_co_cached != NULL) {
- Py_CLEAR(co->_co_cached->_co_code);
- Py_CLEAR(co->_co_cached->_co_cellvars);
- Py_CLEAR(co->_co_cached->_co_freevars);
- Py_CLEAR(co->_co_cached->_co_varnames);
- PyMem_Free(co->_co_cached);
- co->_co_cached = NULL;
- }
- co->co_extra = NULL;
- if (co->co_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *)co);
- co->co_weakreflist = NULL;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ _Py_hashtable_t *consts = interp->code_state.constants;
+
+ assert(!PyUnicode_CheckExact(op)); // strings are interned separately
+
+ _Py_hashtable_entry_t *entry = _Py_hashtable_get_entry(consts, op);
+ if (entry == NULL) {
+ if (_Py_hashtable_set(consts, op, op) != 0) {
+ PyErr_NoMemory();
+ return NULL;
+ }
+
+#ifdef Py_REF_DEBUG
+ Py_ssize_t refcnt = Py_REFCNT(op);
+ if (refcnt != 1) {
+ // Adjust the reftotal to account for the fact that we only
+ // restore a single reference in _PyCode_Fini.
+ _Py_AddRefTotal(_PyThreadState_GET(), -(refcnt - 1));
+ }
+#endif
+
+ _Py_SetImmortal(op);
+ return op;
}
- free_monitoring_data(co->_co_monitoring);
- co->_co_monitoring = NULL;
+
+ assert(_Py_IsImmortal(entry->value));
+ return (PyObject *)entry->value;
}
-int
-_PyStaticCode_Init(PyCodeObject *co)
+static int
+compare_constants(const void *key1, const void *key2)
{
- int res = intern_strings(co->co_names);
- if (res < 0) {
- return -1;
+ PyObject *op1 = (PyObject *)key1;
+ PyObject *op2 = (PyObject *)key2;
+ if (op1 == op2) {
+ return 1;
}
- res = intern_string_constants(co->co_consts, NULL);
- if (res < 0) {
- return -1;
+ if (Py_TYPE(op1) != Py_TYPE(op2)) {
+ return 0;
}
- res = intern_strings(co->co_localsplusnames);
- if (res < 0) {
- return -1;
+ // We compare container contents by identity because we have already
+ // internalized the items.
+ if (PyTuple_CheckExact(op1)) {
+ Py_ssize_t size = PyTuple_GET_SIZE(op1);
+ if (size != PyTuple_GET_SIZE(op2)) {
+ return 0;
+ }
+ for (Py_ssize_t i = 0; i < size; i++) {
+ if (PyTuple_GET_ITEM(op1, i) != PyTuple_GET_ITEM(op2, i)) {
+ return 0;
+ }
+ }
+ return 1;
}
- _PyCode_Quicken(co);
+ else if (PyFrozenSet_CheckExact(op1)) {
+ if (PySet_GET_SIZE(op1) != PySet_GET_SIZE(op2)) {
+ return 0;
+ }
+ Py_ssize_t pos1 = 0, pos2 = 0;
+ PyObject *obj1, *obj2;
+ Py_hash_t hash1, hash2;
+ while ((_PySet_NextEntry(op1, &pos1, &obj1, &hash1)) &&
+ (_PySet_NextEntry(op2, &pos2, &obj2, &hash2)))
+ {
+ if (obj1 != obj2) {
+ return 0;
+ }
+ }
+ return 1;
+ }
+ else if (PySlice_Check(op1)) {
+ PySliceObject *s1 = (PySliceObject *)op1;
+ PySliceObject *s2 = (PySliceObject *)op2;
+ return (s1->start == s2->start &&
+ s1->stop == s2->stop &&
+ s1->step == s2->step);
+ }
+ else if (PyBytes_CheckExact(op1) || PyLong_CheckExact(op1)) {
+ return PyObject_RichCompareBool(op1, op2, Py_EQ);
+ }
+ else if (PyFloat_CheckExact(op1)) {
+ // Ensure that, for example, +0.0 and -0.0 are distinct
+ double f1 = PyFloat_AS_DOUBLE(op1);
+ double f2 = PyFloat_AS_DOUBLE(op2);
+ return memcmp(&f1, &f2, sizeof(double)) == 0;
+ }
+ else if (PyComplex_CheckExact(op1)) {
+ Py_complex c1 = ((PyComplexObject *)op1)->cval;
+ Py_complex c2 = ((PyComplexObject *)op2)->cval;
+ return memcmp(&c1, &c2, sizeof(Py_complex)) == 0;
+ }
+ // gh-130851: Treat instances of unexpected types as distinct if they are
+ // not the same object.
return 0;
}
-#define MAX_CODE_UNITS_PER_LOC_ENTRY 8
-
-PyCodeObject *
-_Py_MakeShimCode(const _PyShimCodeDef *codedef)
+static Py_uhash_t
+hash_const(const void *key)
{
- PyObject *name = NULL;
- PyObject *co_code = NULL;
- PyObject *lines = NULL;
- PyCodeObject *codeobj = NULL;
- uint8_t *loc_table = NULL;
-
- name = _PyUnicode_FromASCII(codedef->cname, strlen(codedef->cname));
- if (name == NULL) {
- goto cleanup;
+ PyObject *op = (PyObject *)key;
+ if (PySlice_Check(op)) {
+ PySliceObject *s = (PySliceObject *)op;
+ PyObject *data[3] = { s->start, s->stop, s->step };
+ return _Py_HashBytes(&data, sizeof(data));
}
- co_code = PyBytes_FromStringAndSize(
- (const char *)codedef->code, codedef->codelen);
- if (co_code == NULL) {
- goto cleanup;
+ else if (PyTuple_CheckExact(op)) {
+ Py_ssize_t size = PyTuple_GET_SIZE(op);
+ PyObject **data = _PyTuple_ITEMS(op);
+ return _Py_HashBytes(data, sizeof(PyObject *) * size);
}
- int code_units = codedef->codelen / sizeof(_Py_CODEUNIT);
- int loc_entries = (code_units + MAX_CODE_UNITS_PER_LOC_ENTRY - 1) /
- MAX_CODE_UNITS_PER_LOC_ENTRY;
- loc_table = PyMem_Malloc(loc_entries);
- if (loc_table == NULL) {
- PyErr_NoMemory();
- goto cleanup;
+ Py_hash_t h = PyObject_Hash(op);
+ if (h == -1) {
+ // gh-130851: Other than slice objects, every constant that the
+ // bytecode compiler generates is hashable. However, users can
+ // provide their own constants, when constructing code objects via
+ // types.CodeType(). If the user-provided constant is unhashable, we
+ // use the memory address of the object as a fallback hash value.
+ PyErr_Clear();
+ return (Py_uhash_t)(uintptr_t)key;
}
- for (int i = 0; i < loc_entries-1; i++) {
- loc_table[i] = 0x80 | (PY_CODE_LOCATION_INFO_NONE << 3) | 7;
- code_units -= MAX_CODE_UNITS_PER_LOC_ENTRY;
+ return (Py_uhash_t)h;
+}
+
+static int
+clear_containers(_Py_hashtable_t *ht, const void *key, const void *value,
+ void *user_data)
+{
+ // First clear containers to avoid recursive deallocation later on in
+ // destroy_key.
+ PyObject *op = (PyObject *)key;
+ if (PyTuple_CheckExact(op)) {
+ for (Py_ssize_t i = 0; i < PyTuple_GET_SIZE(op); i++) {
+ Py_CLEAR(_PyTuple_ITEMS(op)[i]);
+ }
}
- assert(loc_entries > 0);
- assert(code_units > 0 && code_units <= MAX_CODE_UNITS_PER_LOC_ENTRY);
- loc_table[loc_entries-1] = 0x80 |
- (PY_CODE_LOCATION_INFO_NONE << 3) | (code_units-1);
- lines = PyBytes_FromStringAndSize((const char *)loc_table, loc_entries);
- PyMem_Free(loc_table);
- if (lines == NULL) {
- goto cleanup;
+ else if (PySlice_Check(op)) {
+ PySliceObject *slice = (PySliceObject *)op;
+ Py_SETREF(slice->start, Py_None);
+ Py_SETREF(slice->stop, Py_None);
+ Py_SETREF(slice->step, Py_None);
}
- _Py_DECLARE_STR(shim_name, "<shim>");
- struct _PyCodeConstructor con = {
- .filename = &_Py_STR(shim_name),
- .name = name,
- .qualname = name,
- .flags = CO_NEWLOCALS | CO_OPTIMIZED,
-
- .code = co_code,
- .firstlineno = 1,
- .linetable = lines,
-
- .consts = (PyObject *)&_Py_SINGLETON(tuple_empty),
- .names = (PyObject *)&_Py_SINGLETON(tuple_empty),
-
- .localsplusnames = (PyObject *)&_Py_SINGLETON(tuple_empty),
- .localspluskinds = (PyObject *)&_Py_SINGLETON(bytes_empty),
-
- .argcount = 0,
- .posonlyargcount = 0,
- .kwonlyargcount = 0,
+ else if (PyFrozenSet_CheckExact(op)) {
+ _PySet_ClearInternal((PySetObject *)op);
+ }
+ return 0;
+}
- .stacksize = codedef->stacksize,
+static void
+destroy_key(void *key)
+{
+ _Py_ClearImmortal(key);
+}
+#endif
- .exceptiontable = (PyObject *)&_Py_SINGLETON(bytes_empty),
- };
+PyStatus
+_PyCode_Init(PyInterpreterState *interp)
+{
+#ifdef Py_GIL_DISABLED
+ struct _py_code_state *state = &interp->code_state;
+ state->constants = _Py_hashtable_new_full(&hash_const, &compare_constants,
+ &destroy_key, NULL, NULL);
+ if (state->constants == NULL) {
+ return _PyStatus_NO_MEMORY();
+ }
+#endif
+ return _PyStatus_OK();
+}
- codeobj = _PyCode_New(&con);
-cleanup:
- Py_XDECREF(name);
- Py_XDECREF(co_code);
- Py_XDECREF(lines);
- return codeobj;
+void
+_PyCode_Fini(PyInterpreterState *interp)
+{
+#ifdef Py_GIL_DISABLED
+ // Free interned constants
+ struct _py_code_state *state = &interp->code_state;
+ if (state->constants) {
+ _Py_hashtable_foreach(state->constants, &clear_containers, NULL);
+ _Py_hashtable_destroy(state->constants);
+ state->constants = NULL;
+ }
+#endif
}
diff --git a/contrib/tools/python3/Objects/complexobject.c b/contrib/tools/python3/Objects/complexobject.c
index eff17380298..502b4a9a961 100644
--- a/contrib/tools/python3/Objects/complexobject.c
+++ b/contrib/tools/python3/Objects/complexobject.c
@@ -7,10 +7,11 @@
#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
+#include "pycore_complexobject.h" // _PyComplex_FormatAdvancedWriter()
#include "pycore_long.h" // _PyLong_GetZero()
#include "pycore_object.h" // _PyObject_Init()
#include "pycore_pymath.h" // _Py_ADJUST_ERANGE2()
-#include "structmember.h" // PyMemberDef
+
/*[clinic input]
@@ -255,26 +256,51 @@ PyComplex_FromDoubles(double real, double imag)
return PyComplex_FromCComplex(c);
}
+static PyObject * try_complex_special_method(PyObject *);
+
double
PyComplex_RealAsDouble(PyObject *op)
{
+ double real = -1.0;
+
if (PyComplex_Check(op)) {
- return ((PyComplexObject *)op)->cval.real;
+ real = ((PyComplexObject *)op)->cval.real;
}
else {
- return PyFloat_AsDouble(op);
+ PyObject* newop = try_complex_special_method(op);
+ if (newop) {
+ real = ((PyComplexObject *)newop)->cval.real;
+ Py_DECREF(newop);
+ } else if (!PyErr_Occurred()) {
+ real = PyFloat_AsDouble(op);
+ }
}
+
+ return real;
}
double
PyComplex_ImagAsDouble(PyObject *op)
{
+ double imag = -1.0;
+
if (PyComplex_Check(op)) {
- return ((PyComplexObject *)op)->cval.imag;
+ imag = ((PyComplexObject *)op)->cval.imag;
}
else {
- return 0.0;
+ PyObject* newop = try_complex_special_method(op);
+ if (newop) {
+ imag = ((PyComplexObject *)newop)->cval.imag;
+ Py_DECREF(newop);
+ } else if (!PyErr_Occurred()) {
+ PyFloat_AsDouble(op);
+ if (!PyErr_Occurred()) {
+ imag = 0.0;
+ }
+ }
}
+
+ return imag;
}
static PyObject *
@@ -719,9 +745,9 @@ static PyMethodDef complex_methods[] = {
};
static PyMemberDef complex_members[] = {
- {"real", T_DOUBLE, offsetof(PyComplexObject, cval.real), READONLY,
+ {"real", Py_T_DOUBLE, offsetof(PyComplexObject, cval.real), Py_READONLY,
"the real part of a complex number"},
- {"imag", T_DOUBLE, offsetof(PyComplexObject, cval.imag), READONLY,
+ {"imag", Py_T_DOUBLE, offsetof(PyComplexObject, cval.imag), Py_READONLY,
"the imaginary part of a complex number"},
{0},
};
diff --git a/contrib/tools/python3/Objects/descrobject.c b/contrib/tools/python3/Objects/descrobject.c
index 7a3a1f06406..0560d066851 100644
--- a/contrib/tools/python3/Objects/descrobject.c
+++ b/contrib/tools/python3/Objects/descrobject.c
@@ -1,12 +1,16 @@
/* Descriptors -- a new, flexible way to describe attributes */
#include "Python.h"
+#include "pycore_abstract.h" // _PyObject_RealIsSubclass()
+#include "pycore_call.h" // _PyStack_AsDict()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
+#include "pycore_emscripten_trampoline.h" // descr_set_trampoline_call(), descr_get_trampoline_call()
+#include "pycore_descrobject.h" // _PyMethodWrapper_Type
+#include "pycore_modsupport.h" // _PyArg_UnpackStack()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_tuple.h" // _PyTuple_ITEMS()
-#include "structmember.h" // PyMemberDef
-#include "pycore_descrobject.h"
+
/*[clinic input]
class mappingproxy "mappingproxyobject *" "&PyDictProxy_Type"
@@ -14,28 +18,10 @@ class property "propertyobject *" "&PyProperty_Type"
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=556352653fd4c02e]*/
-// see pycore_object.h
-#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
-#error #include <emscripten.h>
-EM_JS(int, descr_set_trampoline_call, (setter set, PyObject *obj, PyObject *value, void *closure), {
- return wasmTable.get(set)(obj, value, closure);
-});
-
-EM_JS(PyObject*, descr_get_trampoline_call, (getter get, PyObject *obj, void *closure), {
- return wasmTable.get(get)(obj, closure);
-});
-#else
-#define descr_set_trampoline_call(set, obj, value, closure) \
- (set)((obj), (value), (closure))
-
-#define descr_get_trampoline_call(get, obj, closure) \
- (get)((obj), (closure))
-
-#endif // __EMSCRIPTEN__ && PY_CALL_TRAMPOLINE
-
static void
-descr_dealloc(PyDescrObject *descr)
+descr_dealloc(PyObject *self)
{
+ PyDescrObject *descr = (PyDescrObject *)self;
_PyObject_GC_UNTRACK(descr);
Py_XDECREF(descr->d_type);
Py_XDECREF(descr->d_name);
@@ -62,28 +48,28 @@ descr_repr(PyDescrObject *descr, const char *format)
}
static PyObject *
-method_repr(PyMethodDescrObject *descr)
+method_repr(PyObject *descr)
{
return descr_repr((PyDescrObject *)descr,
"<method '%V' of '%s' objects>");
}
static PyObject *
-member_repr(PyMemberDescrObject *descr)
+member_repr(PyObject *descr)
{
return descr_repr((PyDescrObject *)descr,
"<member '%V' of '%s' objects>");
}
static PyObject *
-getset_repr(PyGetSetDescrObject *descr)
+getset_repr(PyObject *descr)
{
return descr_repr((PyDescrObject *)descr,
"<attribute '%V' of '%s' objects>");
}
static PyObject *
-wrapperdescr_repr(PyWrapperDescrObject *descr)
+wrapperdescr_repr(PyObject *descr)
{
return descr_repr((PyDescrObject *)descr,
"<slot wrapper '%V' of '%s' objects>");
@@ -105,8 +91,9 @@ descr_check(PyDescrObject *descr, PyObject *obj)
}
static PyObject *
-classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
+classmethod_get(PyObject *self, PyObject *obj, PyObject *type)
{
+ PyMethodDescrObject *descr = (PyMethodDescrObject *)self;
/* Ensure a valid type. Class methods ignore obj. */
if (type == NULL) {
if (obj != NULL)
@@ -147,8 +134,9 @@ classmethod_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
}
static PyObject *
-method_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
+method_get(PyObject *self, PyObject *obj, PyObject *type)
{
+ PyMethodDescrObject *descr = (PyMethodDescrObject *)self;
if (obj == NULL) {
return Py_NewRef(descr);
}
@@ -156,12 +144,12 @@ method_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
return NULL;
}
if (descr->d_method->ml_flags & METH_METHOD) {
- if (PyType_Check(type)) {
+ if (type == NULL || PyType_Check(type)) {
return PyCMethod_New(descr->d_method, obj, NULL, descr->d_common.d_type);
} else {
PyErr_Format(PyExc_TypeError,
"descriptor '%V' needs a type, not '%s', as arg 2",
- descr_name((PyDescrObject *)descr),
+ descr_name((PyDescrObject *)descr), "?",
Py_TYPE(type)->tp_name);
return NULL;
}
@@ -171,8 +159,9 @@ method_get(PyMethodDescrObject *descr, PyObject *obj, PyObject *type)
}
static PyObject *
-member_get(PyMemberDescrObject *descr, PyObject *obj, PyObject *type)
+member_get(PyObject *self, PyObject *obj, PyObject *type)
{
+ PyMemberDescrObject *descr = (PyMemberDescrObject *)self;
if (obj == NULL) {
return Py_NewRef(descr);
}
@@ -180,7 +169,7 @@ member_get(PyMemberDescrObject *descr, PyObject *obj, PyObject *type)
return NULL;
}
- if (descr->d_member->flags & PY_AUDIT_READ) {
+ if (descr->d_member->flags & Py_AUDIT_READ) {
if (PySys_Audit("object.__getattr__", "Os",
obj ? obj : Py_None, descr->d_member->name) < 0) {
return NULL;
@@ -191,8 +180,9 @@ member_get(PyMemberDescrObject *descr, PyObject *obj, PyObject *type)
}
static PyObject *
-getset_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type)
+getset_get(PyObject *self, PyObject *obj, PyObject *type)
{
+ PyGetSetDescrObject *descr = (PyGetSetDescrObject *)self;
if (obj == NULL) {
return Py_NewRef(descr);
}
@@ -210,8 +200,9 @@ getset_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type)
}
static PyObject *
-wrapperdescr_get(PyWrapperDescrObject *descr, PyObject *obj, PyObject *type)
+wrapperdescr_get(PyObject *self, PyObject *obj, PyObject *type)
{
+ PyWrapperDescrObject *descr = (PyWrapperDescrObject *)self;
if (obj == NULL) {
return Py_NewRef(descr);
}
@@ -238,8 +229,9 @@ descr_setcheck(PyDescrObject *descr, PyObject *obj, PyObject *value)
}
static int
-member_set(PyMemberDescrObject *descr, PyObject *obj, PyObject *value)
+member_set(PyObject *self, PyObject *obj, PyObject *value)
{
+ PyMemberDescrObject *descr = (PyMemberDescrObject *)self;
if (descr_setcheck((PyDescrObject *)descr, obj, value) < 0) {
return -1;
}
@@ -247,8 +239,9 @@ member_set(PyMemberDescrObject *descr, PyObject *obj, PyObject *value)
}
static int
-getset_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value)
+getset_set(PyObject *self, PyObject *obj, PyObject *value)
{
+ PyGetSetDescrObject *descr = (PyGetSetDescrObject *)self;
if (descr_setcheck((PyDescrObject *)descr, obj, value) < 0) {
return -1;
}
@@ -400,7 +393,7 @@ method_vectorcall_FASTCALL(
if (method_check_args(func, args, nargs, kwnames)) {
return NULL;
}
- _PyCFunctionFast meth = (_PyCFunctionFast)
+ PyCFunctionFast meth = (PyCFunctionFast)
method_enter_call(tstate, func);
if (meth == NULL) {
return NULL;
@@ -419,7 +412,7 @@ method_vectorcall_FASTCALL_KEYWORDS(
if (method_check_args(func, args, nargs, NULL)) {
return NULL;
}
- _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)
+ PyCFunctionFastWithKeywords meth = (PyCFunctionFastWithKeywords)
method_enter_call(tstate, func);
if (meth == NULL) {
return NULL;
@@ -494,9 +487,10 @@ method_vectorcall_O(
we implement this simply by calling __get__ and then calling the result.
*/
static PyObject *
-classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args,
+classmethoddescr_call(PyObject *_descr, PyObject *args,
PyObject *kwds)
{
+ PyMethodDescrObject *descr = (PyMethodDescrObject *)_descr;
Py_ssize_t argc = PyTuple_GET_SIZE(args);
if (argc < 1) {
PyErr_Format(PyExc_TypeError,
@@ -507,7 +501,7 @@ classmethoddescr_call(PyMethodDescrObject *descr, PyObject *args,
return NULL;
}
PyObject *self = PyTuple_GET_ITEM(args, 0);
- PyObject *bound = classmethod_get(descr, NULL, self);
+ PyObject *bound = classmethod_get((PyObject *)descr, NULL, self);
if (bound == NULL) {
return NULL;
}
@@ -538,8 +532,9 @@ wrapperdescr_raw_call(PyWrapperDescrObject *descr, PyObject *self,
}
static PyObject *
-wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds)
+wrapperdescr_call(PyObject *_descr, PyObject *args, PyObject *kwds)
{
+ PyWrapperDescrObject *descr = (PyWrapperDescrObject *)_descr;
Py_ssize_t argc;
PyObject *self, *result;
@@ -578,15 +573,19 @@ wrapperdescr_call(PyWrapperDescrObject *descr, PyObject *args, PyObject *kwds)
static PyObject *
-method_get_doc(PyMethodDescrObject *descr, void *closure)
+method_get_doc(PyObject *_descr, void *closure)
{
+ PyMethodDescrObject *descr = (PyMethodDescrObject *)_descr;
return _PyType_GetDocFromInternalDoc(descr->d_method->ml_name, descr->d_method->ml_doc);
}
static PyObject *
-method_get_text_signature(PyMethodDescrObject *descr, void *closure)
+method_get_text_signature(PyObject *_descr, void *closure)
{
- return _PyType_GetTextSignatureFromInternalDoc(descr->d_method->ml_name, descr->d_method->ml_doc);
+ PyMethodDescrObject *descr = (PyMethodDescrObject *)_descr;
+ return _PyType_GetTextSignatureFromInternalDoc(descr->d_method->ml_name,
+ descr->d_method->ml_doc,
+ descr->d_method->ml_flags);
}
static PyObject *
@@ -618,41 +617,44 @@ calculate_qualname(PyDescrObject *descr)
}
static PyObject *
-descr_get_qualname(PyDescrObject *descr, void *Py_UNUSED(ignored))
+descr_get_qualname(PyObject *self, void *Py_UNUSED(ignored))
{
+ PyDescrObject *descr = (PyDescrObject *)self;
if (descr->d_qualname == NULL)
descr->d_qualname = calculate_qualname(descr);
return Py_XNewRef(descr->d_qualname);
}
static PyObject *
-descr_reduce(PyDescrObject *descr, PyObject *Py_UNUSED(ignored))
+descr_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ PyDescrObject *descr = (PyDescrObject *)self;
return Py_BuildValue("N(OO)", _PyEval_GetBuiltin(&_Py_ID(getattr)),
PyDescr_TYPE(descr), PyDescr_NAME(descr));
}
static PyMethodDef descr_methods[] = {
- {"__reduce__", (PyCFunction)descr_reduce, METH_NOARGS, NULL},
+ {"__reduce__", descr_reduce, METH_NOARGS, NULL},
{NULL, NULL}
};
static PyMemberDef descr_members[] = {
- {"__objclass__", T_OBJECT, offsetof(PyDescrObject, d_type), READONLY},
- {"__name__", T_OBJECT, offsetof(PyDescrObject, d_name), READONLY},
+ {"__objclass__", _Py_T_OBJECT, offsetof(PyDescrObject, d_type), Py_READONLY},
+ {"__name__", _Py_T_OBJECT, offsetof(PyDescrObject, d_name), Py_READONLY},
{0}
};
static PyGetSetDef method_getset[] = {
- {"__doc__", (getter)method_get_doc},
- {"__qualname__", (getter)descr_get_qualname},
- {"__text_signature__", (getter)method_get_text_signature},
+ {"__doc__", method_get_doc},
+ {"__qualname__", descr_get_qualname},
+ {"__text_signature__", method_get_text_signature},
{0}
};
static PyObject *
-member_get_doc(PyMemberDescrObject *descr, void *closure)
+member_get_doc(PyObject *_descr, void *closure)
{
+ PyMemberDescrObject *descr = (PyMemberDescrObject *)_descr;
if (descr->d_member->doc == NULL) {
Py_RETURN_NONE;
}
@@ -660,14 +662,15 @@ member_get_doc(PyMemberDescrObject *descr, void *closure)
}
static PyGetSetDef member_getset[] = {
- {"__doc__", (getter)member_get_doc},
- {"__qualname__", (getter)descr_get_qualname},
+ {"__doc__", member_get_doc},
+ {"__qualname__", descr_get_qualname},
{0}
};
static PyObject *
-getset_get_doc(PyGetSetDescrObject *descr, void *closure)
+getset_get_doc(PyObject *self, void *closure)
{
+ PyGetSetDescrObject *descr = (PyGetSetDescrObject *)self;
if (descr->d_getset->doc == NULL) {
Py_RETURN_NONE;
}
@@ -675,27 +678,30 @@ getset_get_doc(PyGetSetDescrObject *descr, void *closure)
}
static PyGetSetDef getset_getset[] = {
- {"__doc__", (getter)getset_get_doc},
- {"__qualname__", (getter)descr_get_qualname},
+ {"__doc__", getset_get_doc},
+ {"__qualname__", descr_get_qualname},
{0}
};
static PyObject *
-wrapperdescr_get_doc(PyWrapperDescrObject *descr, void *closure)
+wrapperdescr_get_doc(PyObject *self, void *closure)
{
+ PyWrapperDescrObject *descr = (PyWrapperDescrObject *)self;
return _PyType_GetDocFromInternalDoc(descr->d_base->name, descr->d_base->doc);
}
static PyObject *
-wrapperdescr_get_text_signature(PyWrapperDescrObject *descr, void *closure)
+wrapperdescr_get_text_signature(PyObject *self, void *closure)
{
- return _PyType_GetTextSignatureFromInternalDoc(descr->d_base->name, descr->d_base->doc);
+ PyWrapperDescrObject *descr = (PyWrapperDescrObject *)self;
+ return _PyType_GetTextSignatureFromInternalDoc(descr->d_base->name,
+ descr->d_base->doc, 0);
}
static PyGetSetDef wrapperdescr_getset[] = {
- {"__doc__", (getter)wrapperdescr_get_doc},
- {"__qualname__", (getter)descr_get_qualname},
- {"__text_signature__", (getter)wrapperdescr_get_text_signature},
+ {"__doc__", wrapperdescr_get_doc},
+ {"__qualname__", descr_get_qualname},
+ {"__text_signature__", wrapperdescr_get_text_signature},
{0}
};
@@ -712,12 +718,12 @@ PyTypeObject PyMethodDescr_Type = {
"method_descriptor",
sizeof(PyMethodDescrObject),
0,
- (destructor)descr_dealloc, /* tp_dealloc */
+ descr_dealloc, /* tp_dealloc */
offsetof(PyMethodDescrObject, vectorcall), /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)method_repr, /* tp_repr */
+ method_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
@@ -742,7 +748,7 @@ PyTypeObject PyMethodDescr_Type = {
method_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
- (descrgetfunc)method_get, /* tp_descr_get */
+ method_get, /* tp_descr_get */
0, /* tp_descr_set */
};
@@ -752,17 +758,17 @@ PyTypeObject PyClassMethodDescr_Type = {
"classmethod_descriptor",
sizeof(PyMethodDescrObject),
0,
- (destructor)descr_dealloc, /* tp_dealloc */
+ descr_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)method_repr, /* tp_repr */
+ method_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)classmethoddescr_call, /* tp_call */
+ classmethoddescr_call, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -780,7 +786,7 @@ PyTypeObject PyClassMethodDescr_Type = {
method_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
- (descrgetfunc)classmethod_get, /* tp_descr_get */
+ classmethod_get, /* tp_descr_get */
0, /* tp_descr_set */
};
@@ -789,12 +795,12 @@ PyTypeObject PyMemberDescr_Type = {
"member_descriptor",
sizeof(PyMemberDescrObject),
0,
- (destructor)descr_dealloc, /* tp_dealloc */
+ descr_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)member_repr, /* tp_repr */
+ member_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
@@ -817,8 +823,8 @@ PyTypeObject PyMemberDescr_Type = {
member_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
- (descrgetfunc)member_get, /* tp_descr_get */
- (descrsetfunc)member_set, /* tp_descr_set */
+ member_get, /* tp_descr_get */
+ member_set, /* tp_descr_set */
};
PyTypeObject PyGetSetDescr_Type = {
@@ -826,12 +832,12 @@ PyTypeObject PyGetSetDescr_Type = {
"getset_descriptor",
sizeof(PyGetSetDescrObject),
0,
- (destructor)descr_dealloc, /* tp_dealloc */
+ descr_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)getset_repr, /* tp_repr */
+ getset_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
@@ -854,8 +860,8 @@ PyTypeObject PyGetSetDescr_Type = {
getset_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
- (descrgetfunc)getset_get, /* tp_descr_get */
- (descrsetfunc)getset_set, /* tp_descr_set */
+ getset_get, /* tp_descr_get */
+ getset_set, /* tp_descr_set */
};
PyTypeObject PyWrapperDescr_Type = {
@@ -863,17 +869,17 @@ PyTypeObject PyWrapperDescr_Type = {
"wrapper_descriptor",
sizeof(PyWrapperDescrObject),
0,
- (destructor)descr_dealloc, /* tp_dealloc */
+ descr_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)wrapperdescr_repr, /* tp_repr */
+ wrapperdescr_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)wrapperdescr_call, /* tp_call */
+ wrapperdescr_call, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -892,7 +898,7 @@ PyTypeObject PyWrapperDescr_Type = {
wrapperdescr_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
- (descrgetfunc)wrapperdescr_get, /* tp_descr_get */
+ wrapperdescr_get, /* tp_descr_get */
0, /* tp_descr_set */
};
@@ -903,6 +909,7 @@ descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name)
descr = (PyDescrObject *)PyType_GenericAlloc(descrtype, 0);
if (descr != NULL) {
+ _PyObject_SetDeferredRefcount((PyObject *)descr);
descr->d_type = (PyTypeObject*)Py_XNewRef(type);
descr->d_name = PyUnicode_InternFromString(name);
if (descr->d_name == NULL) {
@@ -1034,20 +1041,22 @@ typedef struct {
} mappingproxyobject;
static Py_ssize_t
-mappingproxy_len(mappingproxyobject *pp)
+mappingproxy_len(PyObject *self)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_Size(pp->mapping);
}
static PyObject *
-mappingproxy_getitem(mappingproxyobject *pp, PyObject *key)
+mappingproxy_getitem(PyObject *self, PyObject *key)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_GetItem(pp->mapping, key);
}
static PyMappingMethods mappingproxy_as_mapping = {
- (lenfunc)mappingproxy_len, /* mp_length */
- (binaryfunc)mappingproxy_getitem, /* mp_subscript */
+ mappingproxy_len, /* mp_length */
+ mappingproxy_getitem, /* mp_subscript */
0, /* mp_ass_subscript */
};
@@ -1076,8 +1085,9 @@ static PyNumberMethods mappingproxy_as_number = {
};
static int
-mappingproxy_contains(mappingproxyobject *pp, PyObject *key)
+mappingproxy_contains(PyObject *self, PyObject *key)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
if (PyDict_CheckExact(pp->mapping))
return PyDict_Contains(pp->mapping, key);
else
@@ -1092,14 +1102,15 @@ static PySequenceMethods mappingproxy_as_sequence = {
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
- (objobjproc)mappingproxy_contains, /* sq_contains */
+ mappingproxy_contains, /* sq_contains */
0, /* sq_inplace_concat */
0, /* sq_inplace_repeat */
};
static PyObject *
-mappingproxy_get(mappingproxyobject *pp, PyObject *const *args, Py_ssize_t nargs)
+mappingproxy_get(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
/* newargs: mapping, key, default=None */
PyObject *newargs[3];
newargs[0] = pp->mapping;
@@ -1110,38 +1121,43 @@ mappingproxy_get(mappingproxyobject *pp, PyObject *const *args, Py_ssize_t nargs
{
return NULL;
}
- return _PyObject_VectorcallMethod(&_Py_ID(get), newargs,
- 3 | PY_VECTORCALL_ARGUMENTS_OFFSET,
- NULL);
+ return PyObject_VectorcallMethod(&_Py_ID(get), newargs,
+ 3 | PY_VECTORCALL_ARGUMENTS_OFFSET,
+ NULL);
}
static PyObject *
-mappingproxy_keys(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored))
+mappingproxy_keys(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_CallMethodNoArgs(pp->mapping, &_Py_ID(keys));
}
static PyObject *
-mappingproxy_values(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored))
+mappingproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_CallMethodNoArgs(pp->mapping, &_Py_ID(values));
}
static PyObject *
-mappingproxy_items(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored))
+mappingproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_CallMethodNoArgs(pp->mapping, &_Py_ID(items));
}
static PyObject *
-mappingproxy_copy(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored))
+mappingproxy_copy(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_CallMethodNoArgs(pp->mapping, &_Py_ID(copy));
}
static PyObject *
-mappingproxy_reversed(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored))
+mappingproxy_reversed(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_CallMethodNoArgs(pp->mapping, &_Py_ID(__reversed__));
}
@@ -1150,52 +1166,57 @@ mappingproxy_reversed(mappingproxyobject *pp, PyObject *Py_UNUSED(ignored))
static PyMethodDef mappingproxy_methods[] = {
{"get", _PyCFunction_CAST(mappingproxy_get), METH_FASTCALL,
- PyDoc_STR("D.get(k[,d]) -> D[k] if k in D, else d."
- " d defaults to None.")},
- {"keys", (PyCFunction)mappingproxy_keys, METH_NOARGS,
+ PyDoc_STR("get($self, key, default=None, /)\n--\n\n"
+ "Return the value for key if key is in the mapping, else default.")},
+ {"keys", mappingproxy_keys, METH_NOARGS,
PyDoc_STR("D.keys() -> a set-like object providing a view on D's keys")},
- {"values", (PyCFunction)mappingproxy_values, METH_NOARGS,
+ {"values", mappingproxy_values, METH_NOARGS,
PyDoc_STR("D.values() -> an object providing a view on D's values")},
- {"items", (PyCFunction)mappingproxy_items, METH_NOARGS,
+ {"items", mappingproxy_items, METH_NOARGS,
PyDoc_STR("D.items() -> a set-like object providing a view on D's items")},
- {"copy", (PyCFunction)mappingproxy_copy, METH_NOARGS,
+ {"copy", mappingproxy_copy, METH_NOARGS,
PyDoc_STR("D.copy() -> a shallow copy of D")},
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS,
PyDoc_STR("See PEP 585")},
- {"__reversed__", (PyCFunction)mappingproxy_reversed, METH_NOARGS,
+ {"__reversed__", mappingproxy_reversed, METH_NOARGS,
PyDoc_STR("D.__reversed__() -> reverse iterator")},
{0}
};
static void
-mappingproxy_dealloc(mappingproxyobject *pp)
+mappingproxy_dealloc(PyObject *self)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
_PyObject_GC_UNTRACK(pp);
Py_DECREF(pp->mapping);
PyObject_GC_Del(pp);
}
static PyObject *
-mappingproxy_getiter(mappingproxyobject *pp)
+mappingproxy_getiter(PyObject *self)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_GetIter(pp->mapping);
}
static Py_hash_t
-mappingproxy_hash(mappingproxyobject *pp)
+mappingproxy_hash(PyObject *self)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_Hash(pp->mapping);
}
static PyObject *
-mappingproxy_str(mappingproxyobject *pp)
+mappingproxy_str(PyObject *self)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyObject_Str(pp->mapping);
}
static PyObject *
-mappingproxy_repr(mappingproxyobject *pp)
+mappingproxy_repr(PyObject *self)
{
+ mappingproxyobject *pp = (mappingproxyobject *)self;
return PyUnicode_FromFormat("mappingproxy(%R)", pp->mapping);
}
@@ -1208,8 +1229,9 @@ mappingproxy_traverse(PyObject *self, visitproc visit, void *arg)
}
static PyObject *
-mappingproxy_richcompare(mappingproxyobject *v, PyObject *w, int op)
+mappingproxy_richcompare(PyObject *self, PyObject *w, int op)
{
+ mappingproxyobject *v = (mappingproxyobject *)self;
return PyObject_RichCompare(v->mapping, w, op);
}
@@ -1233,11 +1255,12 @@ mappingproxy.__new__ as mappingproxy_new
mapping: object
+Read-only proxy of a mapping.
[clinic start generated code]*/
static PyObject *
mappingproxy_new_impl(PyTypeObject *type, PyObject *mapping)
-/*[clinic end generated code: output=65f27f02d5b68fa7 input=d2d620d4f598d4f8]*/
+/*[clinic end generated code: output=65f27f02d5b68fa7 input=c156df096ef7590c]*/
{
mappingproxyobject *mappingproxy;
@@ -1283,8 +1306,9 @@ typedef struct {
#define Wrapper_Check(v) Py_IS_TYPE(v, &_PyMethodWrapper_Type)
static void
-wrapper_dealloc(wrapperobject *wp)
+wrapper_dealloc(PyObject *self)
{
+ wrapperobject *wp = (wrapperobject *)self;
PyObject_GC_UnTrack(wp);
Py_TRASHCAN_BEGIN(wp, wrapper_dealloc)
Py_XDECREF(wp->descr);
@@ -1320,10 +1344,11 @@ wrapper_richcompare(PyObject *a, PyObject *b, int op)
}
static Py_hash_t
-wrapper_hash(wrapperobject *wp)
+wrapper_hash(PyObject *self)
{
+ wrapperobject *wp = (wrapperobject *)self;
Py_hash_t x, y;
- x = _Py_HashPointer(wp->self);
+ x = PyObject_GenericHash(wp->self);
y = _Py_HashPointer(wp->descr);
x = x ^ y;
if (x == -1)
@@ -1332,8 +1357,9 @@ wrapper_hash(wrapperobject *wp)
}
static PyObject *
-wrapper_repr(wrapperobject *wp)
+wrapper_repr(PyObject *self)
{
+ wrapperobject *wp = (wrapperobject *)self;
return PyUnicode_FromFormat("<method-wrapper '%s' of %s object at %p>",
wp->descr->d_base->name,
Py_TYPE(wp->self)->tp_name,
@@ -1341,68 +1367,74 @@ wrapper_repr(wrapperobject *wp)
}
static PyObject *
-wrapper_reduce(wrapperobject *wp, PyObject *Py_UNUSED(ignored))
+wrapper_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ wrapperobject *wp = (wrapperobject *)self;
return Py_BuildValue("N(OO)", _PyEval_GetBuiltin(&_Py_ID(getattr)),
wp->self, PyDescr_NAME(wp->descr));
}
static PyMethodDef wrapper_methods[] = {
- {"__reduce__", (PyCFunction)wrapper_reduce, METH_NOARGS, NULL},
+ {"__reduce__", wrapper_reduce, METH_NOARGS, NULL},
{NULL, NULL}
};
static PyMemberDef wrapper_members[] = {
- {"__self__", T_OBJECT, offsetof(wrapperobject, self), READONLY},
+ {"__self__", _Py_T_OBJECT, offsetof(wrapperobject, self), Py_READONLY},
{0}
};
static PyObject *
-wrapper_objclass(wrapperobject *wp, void *Py_UNUSED(ignored))
+wrapper_objclass(PyObject *wp, void *Py_UNUSED(ignored))
{
- PyObject *c = (PyObject *)PyDescr_TYPE(wp->descr);
+ PyObject *c = (PyObject *)PyDescr_TYPE(((wrapperobject *)wp)->descr);
return Py_NewRef(c);
}
static PyObject *
-wrapper_name(wrapperobject *wp, void *Py_UNUSED(ignored))
+wrapper_name(PyObject *wp, void *Py_UNUSED(ignored))
{
- const char *s = wp->descr->d_base->name;
+ const char *s = ((wrapperobject *)wp)->descr->d_base->name;
return PyUnicode_FromString(s);
}
static PyObject *
-wrapper_doc(wrapperobject *wp, void *Py_UNUSED(ignored))
+wrapper_doc(PyObject *self, void *Py_UNUSED(ignored))
{
+ wrapperobject *wp = (wrapperobject *)self;
return _PyType_GetDocFromInternalDoc(wp->descr->d_base->name, wp->descr->d_base->doc);
}
static PyObject *
-wrapper_text_signature(wrapperobject *wp, void *Py_UNUSED(ignored))
+wrapper_text_signature(PyObject *self, void *Py_UNUSED(ignored))
{
- return _PyType_GetTextSignatureFromInternalDoc(wp->descr->d_base->name, wp->descr->d_base->doc);
+ wrapperobject *wp = (wrapperobject *)self;
+ return _PyType_GetTextSignatureFromInternalDoc(wp->descr->d_base->name,
+ wp->descr->d_base->doc, 0);
}
static PyObject *
-wrapper_qualname(wrapperobject *wp, void *Py_UNUSED(ignored))
+wrapper_qualname(PyObject *self, void *Py_UNUSED(ignored))
{
- return descr_get_qualname((PyDescrObject *)wp->descr, NULL);
+ wrapperobject *wp = (wrapperobject *)self;
+ return descr_get_qualname((PyObject *)wp->descr, NULL);
}
static PyGetSetDef wrapper_getsets[] = {
- {"__objclass__", (getter)wrapper_objclass},
- {"__name__", (getter)wrapper_name},
- {"__qualname__", (getter)wrapper_qualname},
- {"__doc__", (getter)wrapper_doc},
- {"__text_signature__", (getter)wrapper_text_signature},
+ {"__objclass__", wrapper_objclass},
+ {"__name__", wrapper_name},
+ {"__qualname__", wrapper_qualname},
+ {"__doc__", wrapper_doc},
+ {"__text_signature__", wrapper_text_signature},
{0}
};
static PyObject *
-wrapper_call(wrapperobject *wp, PyObject *args, PyObject *kwds)
+wrapper_call(PyObject *self, PyObject *args, PyObject *kwds)
{
+ wrapperobject *wp = (wrapperobject *)self;
return wrapperdescr_raw_call(wp->descr, wp->self, args, kwds);
}
@@ -1421,17 +1453,17 @@ PyTypeObject _PyMethodWrapper_Type = {
sizeof(wrapperobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)wrapper_dealloc, /* tp_dealloc */
+ wrapper_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)wrapper_repr, /* tp_repr */
+ wrapper_repr, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
- (hashfunc)wrapper_hash, /* tp_hash */
- (ternaryfunc)wrapper_call, /* tp_call */
+ wrapper_hash, /* tp_hash */
+ wrapper_call, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
@@ -1489,22 +1521,34 @@ class property(object):
self.__doc__ = doc
except AttributeError: # read-only or dict-less class
pass
+ self.__name = None
+
+ def __set_name__(self, owner, name):
+ self.__name = name
+
+ @property
+ def __name__(self):
+ return self.__name if self.__name is not None else self.fget.__name__
+
+ @__name__.setter
+ def __name__(self, value):
+ self.__name = value
def __get__(self, inst, type=None):
if inst is None:
return self
if self.__get is None:
- raise AttributeError, "property has no getter"
+ raise AttributeError("property has no getter")
return self.__get(inst)
def __set__(self, inst, value):
if self.__set is None:
- raise AttributeError, "property has no setter"
+ raise AttributeError("property has no setter")
return self.__set(inst, value)
def __delete__(self, inst):
if self.__del is None:
- raise AttributeError, "property has no deleter"
+ raise AttributeError("property has no deleter")
return self.__del(inst)
*/
@@ -1513,10 +1557,10 @@ static PyObject * property_copy(PyObject *, PyObject *, PyObject *,
PyObject *);
static PyMemberDef property_members[] = {
- {"fget", T_OBJECT, offsetof(propertyobject, prop_get), READONLY},
- {"fset", T_OBJECT, offsetof(propertyobject, prop_set), READONLY},
- {"fdel", T_OBJECT, offsetof(propertyobject, prop_del), READONLY},
- {"__doc__", T_OBJECT, offsetof(propertyobject, prop_doc), 0},
+ {"fget", _Py_T_OBJECT, offsetof(propertyobject, prop_get), Py_READONLY},
+ {"fset", _Py_T_OBJECT, offsetof(propertyobject, prop_set), Py_READONLY},
+ {"fdel", _Py_T_OBJECT, offsetof(propertyobject, prop_del), Py_READONLY},
+ {"__doc__", _Py_T_OBJECT, offsetof(propertyobject, prop_doc), 0},
{0}
};
@@ -1552,6 +1596,9 @@ property_deleter(PyObject *self, PyObject *deleter)
PyDoc_STRVAR(set_name_doc,
+ "__set_name__($self, owner, name, /)\n"
+ "--\n"
+ "\n"
"Method to set name of a property.");
static PyObject *
@@ -1559,7 +1606,7 @@ property_set_name(PyObject *self, PyObject *args) {
if (PyTuple_GET_SIZE(args) != 2) {
PyErr_Format(
PyExc_TypeError,
- "__set_name__() takes 2 positional arguments but %d were given",
+ "__set_name__() takes 2 positional arguments but %zd were given",
PyTuple_GET_SIZE(args));
return NULL;
}
@@ -1595,6 +1642,20 @@ property_dealloc(PyObject *self)
Py_TYPE(self)->tp_free(self);
}
+static int
+property_name(propertyobject *prop, PyObject **name)
+{
+ if (prop->prop_name != NULL) {
+ *name = Py_NewRef(prop->prop_name);
+ return 1;
+ }
+ if (prop->prop_get == NULL) {
+ *name = NULL;
+ return 0;
+ }
+ return PyObject_GetOptionalAttr(prop->prop_get, &_Py_ID(__name__), name);
+}
+
static PyObject *
property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
{
@@ -1604,11 +1665,15 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
propertyobject *gs = (propertyobject *)self;
if (gs->prop_get == NULL) {
+ PyObject *propname;
+ if (property_name(gs, &propname) < 0) {
+ return NULL;
+ }
PyObject *qualname = PyType_GetQualName(Py_TYPE(obj));
- if (gs->prop_name != NULL && qualname != NULL) {
+ if (propname != NULL && qualname != NULL) {
PyErr_Format(PyExc_AttributeError,
"property %R of %R object has no getter",
- gs->prop_name,
+ propname,
qualname);
}
else if (qualname != NULL) {
@@ -1619,6 +1684,7 @@ property_descr_get(PyObject *self, PyObject *obj, PyObject *type)
PyErr_SetString(PyExc_AttributeError,
"property has no getter");
}
+ Py_XDECREF(propname);
Py_XDECREF(qualname);
return NULL;
}
@@ -1640,16 +1706,20 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
}
if (func == NULL) {
+ PyObject *propname;
+ if (property_name(gs, &propname) < 0) {
+ return -1;
+ }
PyObject *qualname = NULL;
if (obj != NULL) {
qualname = PyType_GetQualName(Py_TYPE(obj));
}
- if (gs->prop_name != NULL && qualname != NULL) {
+ if (propname != NULL && qualname != NULL) {
PyErr_Format(PyExc_AttributeError,
value == NULL ?
"property %R of %R object has no deleter" :
"property %R of %R object has no setter",
- gs->prop_name,
+ propname,
qualname);
}
else if (qualname != NULL) {
@@ -1665,6 +1735,7 @@ property_descr_set(PyObject *self, PyObject *obj, PyObject *value)
"property has no deleter" :
"property has no setter");
}
+ Py_XDECREF(propname);
Py_XDECREF(qualname);
return -1;
}
@@ -1787,7 +1858,7 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
}
/* if no docstring given and the getter has one, use that one */
else if (fget != NULL) {
- int rc = _PyObject_LookupAttr(fget, &_Py_ID(__doc__), &prop_doc);
+ int rc = PyObject_GetOptionalAttr(fget, &_Py_ID(__doc__), &prop_doc);
if (rc < 0) {
return rc;
}
@@ -1840,6 +1911,28 @@ property_init_impl(propertyobject *self, PyObject *fget, PyObject *fset,
}
static PyObject *
+property_get__name__(propertyobject *prop, void *Py_UNUSED(ignored))
+{
+ PyObject *name;
+ if (property_name(prop, &name) < 0) {
+ return NULL;
+ }
+ if (name == NULL) {
+ PyErr_SetString(PyExc_AttributeError,
+ "'property' object has no attribute '__name__'");
+ }
+ return name;
+}
+
+static int
+property_set__name__(propertyobject *prop, PyObject *value,
+ void *Py_UNUSED(ignored))
+{
+ Py_XSETREF(prop->prop_name, Py_XNewRef(value));
+ return 0;
+}
+
+static PyObject *
property_get___isabstractmethod__(propertyobject *prop, void *closure)
{
int res = _PyObject_IsAbstract(prop->prop_get);
@@ -1869,6 +1962,7 @@ property_get___isabstractmethod__(propertyobject *prop, void *closure)
}
static PyGetSetDef property_getsetlist[] = {
+ {"__name__", (getter)property_get__name__, (setter)property_set__name__},
{"__isabstractmethod__",
(getter)property_get___isabstractmethod__, NULL,
NULL,
@@ -1904,29 +1998,29 @@ PyTypeObject PyDictProxy_Type = {
sizeof(mappingproxyobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)mappingproxy_dealloc, /* tp_dealloc */
+ mappingproxy_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)mappingproxy_repr, /* tp_repr */
+ mappingproxy_repr, /* tp_repr */
&mappingproxy_as_number, /* tp_as_number */
&mappingproxy_as_sequence, /* tp_as_sequence */
&mappingproxy_as_mapping, /* tp_as_mapping */
- (hashfunc)mappingproxy_hash, /* tp_hash */
+ mappingproxy_hash, /* tp_hash */
0, /* tp_call */
- (reprfunc)mappingproxy_str, /* tp_str */
+ mappingproxy_str, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_MAPPING, /* tp_flags */
- 0, /* tp_doc */
+ mappingproxy_new__doc__, /* tp_doc */
mappingproxy_traverse, /* tp_traverse */
0, /* tp_clear */
- (richcmpfunc)mappingproxy_richcompare, /* tp_richcompare */
+ mappingproxy_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)mappingproxy_getiter, /* tp_iter */
+ mappingproxy_getiter, /* tp_iter */
0, /* tp_iternext */
mappingproxy_methods, /* tp_methods */
0, /* tp_members */
@@ -1966,7 +2060,7 @@ PyTypeObject PyProperty_Type = {
Py_TPFLAGS_BASETYPE, /* tp_flags */
property_init__doc__, /* tp_doc */
property_traverse, /* tp_traverse */
- (inquiry)property_clear, /* tp_clear */
+ property_clear, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
diff --git a/contrib/tools/python3/Objects/dictobject.c b/contrib/tools/python3/Objects/dictobject.c
index 7337e290e89..843ece535be 100644
--- a/contrib/tools/python3/Objects/dictobject.c
+++ b/contrib/tools/python3/Objects/dictobject.c
@@ -21,6 +21,7 @@ layout:
| dk_log2_size |
| dk_log2_index_bytes |
| dk_kind |
+| dk_version |
| dk_usable |
| dk_nentries |
+---------------------+
@@ -55,7 +56,7 @@ The DictObject can be in one of two forms.
Either:
A combined table:
ma_values == NULL, dk_refcnt == 1.
- Values are stored in the me_value field of the PyDictKeysObject.
+ Values are stored in the me_value field of the PyDictKeyEntry.
Or:
A split table:
ma_values != NULL, dk_refcnt >= 1
@@ -80,6 +81,8 @@ DK_ENTRIES(keys)[index] if index >= 0):
Active upon key insertion. Dummy slots cannot be made Unused again
else the probe sequence in case of collision would have no way to know
they were once active.
+ In free-threaded builds dummy slots are not re-used to allow lock-free
+ lookups to proceed safely.
4. Pending. index >= 0, key != NULL, and value == NULL (split only)
Not yet inserted in split-table.
@@ -113,15 +116,20 @@ As a consequence of this, split keys have a maximum size of 16.
#define PyDict_MINSIZE 8
#include "Python.h"
-#include "pycore_bitutils.h" // _Py_bit_length
-#include "pycore_call.h" // _PyObject_CallNoArgs()
-#include "pycore_code.h" // stats
-#include "pycore_dict.h" // PyDictKeysObject
-#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
-#include "pycore_object.h" // _PyObject_GC_TRACK()
-#include "pycore_pyerrors.h" // _PyErr_GetRaisedException()
-#include "pycore_pystate.h" // _PyThreadState_GET()
-#include "stringlib/eq.h" // unicode_eq()
+#include "pycore_bitutils.h" // _Py_bit_length
+#include "pycore_call.h" // _PyObject_CallNoArgs()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
+#include "pycore_code.h" // stats
+#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION, Py_END_CRITICAL_SECTION
+#include "pycore_dict.h" // export _PyDict_SizeOf()
+#include "pycore_freelist.h" // _PyFreeListState_GET()
+#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
+#include "pycore_object.h" // _PyObject_GC_TRACK(), _PyDebugAllocatorStats()
+#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_RELAXED
+#include "pycore_pyerrors.h" // _PyErr_GetRaisedException()
+#include "pycore_pystate.h" // _PyThreadState_GET()
+#include "pycore_setobject.h" // _PySet_NextEntry()
+#include "stringlib/eq.h" // unicode_eq()
#include <stdbool.h>
@@ -138,6 +146,143 @@ To avoid slowing down lookups on a near-full table, we resize the table when
it's USABLE_FRACTION (currently two-thirds) full.
*/
+#ifdef Py_GIL_DISABLED
+
+static inline void
+ASSERT_DICT_LOCKED(PyObject *op)
+{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
+}
+#define ASSERT_DICT_LOCKED(op) ASSERT_DICT_LOCKED(_Py_CAST(PyObject*, op))
+#define ASSERT_WORLD_STOPPED_OR_DICT_LOCKED(op) \
+ if (!_PyInterpreterState_GET()->stoptheworld.world_stopped) { \
+ ASSERT_DICT_LOCKED(op); \
+ }
+#define ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(op) \
+ if (!_PyInterpreterState_GET()->stoptheworld.world_stopped) { \
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op); \
+ }
+
+#define IS_DICT_SHARED(mp) _PyObject_GC_IS_SHARED(mp)
+#define SET_DICT_SHARED(mp) _PyObject_GC_SET_SHARED(mp)
+#define LOAD_INDEX(keys, size, idx) _Py_atomic_load_int##size##_relaxed(&((const int##size##_t*)keys->dk_indices)[idx]);
+#define STORE_INDEX(keys, size, idx, value) _Py_atomic_store_int##size##_relaxed(&((int##size##_t*)keys->dk_indices)[idx], (int##size##_t)value);
+#define ASSERT_OWNED_OR_SHARED(mp) \
+ assert(_Py_IsOwnedByCurrentThread((PyObject *)mp) || IS_DICT_SHARED(mp));
+
+#define LOCK_KEYS_IF_SPLIT(keys, kind) \
+ if (kind == DICT_KEYS_SPLIT) { \
+ LOCK_KEYS(keys); \
+ }
+
+#define UNLOCK_KEYS_IF_SPLIT(keys, kind) \
+ if (kind == DICT_KEYS_SPLIT) { \
+ UNLOCK_KEYS(keys); \
+ }
+
+static inline Py_ssize_t
+load_keys_nentries(PyDictObject *mp)
+{
+ PyDictKeysObject *keys = _Py_atomic_load_ptr(&mp->ma_keys);
+ return _Py_atomic_load_ssize(&keys->dk_nentries);
+}
+
+static inline void
+set_keys(PyDictObject *mp, PyDictKeysObject *keys)
+{
+ ASSERT_OWNED_OR_SHARED(mp);
+ _Py_atomic_store_ptr_release(&mp->ma_keys, keys);
+}
+
+static inline void
+set_values(PyDictObject *mp, PyDictValues *values)
+{
+ ASSERT_OWNED_OR_SHARED(mp);
+ _Py_atomic_store_ptr_release(&mp->ma_values, values);
+}
+
+#define LOCK_KEYS(keys) PyMutex_LockFlags(&keys->dk_mutex, _Py_LOCK_DONT_DETACH)
+#define UNLOCK_KEYS(keys) PyMutex_Unlock(&keys->dk_mutex)
+
+#define ASSERT_KEYS_LOCKED(keys) assert(PyMutex_IsLocked(&keys->dk_mutex))
+#define LOAD_SHARED_KEY(key) _Py_atomic_load_ptr_acquire(&key)
+#define STORE_SHARED_KEY(key, value) _Py_atomic_store_ptr_release(&key, value)
+// Inc refs the keys object, giving the previous value
+#define INCREF_KEYS(dk) _Py_atomic_add_ssize(&dk->dk_refcnt, 1)
+// Dec refs the keys object, giving the previous value
+#define DECREF_KEYS(dk) _Py_atomic_add_ssize(&dk->dk_refcnt, -1)
+#define LOAD_KEYS_NENTRIES(keys) _Py_atomic_load_ssize_relaxed(&keys->dk_nentries)
+
+#define INCREF_KEYS_FT(dk) dictkeys_incref(dk)
+#define DECREF_KEYS_FT(dk, shared) dictkeys_decref(_PyInterpreterState_GET(), dk, shared)
+
+static inline void split_keys_entry_added(PyDictKeysObject *keys)
+{
+ ASSERT_KEYS_LOCKED(keys);
+
+ // We increase before we decrease so we never get too small of a value
+ // when we're racing with reads
+ _Py_atomic_store_ssize_relaxed(&keys->dk_nentries, keys->dk_nentries + 1);
+ _Py_atomic_store_ssize_release(&keys->dk_usable, keys->dk_usable - 1);
+}
+
+#else /* Py_GIL_DISABLED */
+
+#define ASSERT_DICT_LOCKED(op)
+#define ASSERT_WORLD_STOPPED_OR_DICT_LOCKED(op)
+#define ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(op)
+#define LOCK_KEYS(keys)
+#define UNLOCK_KEYS(keys)
+#define ASSERT_KEYS_LOCKED(keys)
+#define LOAD_SHARED_KEY(key) key
+#define STORE_SHARED_KEY(key, value) key = value
+#define INCREF_KEYS(dk) dk->dk_refcnt++
+#define DECREF_KEYS(dk) dk->dk_refcnt--
+#define LOAD_KEYS_NENTRIES(keys) keys->dk_nentries
+#define INCREF_KEYS_FT(dk)
+#define DECREF_KEYS_FT(dk, shared)
+#define LOCK_KEYS_IF_SPLIT(keys, kind)
+#define UNLOCK_KEYS_IF_SPLIT(keys, kind)
+#define IS_DICT_SHARED(mp) (false)
+#define SET_DICT_SHARED(mp)
+#define LOAD_INDEX(keys, size, idx) ((const int##size##_t*)(keys->dk_indices))[idx]
+#define STORE_INDEX(keys, size, idx, value) ((int##size##_t*)(keys->dk_indices))[idx] = (int##size##_t)value
+
+static inline void split_keys_entry_added(PyDictKeysObject *keys)
+{
+ keys->dk_usable--;
+ keys->dk_nentries++;
+}
+
+static inline void
+set_keys(PyDictObject *mp, PyDictKeysObject *keys)
+{
+ mp->ma_keys = keys;
+}
+
+static inline void
+set_values(PyDictObject *mp, PyDictValues *values)
+{
+ mp->ma_values = values;
+}
+
+static inline Py_ssize_t
+load_keys_nentries(PyDictObject *mp)
+{
+ return mp->ma_keys->dk_nentries;
+}
+
+
+#endif
+
+#define STORE_KEY(ep, key) FT_ATOMIC_STORE_PTR_RELEASE((ep)->me_key, key)
+#define STORE_VALUE(ep, value) FT_ATOMIC_STORE_PTR_RELEASE((ep)->me_value, value)
+#define STORE_SPLIT_VALUE(mp, idx, value) FT_ATOMIC_STORE_PTR_RELEASE(mp->ma_values->values[idx], value)
+#define STORE_HASH(ep, hash) FT_ATOMIC_STORE_SSIZE_RELAXED((ep)->me_hash, hash)
+#define STORE_KEYS_USABLE(keys, usable) FT_ATOMIC_STORE_SSIZE_RELAXED(keys->dk_usable, usable)
+#define STORE_KEYS_NENTRIES(keys, nentries) FT_ATOMIC_STORE_SSIZE_RELAXED(keys->dk_nentries, nentries)
+#define STORE_USED(mp, used) FT_ATOMIC_STORE_SSIZE_RELAXED(mp->ma_used, used)
+
#define PERTURB_SHIFT 5
/*
@@ -235,45 +380,56 @@ equally good collision statistics, needed less code & used less memory.
static int dictresize(PyInterpreterState *interp, PyDictObject *mp,
uint8_t log_newsize, int unicode);
-static PyObject* dict_iter(PyDictObject *dict);
+static PyObject* dict_iter(PyObject *dict);
+
+static int
+setitem_lock_held(PyDictObject *mp, PyObject *key, PyObject *value);
+static int
+dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_value,
+ PyObject **result, int incref_result);
+
+#ifndef NDEBUG
+static int _PyObject_InlineValuesConsistencyCheck(PyObject *obj);
+#endif
#include "clinic/dictobject.c.h"
-#if PyDict_MAXFREELIST > 0
-static struct _Py_dict_state *
-get_dict_state(PyInterpreterState *interp)
+#ifdef WITH_FREELISTS
+static struct _Py_dict_freelist *
+get_dict_freelist(void)
+{
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
+ return &freelists->dicts;
+}
+
+static struct _Py_dictkeys_freelist *
+get_dictkeys_freelist(void)
{
- return &interp->dict_state;
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
+ return &freelists->dictkeys;
}
#endif
void
-_PyDict_ClearFreeList(PyInterpreterState *interp)
+_PyDict_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization)
{
-#if PyDict_MAXFREELIST > 0
- struct _Py_dict_state *state = &interp->dict_state;
- while (state->numfree) {
- PyDictObject *op = state->free_list[--state->numfree];
+#ifdef WITH_FREELISTS
+ struct _Py_dict_freelist *freelist = &freelists->dicts;
+ while (freelist->numfree > 0) {
+ PyDictObject *op = freelist->items[--freelist->numfree];
assert(PyDict_CheckExact(op));
PyObject_GC_Del(op);
}
- while (state->keys_numfree) {
- PyObject_Free(state->keys_free_list[--state->keys_numfree]);
+ struct _Py_dictkeys_freelist *keys_freelist = &freelists->dictkeys;
+ while (keys_freelist->numfree > 0) {
+ PyMem_Free(keys_freelist->items[--keys_freelist->numfree]);
+ }
+ if (is_finalization) {
+ freelist->numfree = -1;
+ keys_freelist->numfree = -1;
}
-#endif
-}
-
-
-void
-_PyDict_Fini(PyInterpreterState *interp)
-{
- _PyDict_ClearFreeList(interp);
-#if defined(Py_DEBUG) && PyDict_MAXFREELIST > 0
- struct _Py_dict_state *state = &interp->dict_state;
- state->numfree = -1;
- state->keys_numfree = -1;
#endif
}
@@ -281,24 +437,26 @@ static inline Py_hash_t
unicode_get_hash(PyObject *o)
{
assert(PyUnicode_CheckExact(o));
- return _PyASCIIObject_CAST(o)->hash;
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(_PyASCIIObject_CAST(o)->hash);
}
/* Print summary info about the state of the optimized allocator */
void
_PyDict_DebugMallocStats(FILE *out)
{
-#if PyDict_MAXFREELIST > 0
- PyInterpreterState *interp = _PyInterpreterState_GET();
- struct _Py_dict_state *state = get_dict_state(interp);
+#ifdef WITH_FREELISTS
+ struct _Py_dict_freelist *dict_freelist = get_dict_freelist();
_PyDebugAllocatorStats(out, "free PyDictObject",
- state->numfree, sizeof(PyDictObject));
+ dict_freelist->numfree, sizeof(PyDictObject));
+ struct _Py_dictkeys_freelist *dictkeys_freelist = get_dictkeys_freelist();
+ _PyDebugAllocatorStats(out, "free PyDictKeysObject",
+ dictkeys_freelist->numfree, sizeof(PyDictKeysObject));
#endif
}
#define DK_MASK(dk) (DK_SIZE(dk)-1)
-static void free_keys_object(PyInterpreterState *interp, PyDictKeysObject *keys);
+static void free_keys_object(PyDictKeysObject *keys, bool use_qsbr);
/* PyDictKeysObject has refcounts like PyObject does, so we have the
following two functions to mirror what Py_INCREF() and Py_DECREF() do.
@@ -310,27 +468,43 @@ static void free_keys_object(PyInterpreterState *interp, PyDictKeysObject *keys)
static inline void
dictkeys_incref(PyDictKeysObject *dk)
{
- if (dk->dk_refcnt == _Py_IMMORTAL_REFCNT) {
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(dk->dk_refcnt) == _Py_IMMORTAL_REFCNT) {
return;
}
#ifdef Py_REF_DEBUG
- _Py_IncRefTotal(_PyInterpreterState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
#endif
- dk->dk_refcnt++;
+ INCREF_KEYS(dk);
}
static inline void
-dictkeys_decref(PyInterpreterState *interp, PyDictKeysObject *dk)
+dictkeys_decref(PyInterpreterState *interp, PyDictKeysObject *dk, bool use_qsbr)
{
- if (dk->dk_refcnt == _Py_IMMORTAL_REFCNT) {
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(dk->dk_refcnt) == _Py_IMMORTAL_REFCNT) {
return;
}
- assert(dk->dk_refcnt > 0);
+ assert(FT_ATOMIC_LOAD_SSIZE(dk->dk_refcnt) > 0);
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal(_PyInterpreterState_GET());
+ _Py_DecRefTotal(_PyThreadState_GET());
#endif
- if (--dk->dk_refcnt == 0) {
- free_keys_object(interp, dk);
+ if (DECREF_KEYS(dk) == 1) {
+ if (DK_IS_UNICODE(dk)) {
+ PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(dk);
+ Py_ssize_t i, n;
+ for (i = 0, n = dk->dk_nentries; i < n; i++) {
+ Py_XDECREF(entries[i].me_key);
+ Py_XDECREF(entries[i].me_value);
+ }
+ }
+ else {
+ PyDictKeyEntry *entries = DK_ENTRIES(dk);
+ Py_ssize_t i, n;
+ for (i = 0, n = dk->dk_nentries; i < n; i++) {
+ Py_XDECREF(entries[i].me_key);
+ Py_XDECREF(entries[i].me_value);
+ }
+ }
+ free_keys_object(dk, use_qsbr);
}
}
@@ -342,22 +516,18 @@ dictkeys_get_index(const PyDictKeysObject *keys, Py_ssize_t i)
Py_ssize_t ix;
if (log2size < 8) {
- const int8_t *indices = (const int8_t*)(keys->dk_indices);
- ix = indices[i];
+ ix = LOAD_INDEX(keys, 8, i);
}
else if (log2size < 16) {
- const int16_t *indices = (const int16_t*)(keys->dk_indices);
- ix = indices[i];
+ ix = LOAD_INDEX(keys, 16, i);
}
#if SIZEOF_VOID_P > 4
else if (log2size >= 32) {
- const int64_t *indices = (const int64_t*)(keys->dk_indices);
- ix = indices[i];
+ ix = LOAD_INDEX(keys, 64, i);
}
#endif
else {
- const int32_t *indices = (const int32_t*)(keys->dk_indices);
- ix = indices[i];
+ ix = LOAD_INDEX(keys, 32, i);
}
assert(ix >= DKIX_DUMMY);
return ix;
@@ -373,25 +543,21 @@ dictkeys_set_index(PyDictKeysObject *keys, Py_ssize_t i, Py_ssize_t ix)
assert(keys->dk_version == 0);
if (log2size < 8) {
- int8_t *indices = (int8_t*)(keys->dk_indices);
assert(ix <= 0x7f);
- indices[i] = (char)ix;
+ STORE_INDEX(keys, 8, i, ix);
}
else if (log2size < 16) {
- int16_t *indices = (int16_t*)(keys->dk_indices);
assert(ix <= 0x7fff);
- indices[i] = (int16_t)ix;
+ STORE_INDEX(keys, 16, i, ix);
}
#if SIZEOF_VOID_P > 4
else if (log2size >= 32) {
- int64_t *indices = (int64_t*)(keys->dk_indices);
- indices[i] = ix;
+ STORE_INDEX(keys, 64, i, ix);
}
#endif
else {
- int32_t *indices = (int32_t*)(keys->dk_indices);
assert(ix <= 0x7fffffff);
- indices[i] = (int32_t)ix;
+ STORE_INDEX(keys, 32, i, ix);
}
}
@@ -414,13 +580,13 @@ static inline uint8_t
calculate_log2_keysize(Py_ssize_t minsize)
{
#if SIZEOF_LONG == SIZEOF_SIZE_T
- minsize = (minsize | PyDict_MINSIZE) - 1;
- return _Py_bit_length(minsize | (PyDict_MINSIZE-1));
+ minsize = Py_MAX(minsize, PyDict_MINSIZE);
+ return _Py_bit_length(minsize - 1);
#elif defined(_MSC_VER)
- // On 64bit Windows, sizeof(long) == 4.
- minsize = (minsize | PyDict_MINSIZE) - 1;
+ // On 64bit Windows, sizeof(long) == 4. We cannot use _Py_bit_length.
+ minsize = Py_MAX(minsize, PyDict_MINSIZE);
unsigned long msb;
- _BitScanReverse64(&msb, (uint64_t)minsize);
+ _BitScanReverse64(&msb, (uint64_t)minsize - 1);
return (uint8_t)(msb + 1);
#else
uint8_t log2_size;
@@ -467,6 +633,9 @@ static PyDictKeysObject empty_keys_struct = {
0, /* dk_log2_size */
3, /* dk_log2_index_bytes */
DICT_KEYS_UNICODE, /* dk_kind */
+#ifdef Py_GIL_DISABLED
+ {0}, /* dk_mutex */
+#endif
1, /* dk_version */
0, /* dk_usable (immutable) */
0, /* dk_nentries */
@@ -489,8 +658,9 @@ static inline int
get_index_from_order(PyDictObject *mp, Py_ssize_t i)
{
assert(mp->ma_used <= SHARED_KEYS_MAX_SIZE);
- assert(i < (((char *)mp->ma_values)[-2]));
- return ((char *)mp->ma_values)[-3-i];
+ assert(i < mp->ma_values->size);
+ uint8_t *array = get_insertion_order_array(mp->ma_values);
+ return array[i];
}
#ifdef DEBUG_PYDICT
@@ -513,6 +683,8 @@ dump_entries(PyDictKeysObject *dk)
int
_PyDict_CheckConsistency(PyObject *op, int check_content)
{
+ ASSERT_WORLD_STOPPED_OR_DICT_LOCKED(op);
+
#define CHECK(expr) \
do { if (!(expr)) { _PyObject_ASSERT_FAILED_MSG(op, Py_STRINGIFY(expr)); } } while (0)
@@ -524,10 +696,15 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
int splitted = _PyDict_HasSplitTable(mp);
Py_ssize_t usable = USABLE_FRACTION(DK_SIZE(keys));
+ // In the free-threaded build, shared keys may be concurrently modified,
+ // so use atomic loads.
+ Py_ssize_t dk_usable = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(keys->dk_usable);
+ Py_ssize_t dk_nentries = FT_ATOMIC_LOAD_SSIZE_ACQUIRE(keys->dk_nentries);
+
CHECK(0 <= mp->ma_used && mp->ma_used <= usable);
- CHECK(0 <= keys->dk_usable && keys->dk_usable <= usable);
- CHECK(0 <= keys->dk_nentries && keys->dk_nentries <= usable);
- CHECK(keys->dk_usable + keys->dk_nentries <= usable);
+ CHECK(0 <= dk_usable && dk_usable <= usable);
+ CHECK(0 <= dk_nentries && dk_nentries <= usable);
+ CHECK(dk_usable + dk_nentries <= usable);
if (!splitted) {
/* combined table */
@@ -537,9 +714,14 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
else {
CHECK(keys->dk_kind == DICT_KEYS_SPLIT);
CHECK(mp->ma_used <= SHARED_KEYS_MAX_SIZE);
+ if (mp->ma_values->embedded) {
+ CHECK(mp->ma_values->embedded == 1);
+ CHECK(mp->ma_values->valid == 1);
+ }
}
if (check_content) {
+ LOCK_KEYS_IF_SPLIT(keys, keys->dk_kind);
for (Py_ssize_t i=0; i < DK_SIZE(keys); i++) {
Py_ssize_t ix = dictkeys_get_index(keys, i);
CHECK(DKIX_DUMMY <= ix && ix <= usable);
@@ -595,6 +777,7 @@ _PyDict_CheckConsistency(PyObject *op, int check_content)
CHECK(mp->ma_values->values[index] != NULL);
}
}
+ UNLOCK_KEYS_IF_SPLIT(keys, keys->dk_kind);
}
return 1;
@@ -628,34 +811,33 @@ new_keys_object(PyInterpreterState *interp, uint8_t log2_size, bool unicode)
log2_bytes = log2_size + 2;
}
-#if PyDict_MAXFREELIST > 0
- struct _Py_dict_state *state = get_dict_state(interp);
-#ifdef Py_DEBUG
- // new_keys_object() must not be called after _PyDict_Fini()
- assert(state->keys_numfree != -1);
-#endif
- if (log2_size == PyDict_LOG_MINSIZE && unicode && state->keys_numfree > 0) {
- dk = state->keys_free_list[--state->keys_numfree];
+#ifdef WITH_FREELISTS
+ struct _Py_dictkeys_freelist *freelist = get_dictkeys_freelist();
+ if (log2_size == PyDict_LOG_MINSIZE && unicode && freelist->numfree > 0) {
+ dk = freelist->items[--freelist->numfree];
OBJECT_STAT_INC(from_freelist);
}
else
#endif
{
- dk = PyObject_Malloc(sizeof(PyDictKeysObject)
- + ((size_t)1 << log2_bytes)
- + entry_size * usable);
+ dk = PyMem_Malloc(sizeof(PyDictKeysObject)
+ + ((size_t)1 << log2_bytes)
+ + entry_size * usable);
if (dk == NULL) {
PyErr_NoMemory();
return NULL;
}
}
#ifdef Py_REF_DEBUG
- _Py_IncRefTotal(_PyInterpreterState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
#endif
dk->dk_refcnt = 1;
dk->dk_log2_size = log2_size;
dk->dk_log2_index_bytes = log2_bytes;
dk->dk_kind = unicode ? DICT_KEYS_UNICODE : DICT_KEYS_GENERAL;
+#ifdef Py_GIL_DISABLED
+ dk->dk_mutex = (PyMutex){0};
+#endif
dk->dk_nentries = 0;
dk->dk_usable = usable;
dk->dk_version = 0;
@@ -665,63 +847,66 @@ new_keys_object(PyInterpreterState *interp, uint8_t log2_size, bool unicode)
}
static void
-free_keys_object(PyInterpreterState *interp, PyDictKeysObject *keys)
+free_keys_object(PyDictKeysObject *keys, bool use_qsbr)
{
- assert(keys != Py_EMPTY_KEYS);
- if (DK_IS_UNICODE(keys)) {
- PyDictUnicodeEntry *entries = DK_UNICODE_ENTRIES(keys);
- Py_ssize_t i, n;
- for (i = 0, n = keys->dk_nentries; i < n; i++) {
- Py_XDECREF(entries[i].me_key);
- Py_XDECREF(entries[i].me_value);
- }
- }
- else {
- PyDictKeyEntry *entries = DK_ENTRIES(keys);
- Py_ssize_t i, n;
- for (i = 0, n = keys->dk_nentries; i < n; i++) {
- Py_XDECREF(entries[i].me_key);
- Py_XDECREF(entries[i].me_value);
- }
+#ifdef Py_GIL_DISABLED
+ if (use_qsbr) {
+ _PyMem_FreeDelayed(keys, _PyDict_KeysSize(keys));
+ return;
}
-#if PyDict_MAXFREELIST > 0
- struct _Py_dict_state *state = get_dict_state(interp);
-#ifdef Py_DEBUG
- // free_keys_object() must not be called after _PyDict_Fini()
- assert(state->keys_numfree != -1);
#endif
+#ifdef WITH_FREELISTS
+ struct _Py_dictkeys_freelist *freelist = get_dictkeys_freelist();
if (DK_LOG_SIZE(keys) == PyDict_LOG_MINSIZE
- && state->keys_numfree < PyDict_MAXFREELIST
+ && freelist->numfree < PyDict_MAXFREELIST
+ && freelist->numfree >= 0
&& DK_IS_UNICODE(keys)) {
- state->keys_free_list[state->keys_numfree++] = keys;
+ freelist->items[freelist->numfree++] = keys;
OBJECT_STAT_INC(to_freelist);
return;
}
#endif
- PyObject_Free(keys);
+ PyMem_Free(keys);
}
+static size_t
+values_size_from_count(size_t count)
+{
+ assert(count >= 1);
+ size_t suffix_size = _Py_SIZE_ROUND_UP(count, sizeof(PyObject *));
+ assert(suffix_size < 128);
+ assert(suffix_size % sizeof(PyObject *) == 0);
+ return (count + 1) * sizeof(PyObject *) + suffix_size;
+}
+
+#define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys)
+
static inline PyDictValues*
new_values(size_t size)
{
- assert(size >= 1);
- size_t prefix_size = _Py_SIZE_ROUND_UP(size+2, sizeof(PyObject *));
- assert(prefix_size < 256);
- size_t n = prefix_size + size * sizeof(PyObject *);
- uint8_t *mem = PyMem_Malloc(n);
- if (mem == NULL) {
+ size_t n = values_size_from_count(size);
+ PyDictValues *res = (PyDictValues *)PyMem_Malloc(n);
+ if (res == NULL) {
return NULL;
}
- assert(prefix_size % sizeof(PyObject *) == 0);
- mem[prefix_size-1] = (uint8_t)prefix_size;
- return (PyDictValues*)(mem + prefix_size);
+ res->embedded = 0;
+ res->size = 0;
+ assert(size < 256);
+ res->capacity = (uint8_t)size;
+ return res;
}
static inline void
-free_values(PyDictValues *values)
+free_values(PyDictValues *values, bool use_qsbr)
{
- int prefix_size = ((uint8_t *)values)[-1];
- PyMem_Free(((char *)values)-prefix_size);
+ assert(values->embedded == 0);
+#ifdef Py_GIL_DISABLED
+ if (use_qsbr) {
+ _PyMem_FreeDelayed(values, values_size_from_count(values->capacity));
+ return;
+ }
+#endif
+ PyMem_Free(values);
}
/* Consumes a reference to the keys object */
@@ -732,14 +917,10 @@ new_dict(PyInterpreterState *interp,
{
PyDictObject *mp;
assert(keys != NULL);
-#if PyDict_MAXFREELIST > 0
- struct _Py_dict_state *state = get_dict_state(interp);
-#ifdef Py_DEBUG
- // new_dict() must not be called after _PyDict_Fini()
- assert(state->numfree != -1);
-#endif
- if (state->numfree) {
- mp = state->free_list[--state->numfree];
+#ifdef WITH_FREELISTS
+ struct _Py_dict_freelist *freelist = get_dict_freelist();
+ if (freelist->numfree > 0) {
+ mp = freelist->items[--freelist->numfree];
assert (mp != NULL);
assert (Py_IS_TYPE(mp, &PyDict_Type));
OBJECT_STAT_INC(from_freelist);
@@ -750,9 +931,9 @@ new_dict(PyInterpreterState *interp,
{
mp = PyObject_GC_New(PyDictObject, &PyDict_Type);
if (mp == NULL) {
- dictkeys_decref(interp, keys);
+ dictkeys_decref(interp, keys, false);
if (free_values_on_failure) {
- free_values(values);
+ free_values(values, false);
}
return NULL;
}
@@ -765,23 +946,15 @@ new_dict(PyInterpreterState *interp,
return (PyObject *)mp;
}
-static inline size_t
-shared_keys_usable_size(PyDictKeysObject *keys)
-{
- return (size_t)keys->dk_nentries + (size_t)keys->dk_usable;
-}
-
-/* Consumes a reference to the keys object */
static PyObject *
new_dict_with_shared_keys(PyInterpreterState *interp, PyDictKeysObject *keys)
{
size_t size = shared_keys_usable_size(keys);
PyDictValues *values = new_values(size);
if (values == NULL) {
- dictkeys_decref(interp, keys);
return PyErr_NoMemory();
}
- ((char *)values)[-2] = 0;
+ dictkeys_incref(keys);
for (size_t i = 0; i < size; i++) {
values->values[i] = NULL;
}
@@ -793,13 +966,15 @@ static PyDictKeysObject *
clone_combined_dict_keys(PyDictObject *orig)
{
assert(PyDict_Check(orig));
- assert(Py_TYPE(orig)->tp_iter == (getiterfunc)dict_iter);
+ assert(Py_TYPE(orig)->tp_iter == dict_iter);
assert(orig->ma_values == NULL);
assert(orig->ma_keys != Py_EMPTY_KEYS);
assert(orig->ma_keys->dk_refcnt == 1);
+ ASSERT_DICT_LOCKED(orig);
+
size_t keys_size = _PyDict_KeysSize(orig->ma_keys);
- PyDictKeysObject *keys = PyObject_Malloc(keys_size);
+ PyDictKeysObject *keys = PyMem_Malloc(keys_size);
if (keys == NULL) {
PyErr_NoMemory();
return NULL;
@@ -841,7 +1016,7 @@ clone_combined_dict_keys(PyDictObject *orig)
we have it now; calling dictkeys_incref would be an error as
keys->dk_refcnt is already set to 1 (after memcpy). */
#ifdef Py_REF_DEBUG
- _Py_IncRefTotal(_PyInterpreterState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
#endif
return keys;
}
@@ -876,11 +1051,11 @@ lookdict_index(PyDictKeysObject *k, Py_hash_t hash, Py_ssize_t index)
Py_UNREACHABLE();
}
-// Search non-Unicode key from Unicode table
-static Py_ssize_t
-unicodekeys_lookup_generic(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
+static inline Py_ALWAYS_INLINE Py_ssize_t
+do_lookup(PyDictObject *mp, PyDictKeysObject *dk, PyObject *key, Py_hash_t hash,
+ int (*check_lookup)(PyDictObject *, PyDictKeysObject *, void *, Py_ssize_t ix, PyObject *key, Py_hash_t))
{
- PyDictUnicodeEntry *ep0 = DK_UNICODE_ENTRIES(dk);
+ void *ep0 = _DK_ENTRIES(dk);
size_t mask = DK_MASK(dk);
size_t perturb = hash;
size_t i = (size_t)hash & mask;
@@ -888,73 +1063,26 @@ unicodekeys_lookup_generic(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key
for (;;) {
ix = dictkeys_get_index(dk, i);
if (ix >= 0) {
- PyDictUnicodeEntry *ep = &ep0[ix];
- assert(ep->me_key != NULL);
- assert(PyUnicode_CheckExact(ep->me_key));
- if (ep->me_key == key) {
+ int cmp = check_lookup(mp, dk, ep0, ix, key, hash);
+ if (cmp < 0) {
+ return cmp;
+ } else if (cmp) {
return ix;
}
- if (unicode_get_hash(ep->me_key) == hash) {
- PyObject *startkey = ep->me_key;
- Py_INCREF(startkey);
- int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
- Py_DECREF(startkey);
- if (cmp < 0) {
- return DKIX_ERROR;
- }
- if (dk == mp->ma_keys && ep->me_key == startkey) {
- if (cmp > 0) {
- return ix;
- }
- }
- else {
- /* The dict was mutated, restart */
- return DKIX_KEY_CHANGED;
- }
- }
}
else if (ix == DKIX_EMPTY) {
return DKIX_EMPTY;
}
perturb >>= PERTURB_SHIFT;
i = mask & (i*5 + perturb + 1);
- }
- Py_UNREACHABLE();
-}
-// Search Unicode key from Unicode table.
-static Py_ssize_t _Py_HOT_FUNCTION
-unicodekeys_lookup_unicode(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
-{
- PyDictUnicodeEntry *ep0 = DK_UNICODE_ENTRIES(dk);
- size_t mask = DK_MASK(dk);
- size_t perturb = hash;
- size_t i = (size_t)hash & mask;
- Py_ssize_t ix;
- for (;;) {
- ix = dictkeys_get_index(dk, i);
- if (ix >= 0) {
- PyDictUnicodeEntry *ep = &ep0[ix];
- assert(ep->me_key != NULL);
- assert(PyUnicode_CheckExact(ep->me_key));
- if (ep->me_key == key ||
- (unicode_get_hash(ep->me_key) == hash && unicode_eq(ep->me_key, key))) {
- return ix;
- }
- }
- else if (ix == DKIX_EMPTY) {
- return DKIX_EMPTY;
- }
- perturb >>= PERTURB_SHIFT;
- i = mask & (i*5 + perturb + 1);
// Manual loop unrolling
ix = dictkeys_get_index(dk, i);
if (ix >= 0) {
- PyDictUnicodeEntry *ep = &ep0[ix];
- assert(ep->me_key != NULL);
- assert(PyUnicode_CheckExact(ep->me_key));
- if (ep->me_key == key ||
- (unicode_get_hash(ep->me_key) == hash && unicode_eq(ep->me_key, key))) {
+ int cmp = check_lookup(mp, dk, ep0, ix, key, hash);
+ if (cmp < 0) {
+ return cmp;
+ } else if (cmp) {
return ix;
}
}
@@ -967,49 +1095,125 @@ unicodekeys_lookup_unicode(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
Py_UNREACHABLE();
}
-// Search key from Generic table.
+static inline int
+compare_unicode_generic(PyDictObject *mp, PyDictKeysObject *dk,
+ void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
+{
+ PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix];
+ assert(ep->me_key != NULL);
+ assert(PyUnicode_CheckExact(ep->me_key));
+ assert(!PyUnicode_CheckExact(key));
+
+ if (unicode_get_hash(ep->me_key) == hash) {
+ PyObject *startkey = ep->me_key;
+ Py_INCREF(startkey);
+ int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
+ Py_DECREF(startkey);
+ if (cmp < 0) {
+ return DKIX_ERROR;
+ }
+ if (dk == mp->ma_keys && ep->me_key == startkey) {
+ return cmp;
+ }
+ else {
+ /* The dict was mutated, restart */
+ return DKIX_KEY_CHANGED;
+ }
+ }
+ return 0;
+}
+
+// Search non-Unicode key from Unicode table
static Py_ssize_t
-dictkeys_generic_lookup(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
+unicodekeys_lookup_generic(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
{
- PyDictKeyEntry *ep0 = DK_ENTRIES(dk);
- size_t mask = DK_MASK(dk);
- size_t perturb = hash;
- size_t i = (size_t)hash & mask;
- Py_ssize_t ix;
- for (;;) {
- ix = dictkeys_get_index(dk, i);
- if (ix >= 0) {
- PyDictKeyEntry *ep = &ep0[ix];
- assert(ep->me_key != NULL);
- if (ep->me_key == key) {
- return ix;
- }
- if (ep->me_hash == hash) {
- PyObject *startkey = ep->me_key;
- Py_INCREF(startkey);
- int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
- Py_DECREF(startkey);
- if (cmp < 0) {
- return DKIX_ERROR;
- }
- if (dk == mp->ma_keys && ep->me_key == startkey) {
- if (cmp > 0) {
- return ix;
- }
- }
- else {
- /* The dict was mutated, restart */
- return DKIX_KEY_CHANGED;
- }
- }
+ return do_lookup(mp, dk, key, hash, compare_unicode_generic);
+}
+
+static inline int
+compare_unicode_unicode(PyDictObject *mp, PyDictKeysObject *dk,
+ void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
+{
+ PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix];
+ PyObject *ep_key = FT_ATOMIC_LOAD_PTR_RELAXED(ep->me_key);
+ assert(ep_key != NULL);
+ assert(PyUnicode_CheckExact(ep_key));
+ if (ep_key == key ||
+ (unicode_get_hash(ep_key) == hash && unicode_eq(ep_key, key))) {
+ return 1;
+ }
+ return 0;
+}
+
+static Py_ssize_t _Py_HOT_FUNCTION
+unicodekeys_lookup_unicode(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
+{
+ return do_lookup(NULL, dk, key, hash, compare_unicode_unicode);
+}
+
+static inline int
+compare_generic(PyDictObject *mp, PyDictKeysObject *dk,
+ void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
+{
+ PyDictKeyEntry *ep = &((PyDictKeyEntry *)ep0)[ix];
+ assert(ep->me_key != NULL);
+ if (ep->me_key == key) {
+ return 1;
+ }
+ if (ep->me_hash == hash) {
+ PyObject *startkey = ep->me_key;
+ Py_INCREF(startkey);
+ int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
+ Py_DECREF(startkey);
+ if (cmp < 0) {
+ return DKIX_ERROR;
}
- else if (ix == DKIX_EMPTY) {
- return DKIX_EMPTY;
+ if (dk == mp->ma_keys && ep->me_key == startkey) {
+ return cmp;
+ }
+ else {
+ /* The dict was mutated, restart */
+ return DKIX_KEY_CHANGED;
}
- perturb >>= PERTURB_SHIFT;
- i = mask & (i*5 + perturb + 1);
}
- Py_UNREACHABLE();
+ return 0;
+}
+
+static Py_ssize_t
+dictkeys_generic_lookup(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
+{
+ return do_lookup(mp, dk, key, hash, compare_generic);
+}
+
+#ifdef Py_GIL_DISABLED
+
+static Py_ssize_t
+unicodekeys_lookup_unicode_threadsafe(PyDictKeysObject* dk, PyObject *key,
+ Py_hash_t hash);
+
+#endif
+
+static Py_ssize_t
+unicodekeys_lookup_split(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
+{
+ Py_ssize_t ix;
+ assert(dk->dk_kind == DICT_KEYS_SPLIT);
+ assert(PyUnicode_CheckExact(key));
+
+#ifdef Py_GIL_DISABLED
+ // A split dictionaries keys can be mutated by other dictionaries
+ // but if we have a unicode key we can avoid locking the shared
+ // keys.
+ ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
+ if (ix == DKIX_KEY_CHANGED) {
+ LOCK_KEYS(dk);
+ ix = unicodekeys_lookup_unicode(dk, key, hash);
+ UNLOCK_KEYS(dk);
+ }
+#else
+ ix = unicodekeys_lookup_unicode(dk, key, hash);
+#endif
+ return ix;
}
/* Lookup a string in a (all unicode) dict keys.
@@ -1036,6 +1240,25 @@ _PyDictKeys_StringLookup(PyDictKeysObject* dk, PyObject *key)
return unicodekeys_lookup_unicode(dk, key, hash);
}
+/* Like _PyDictKeys_StringLookup() but only works on split keys. Note
+ * that in free-threaded builds this locks the keys object as required.
+ */
+Py_ssize_t
+_PyDictKeys_StringLookupSplit(PyDictKeysObject* dk, PyObject *key)
+{
+ assert(dk->dk_kind == DICT_KEYS_SPLIT);
+ assert(PyUnicode_CheckExact(key));
+ Py_hash_t hash = unicode_get_hash(key);
+ if (hash == -1) {
+ hash = PyUnicode_Type.tp_hash(key);
+ if (hash == -1) {
+ PyErr_Clear();
+ return DKIX_ERROR;
+ }
+ }
+ return unicodekeys_lookup_split(dk, key, hash);
+}
+
/*
The basic lookup function used by all operations.
This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
@@ -1058,16 +1281,40 @@ _Py_dict_lookup(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **valu
DictKeysKind kind;
Py_ssize_t ix;
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
start:
dk = mp->ma_keys;
kind = dk->dk_kind;
if (kind != DICT_KEYS_GENERAL) {
if (PyUnicode_CheckExact(key)) {
+#ifdef Py_GIL_DISABLED
+ if (kind == DICT_KEYS_SPLIT) {
+ // A split dictionaries keys can be mutated by other
+ // dictionaries but if we have a unicode key we can avoid
+ // locking the shared keys.
+ ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
+ if (ix == DKIX_KEY_CHANGED) {
+ LOCK_KEYS(dk);
+ ix = unicodekeys_lookup_unicode(dk, key, hash);
+ UNLOCK_KEYS(dk);
+ }
+ }
+ else {
+ ix = unicodekeys_lookup_unicode(dk, key, hash);
+ }
+#else
ix = unicodekeys_lookup_unicode(dk, key, hash);
+#endif
}
else {
+ INCREF_KEYS_FT(dk);
+ LOCK_KEYS_IF_SPLIT(dk, kind);
+
ix = unicodekeys_lookup_generic(mp, dk, key, hash);
+
+ UNLOCK_KEYS_IF_SPLIT(dk, kind);
+ DECREF_KEYS_FT(dk, IS_DICT_SHARED(mp));
if (ix == DKIX_KEY_CHANGED) {
goto start;
}
@@ -1101,6 +1348,270 @@ start:
return ix;
}
+#ifdef Py_GIL_DISABLED
+static inline void
+ensure_shared_on_read(PyDictObject *mp)
+{
+ if (!_Py_IsOwnedByCurrentThread((PyObject *)mp) && !IS_DICT_SHARED(mp)) {
+ // The first time we access a dict from a non-owning thread we mark it
+ // as shared. This ensures that a concurrent resize operation will
+ // delay freeing the old keys or values using QSBR, which is necessary
+ // to safely allow concurrent reads without locking...
+ Py_BEGIN_CRITICAL_SECTION(mp);
+ if (!IS_DICT_SHARED(mp)) {
+ SET_DICT_SHARED(mp);
+ }
+ Py_END_CRITICAL_SECTION();
+ }
+}
+#endif
+
+static inline void
+ensure_shared_on_resize(PyDictObject *mp)
+{
+#ifdef Py_GIL_DISABLED
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(mp);
+
+ if (!_Py_IsOwnedByCurrentThread((PyObject *)mp) && !IS_DICT_SHARED(mp)) {
+ // We are writing to the dict from another thread that owns
+ // it and we haven't marked it as shared which will ensure
+ // that when we re-size ma_keys or ma_values that we will
+ // free using QSBR. We need to lock the dictionary to
+ // contend with writes from the owning thread, mark it as
+ // shared, and then we can continue with lock-free reads.
+ // Technically this is a little heavy handed, we could just
+ // free the individual old keys / old-values using qsbr
+ SET_DICT_SHARED(mp);
+ }
+#endif
+}
+
+#ifdef Py_GIL_DISABLED
+
+static inline Py_ALWAYS_INLINE int
+compare_unicode_generic_threadsafe(PyDictObject *mp, PyDictKeysObject *dk,
+ void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
+{
+ PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix];
+ PyObject *startkey = _Py_atomic_load_ptr_relaxed(&ep->me_key);
+ assert(startkey == NULL || PyUnicode_CheckExact(ep->me_key));
+ assert(!PyUnicode_CheckExact(key));
+
+ if (startkey != NULL) {
+ if (!_Py_TryIncrefCompare(&ep->me_key, startkey)) {
+ return DKIX_KEY_CHANGED;
+ }
+
+ if (unicode_get_hash(startkey) == hash) {
+ int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
+ Py_DECREF(startkey);
+ if (cmp < 0) {
+ return DKIX_ERROR;
+ }
+ if (dk == _Py_atomic_load_ptr_relaxed(&mp->ma_keys) &&
+ startkey == _Py_atomic_load_ptr_relaxed(&ep->me_key)) {
+ return cmp;
+ }
+ else {
+ /* The dict was mutated, restart */
+ return DKIX_KEY_CHANGED;
+ }
+ }
+ else {
+ Py_DECREF(startkey);
+ }
+ }
+ return 0;
+}
+
+// Search non-Unicode key from Unicode table
+static Py_ssize_t
+unicodekeys_lookup_generic_threadsafe(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
+{
+ return do_lookup(mp, dk, key, hash, compare_unicode_generic_threadsafe);
+}
+
+static inline Py_ALWAYS_INLINE int
+compare_unicode_unicode_threadsafe(PyDictObject *mp, PyDictKeysObject *dk,
+ void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
+{
+ PyDictUnicodeEntry *ep = &((PyDictUnicodeEntry *)ep0)[ix];
+ PyObject *startkey = _Py_atomic_load_ptr_relaxed(&ep->me_key);
+ assert(startkey == NULL || PyUnicode_CheckExact(startkey));
+ if (startkey == key) {
+ return 1;
+ }
+ if (startkey != NULL) {
+ if (_Py_IsImmortal(startkey)) {
+ return unicode_get_hash(startkey) == hash && unicode_eq(startkey, key);
+ }
+ else {
+ if (!_Py_TryIncrefCompare(&ep->me_key, startkey)) {
+ return DKIX_KEY_CHANGED;
+ }
+ if (unicode_get_hash(startkey) == hash && unicode_eq(startkey, key)) {
+ Py_DECREF(startkey);
+ return 1;
+ }
+ Py_DECREF(startkey);
+ }
+ }
+ return 0;
+}
+
+static Py_ssize_t _Py_HOT_FUNCTION
+unicodekeys_lookup_unicode_threadsafe(PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
+{
+ return do_lookup(NULL, dk, key, hash, compare_unicode_unicode_threadsafe);
+}
+
+static inline Py_ALWAYS_INLINE int
+compare_generic_threadsafe(PyDictObject *mp, PyDictKeysObject *dk,
+ void *ep0, Py_ssize_t ix, PyObject *key, Py_hash_t hash)
+{
+ PyDictKeyEntry *ep = &((PyDictKeyEntry *)ep0)[ix];
+ PyObject *startkey = _Py_atomic_load_ptr_relaxed(&ep->me_key);
+ if (startkey == key) {
+ return 1;
+ }
+ Py_ssize_t ep_hash = _Py_atomic_load_ssize_relaxed(&ep->me_hash);
+ if (ep_hash == hash) {
+ if (startkey == NULL || !_Py_TryIncrefCompare(&ep->me_key, startkey)) {
+ return DKIX_KEY_CHANGED;
+ }
+ int cmp = PyObject_RichCompareBool(startkey, key, Py_EQ);
+ Py_DECREF(startkey);
+ if (cmp < 0) {
+ return DKIX_ERROR;
+ }
+ if (dk == _Py_atomic_load_ptr_relaxed(&mp->ma_keys) &&
+ startkey == _Py_atomic_load_ptr_relaxed(&ep->me_key)) {
+ return cmp;
+ }
+ else {
+ /* The dict was mutated, restart */
+ return DKIX_KEY_CHANGED;
+ }
+ }
+ return 0;
+}
+
+static Py_ssize_t
+dictkeys_generic_lookup_threadsafe(PyDictObject *mp, PyDictKeysObject* dk, PyObject *key, Py_hash_t hash)
+{
+ return do_lookup(mp, dk, key, hash, compare_generic_threadsafe);
+}
+
+Py_ssize_t
+_Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr)
+{
+ PyDictKeysObject *dk;
+ DictKeysKind kind;
+ Py_ssize_t ix;
+ PyObject *value;
+
+ ensure_shared_on_read(mp);
+
+ dk = _Py_atomic_load_ptr(&mp->ma_keys);
+ kind = dk->dk_kind;
+
+ if (kind != DICT_KEYS_GENERAL) {
+ if (PyUnicode_CheckExact(key)) {
+ ix = unicodekeys_lookup_unicode_threadsafe(dk, key, hash);
+ }
+ else {
+ ix = unicodekeys_lookup_generic_threadsafe(mp, dk, key, hash);
+ }
+ if (ix == DKIX_KEY_CHANGED) {
+ goto read_failed;
+ }
+
+ if (ix >= 0) {
+ if (kind == DICT_KEYS_SPLIT) {
+ PyDictValues *values = _Py_atomic_load_ptr(&mp->ma_values);
+ if (values == NULL)
+ goto read_failed;
+
+ uint8_t capacity = _Py_atomic_load_uint8_relaxed(&values->capacity);
+ if (ix >= (Py_ssize_t)capacity)
+ goto read_failed;
+
+ value = _Py_TryXGetRef(&values->values[ix]);
+ if (value == NULL)
+ goto read_failed;
+
+ if (values != _Py_atomic_load_ptr(&mp->ma_values)) {
+ Py_DECREF(value);
+ goto read_failed;
+ }
+ }
+ else {
+ value = _Py_TryXGetRef(&DK_UNICODE_ENTRIES(dk)[ix].me_value);
+ if (value == NULL) {
+ goto read_failed;
+ }
+
+ if (dk != _Py_atomic_load_ptr(&mp->ma_keys)) {
+ Py_DECREF(value);
+ goto read_failed;
+ }
+ }
+ }
+ else {
+ value = NULL;
+ }
+ }
+ else {
+ ix = dictkeys_generic_lookup_threadsafe(mp, dk, key, hash);
+ if (ix == DKIX_KEY_CHANGED) {
+ goto read_failed;
+ }
+ if (ix >= 0) {
+ value = _Py_TryXGetRef(&DK_ENTRIES(dk)[ix].me_value);
+ if (value == NULL)
+ goto read_failed;
+
+ if (dk != _Py_atomic_load_ptr(&mp->ma_keys)) {
+ Py_DECREF(value);
+ goto read_failed;
+ }
+ }
+ else {
+ value = NULL;
+ }
+ }
+
+ *value_addr = value;
+ return ix;
+
+read_failed:
+ // In addition to the normal races of the dict being modified the _Py_TryXGetRef
+ // can all fail if they don't yet have a shared ref count. That can happen here
+ // or in the *_lookup_* helper. In that case we need to take the lock to avoid
+ // mutation and do a normal incref which will make them shared.
+ Py_BEGIN_CRITICAL_SECTION(mp);
+ ix = _Py_dict_lookup(mp, key, hash, &value);
+ *value_addr = value;
+ if (value != NULL) {
+ assert(ix >= 0);
+ _Py_NewRefWithLock(value);
+ }
+ Py_END_CRITICAL_SECTION();
+ return ix;
+}
+
+#else // Py_GIL_DISABLED
+
+Py_ssize_t
+_Py_dict_lookup_threadsafe(PyDictObject *mp, PyObject *key, Py_hash_t hash, PyObject **value_addr)
+{
+ Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, value_addr);
+ Py_XNewRef(*value_addr);
+ return ix;
+}
+
+#endif
+
int
_PyDict_HasOnlyStringKeys(PyObject *dict)
{
@@ -1133,10 +1644,13 @@ _PyDict_MaybeUntrack(PyObject *op)
PyObject *value;
Py_ssize_t i, numentries;
+ ASSERT_WORLD_STOPPED_OR_DICT_LOCKED(op);
+
if (!PyDict_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op))
return;
mp = (PyDictObject *) op;
+ ASSERT_CONSISTENT(mp);
numentries = mp->ma_keys->dk_nentries;
if (_PyDict_HasSplitTable(mp)) {
for (i = 0; i < numentries; i++) {
@@ -1171,10 +1685,19 @@ _PyDict_MaybeUntrack(PyObject *op)
_PyObject_GC_UNTRACK(op);
}
+static inline int
+is_unusable_slot(Py_ssize_t ix)
+{
+#ifdef Py_GIL_DISABLED
+ return ix >= 0 || ix == DKIX_DUMMY;
+#else
+ return ix >= 0;
+#endif
+}
+
/* Internal function to find slot for an item from its hash
when it is known that the key is not present in the dict.
-
- The dict must be combined. */
+ */
static Py_ssize_t
find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash)
{
@@ -1183,7 +1706,7 @@ find_empty_slot(PyDictKeysObject *keys, Py_hash_t hash)
const size_t mask = DK_MASK(keys);
size_t i = hash & mask;
Py_ssize_t ix = dictkeys_get_index(keys, i);
- for (size_t perturb = hash; ix >= 0;) {
+ for (size_t perturb = hash; is_unusable_slot(ix);) {
perturb >>= PERTURB_SHIFT;
i = (i*5 + perturb + 1) & mask;
ix = dictkeys_get_index(keys, i);
@@ -1197,38 +1720,108 @@ insertion_resize(PyInterpreterState *interp, PyDictObject *mp, int unicode)
return dictresize(interp, mp, calculate_log2_keysize(GROWTH_RATE(mp)), unicode);
}
-static Py_ssize_t
-insert_into_dictkeys(PyDictKeysObject *keys, PyObject *name)
+static inline int
+insert_combined_dict(PyInterpreterState *interp, PyDictObject *mp,
+ Py_hash_t hash, PyObject *key, PyObject *value)
{
- assert(PyUnicode_CheckExact(name));
- Py_hash_t hash = unicode_get_hash(name);
- if (hash == -1) {
- hash = PyUnicode_Type.tp_hash(name);
- if (hash == -1) {
- PyErr_Clear();
- return DKIX_EMPTY;
- }
+ // gh-140551: If dict was cleared in _Py_dict_lookup,
+ // we have to resize one more time to force general key kind.
+ if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) {
+ if (insertion_resize(interp, mp, 0) < 0)
+ return -1;
+ assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
}
- Py_ssize_t ix = unicodekeys_lookup_unicode(keys, name, hash);
- if (ix == DKIX_EMPTY) {
- if (keys->dk_usable <= 0) {
- return DKIX_EMPTY;
+
+ if (mp->ma_keys->dk_usable <= 0) {
+ /* Need to resize. */
+ if (insertion_resize(interp, mp, 1) < 0) {
+ return -1;
}
- /* Insert into new slot. */
+ }
+
+ uint64_t new_version = _PyDict_NotifyEvent(
+ interp, PyDict_EVENT_ADDED, mp, key, value);
+ mp->ma_keys->dk_version = 0;
+
+ Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
+ dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);
+
+ if (DK_IS_UNICODE(mp->ma_keys)) {
+ PyDictUnicodeEntry *ep;
+ ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
+ STORE_KEY(ep, key);
+ STORE_VALUE(ep, value);
+ }
+ else {
+ PyDictKeyEntry *ep;
+ ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
+ STORE_KEY(ep, key);
+ STORE_VALUE(ep, value);
+ STORE_HASH(ep, hash);
+ }
+ mp->ma_version_tag = new_version;
+ STORE_KEYS_USABLE(mp->ma_keys, mp->ma_keys->dk_usable - 1);
+ STORE_KEYS_NENTRIES(mp->ma_keys, mp->ma_keys->dk_nentries + 1);
+ assert(mp->ma_keys->dk_usable >= 0);
+ return 0;
+}
+
+static Py_ssize_t
+insert_split_key(PyDictKeysObject *keys, PyObject *key, Py_hash_t hash)
+{
+ assert(PyUnicode_CheckExact(key));
+ Py_ssize_t ix;
+
+
+#ifdef Py_GIL_DISABLED
+ ix = unicodekeys_lookup_unicode_threadsafe(keys, key, hash);
+ if (ix >= 0) {
+ return ix;
+ }
+#endif
+
+ LOCK_KEYS(keys);
+ ix = unicodekeys_lookup_unicode(keys, key, hash);
+ if (ix == DKIX_EMPTY && keys->dk_usable > 0) {
+ // Insert into new slot
keys->dk_version = 0;
Py_ssize_t hashpos = find_empty_slot(keys, hash);
ix = keys->dk_nentries;
- PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(keys)[ix];
dictkeys_set_index(keys, hashpos, ix);
- assert(ep->me_key == NULL);
- ep->me_key = Py_NewRef(name);
- keys->dk_usable--;
- keys->dk_nentries++;
+ PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(keys)[ix];
+ STORE_SHARED_KEY(ep->me_key, Py_NewRef(key));
+ split_keys_entry_added(keys);
}
assert (ix < SHARED_KEYS_MAX_SIZE);
+ UNLOCK_KEYS(keys);
return ix;
}
+static void
+insert_split_value(PyInterpreterState *interp, PyDictObject *mp, PyObject *key, PyObject *value, Py_ssize_t ix)
+{
+ assert(PyUnicode_CheckExact(key));
+ ASSERT_DICT_LOCKED(mp);
+ MAINTAIN_TRACKING(mp, key, value);
+ PyObject *old_value = mp->ma_values->values[ix];
+ if (old_value == NULL) {
+ uint64_t new_version = _PyDict_NotifyEvent(interp, PyDict_EVENT_ADDED, mp, key, value);
+ STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
+ _PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
+ STORE_USED(mp, mp->ma_used + 1);
+ mp->ma_version_tag = new_version;
+ }
+ else {
+ uint64_t new_version = _PyDict_NotifyEvent(interp, PyDict_EVENT_MODIFIED, mp, key, value);
+ STORE_SPLIT_VALUE(mp, ix, Py_NewRef(value));
+ mp->ma_version_tag = new_version;
+ // old_value should be DECREFed after GC track checking is done, if not, it could raise a segmentation fault,
+ // when dict only holds the strong reference to value in ep->me_value.
+ Py_DECREF(old_value);
+ }
+ ASSERT_CONSISTENT(mp);
+}
+
/*
Internal routine to insert a new item into the table.
Used both by the internal resize routine and by the public insert routine.
@@ -1239,62 +1832,42 @@ static int
insertdict(PyInterpreterState *interp, PyDictObject *mp,
PyObject *key, Py_hash_t hash, PyObject *value)
{
- PyObject *old_value;
-
- if (DK_IS_UNICODE(mp->ma_keys) && !PyUnicode_CheckExact(key)) {
- if (insertion_resize(interp, mp, 0) < 0)
- goto Fail;
- assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
- }
+ PyObject *old_value = NULL;
+ Py_ssize_t ix;
- Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value);
- if (ix == DKIX_ERROR)
- goto Fail;
+ ASSERT_DICT_LOCKED(mp);
MAINTAIN_TRACKING(mp, key, value);
- if (ix == DKIX_EMPTY) {
- assert(old_value == NULL);
- if (mp->ma_keys->dk_usable <= 0) {
- /* Need to resize. */
- if (insertion_resize(interp, mp, 1) < 0)
- goto Fail;
+ if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) {
+ ix = insert_split_key(mp->ma_keys, key, hash);
+ if (ix != DKIX_EMPTY) {
+ insert_split_value(interp, mp, key, value, ix);
+ Py_DECREF(key);
+ Py_DECREF(value);
+ return 0;
}
+ // No space in shared keys. Go to insert_combined_dict() below.
+ }
+ else {
+ ix = _Py_dict_lookup(mp, key, hash, &old_value);
+ if (ix == DKIX_ERROR)
+ goto Fail;
- uint64_t new_version = _PyDict_NotifyEvent(
- interp, PyDict_EVENT_ADDED, mp, key, value);
- /* Insert into new slot. */
- mp->ma_keys->dk_version = 0;
-
- Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
- dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);
+ }
- if (DK_IS_UNICODE(mp->ma_keys)) {
- PyDictUnicodeEntry *ep;
- ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
- ep->me_key = key;
- if (mp->ma_values) {
- Py_ssize_t index = mp->ma_keys->dk_nentries;
- _PyDictValues_AddToInsertionOrder(mp->ma_values, index);
- assert (mp->ma_values->values[index] == NULL);
- mp->ma_values->values[index] = value;
- }
- else {
- ep->me_value = value;
- }
- }
- else {
- PyDictKeyEntry *ep;
- ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
- ep->me_key = key;
- ep->me_hash = hash;
- ep->me_value = value;
+ if (old_value == NULL) {
+ // insert_combined_dict() will convert from non DICT_KEYS_GENERAL table
+ // into DICT_KEYS_GENERAL table if key is not Unicode.
+ // We don't convert it before _Py_dict_lookup because non-Unicode key
+ // may change generic table into Unicode table.
+ //
+ // NOTE: ix may not be DKIX_EMPTY because split table may have key
+ // without value.
+ if (insert_combined_dict(interp, mp, hash, key, value) < 0) {
+ goto Fail;
}
- mp->ma_used++;
- mp->ma_version_tag = new_version;
- mp->ma_keys->dk_usable--;
- mp->ma_keys->dk_nentries++;
- assert(mp->ma_keys->dk_usable >= 0);
+ STORE_USED(mp, mp->ma_used + 1);
ASSERT_CONSISTENT(mp);
return 0;
}
@@ -1302,22 +1875,20 @@ insertdict(PyInterpreterState *interp, PyDictObject *mp,
if (old_value != value) {
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_MODIFIED, mp, key, value);
- if (_PyDict_HasSplitTable(mp)) {
- mp->ma_values->values[ix] = value;
- if (old_value == NULL) {
- _PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
- mp->ma_used++;
- }
- }
- else {
- assert(old_value != NULL);
- if (DK_IS_UNICODE(mp->ma_keys)) {
- DK_UNICODE_ENTRIES(mp->ma_keys)[ix].me_value = value;
+ assert(old_value != NULL);
+ if (DK_IS_UNICODE(mp->ma_keys)) {
+ if (_PyDict_HasSplitTable(mp)) {
+ STORE_SPLIT_VALUE(mp, ix, value);
}
else {
- DK_ENTRIES(mp->ma_keys)[ix].me_value = value;
+ PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[ix];
+ STORE_VALUE(ep, value);
}
}
+ else {
+ PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix];
+ STORE_VALUE(ep, value);
+ }
mp->ma_version_tag = new_version;
}
Py_XDECREF(old_value); /* which **CAN** re-enter (see issue #22653) */
@@ -1331,13 +1902,14 @@ Fail:
return -1;
}
-// Same to insertdict but specialized for ma_keys = Py_EMPTY_KEYS.
+// Same as insertdict but specialized for ma_keys == Py_EMPTY_KEYS.
// Consumes key and value references.
static int
insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
PyObject *key, Py_hash_t hash, PyObject *value)
{
assert(mp->ma_keys == Py_EMPTY_KEYS);
+ ASSERT_DICT_LOCKED(mp);
int unicode = PyUnicode_CheckExact(key);
PyDictKeysObject *newkeys = new_keys_object(
@@ -1351,28 +1923,33 @@ insert_to_emptydict(PyInterpreterState *interp, PyDictObject *mp,
interp, PyDict_EVENT_ADDED, mp, key, value);
/* We don't decref Py_EMPTY_KEYS here because it is immortal. */
- mp->ma_keys = newkeys;
- mp->ma_values = NULL;
+ assert(mp->ma_values == NULL);
MAINTAIN_TRACKING(mp, key, value);
size_t hashpos = (size_t)hash & (PyDict_MINSIZE-1);
- dictkeys_set_index(mp->ma_keys, hashpos, 0);
+ dictkeys_set_index(newkeys, hashpos, 0);
if (unicode) {
- PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(mp->ma_keys);
+ PyDictUnicodeEntry *ep = DK_UNICODE_ENTRIES(newkeys);
ep->me_key = key;
- ep->me_value = value;
+ STORE_VALUE(ep, value);
}
else {
- PyDictKeyEntry *ep = DK_ENTRIES(mp->ma_keys);
+ PyDictKeyEntry *ep = DK_ENTRIES(newkeys);
ep->me_key = key;
ep->me_hash = hash;
- ep->me_value = value;
+ STORE_VALUE(ep, value);
}
- mp->ma_used++;
+ STORE_USED(mp, mp->ma_used + 1);
mp->ma_version_tag = new_version;
- mp->ma_keys->dk_usable--;
- mp->ma_keys->dk_nentries++;
+ newkeys->dk_usable--;
+ newkeys->dk_nentries++;
+ // We store the keys last so no one can see them in a partially inconsistent
+ // state so that we don't need to switch the keys to being shared yet for
+ // the case where we're inserting from the non-owner thread. We don't use
+ // set_keys here because the transition from empty to non-empty is safe
+ // as the empty keys will never be freed.
+ FT_ATOMIC_STORE_PTR_RELEASE(mp->ma_keys, newkeys);
return 0;
}
@@ -1417,7 +1994,7 @@ actually be smaller than the old one.
If a table is split (its keys and hashes are shared, its values are not),
then the values are temporarily copied into the table, it is resized as
a combined table, then the me_value slots in the old table are NULLed out.
-After resizing a table is always combined.
+After resizing, a table is always combined.
This function supports:
- Unicode split -> Unicode combined or Generic
@@ -1428,9 +2005,11 @@ static int
dictresize(PyInterpreterState *interp, PyDictObject *mp,
uint8_t log2_newsize, int unicode)
{
- PyDictKeysObject *oldkeys;
+ PyDictKeysObject *oldkeys, *newkeys;
PyDictValues *oldvalues;
+ ASSERT_DICT_LOCKED(mp);
+
if (log2_newsize >= SIZEOF_SIZE_T*8) {
PyErr_NoMemory();
return -1;
@@ -1444,30 +2023,31 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
unicode = 0;
}
+ ensure_shared_on_resize(mp);
/* NOTE: Current odict checks mp->ma_keys to detect resize happen.
* So we can't reuse oldkeys even if oldkeys->dk_size == newsize.
* TODO: Try reusing oldkeys when reimplement odict.
*/
/* Allocate a new table. */
- mp->ma_keys = new_keys_object(interp, log2_newsize, unicode);
- if (mp->ma_keys == NULL) {
- mp->ma_keys = oldkeys;
+ newkeys = new_keys_object(interp, log2_newsize, unicode);
+ if (newkeys == NULL) {
return -1;
}
// New table must be large enough.
- assert(mp->ma_keys->dk_usable >= mp->ma_used);
+ assert(newkeys->dk_usable >= mp->ma_used);
Py_ssize_t numentries = mp->ma_used;
if (oldvalues != NULL) {
- PyDictUnicodeEntry *oldentries = DK_UNICODE_ENTRIES(oldkeys);
+ LOCK_KEYS(oldkeys);
+ PyDictUnicodeEntry *oldentries = DK_UNICODE_ENTRIES(oldkeys);
/* Convert split table into new combined table.
* We must incref keys; we can transfer values.
*/
- if (mp->ma_keys->dk_kind == DICT_KEYS_GENERAL) {
+ if (newkeys->dk_kind == DICT_KEYS_GENERAL) {
// split -> generic
- PyDictKeyEntry *newentries = DK_ENTRIES(mp->ma_keys);
+ PyDictKeyEntry *newentries = DK_ENTRIES(newkeys);
for (Py_ssize_t i = 0; i < numentries; i++) {
int index = get_index_from_order(mp, i);
@@ -1477,10 +2057,10 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
newentries[i].me_hash = unicode_get_hash(ep->me_key);
newentries[i].me_value = oldvalues->values[index];
}
- build_indices_generic(mp->ma_keys, newentries, numentries);
+ build_indices_generic(newkeys, newentries, numentries);
}
else { // split -> combined unicode
- PyDictUnicodeEntry *newentries = DK_UNICODE_ENTRIES(mp->ma_keys);
+ PyDictUnicodeEntry *newentries = DK_UNICODE_ENTRIES(newkeys);
for (Py_ssize_t i = 0; i < numentries; i++) {
int index = get_index_from_order(mp, i);
@@ -1489,18 +2069,27 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
newentries[i].me_key = Py_NewRef(ep->me_key);
newentries[i].me_value = oldvalues->values[index];
}
- build_indices_unicode(mp->ma_keys, newentries, numentries);
+ build_indices_unicode(newkeys, newentries, numentries);
+ }
+ UNLOCK_KEYS(oldkeys);
+ set_keys(mp, newkeys);
+ dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
+ set_values(mp, NULL);
+ if (oldvalues->embedded) {
+ assert(oldvalues->embedded == 1);
+ assert(oldvalues->valid == 1);
+ FT_ATOMIC_STORE_UINT8(oldvalues->valid, 0);
+ }
+ else {
+ free_values(oldvalues, IS_DICT_SHARED(mp));
}
- dictkeys_decref(interp, oldkeys);
- mp->ma_values = NULL;
- free_values(oldvalues);
}
else { // oldkeys is combined.
if (oldkeys->dk_kind == DICT_KEYS_GENERAL) {
// generic -> generic
- assert(mp->ma_keys->dk_kind == DICT_KEYS_GENERAL);
+ assert(newkeys->dk_kind == DICT_KEYS_GENERAL);
PyDictKeyEntry *oldentries = DK_ENTRIES(oldkeys);
- PyDictKeyEntry *newentries = DK_ENTRIES(mp->ma_keys);
+ PyDictKeyEntry *newentries = DK_ENTRIES(newkeys);
if (oldkeys->dk_nentries == numentries) {
memcpy(newentries, oldentries, numentries * sizeof(PyDictKeyEntry));
}
@@ -1512,12 +2101,12 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
newentries[i] = *ep++;
}
}
- build_indices_generic(mp->ma_keys, newentries, numentries);
+ build_indices_generic(newkeys, newentries, numentries);
}
else { // oldkeys is combined unicode
PyDictUnicodeEntry *oldentries = DK_UNICODE_ENTRIES(oldkeys);
if (unicode) { // combined unicode -> combined unicode
- PyDictUnicodeEntry *newentries = DK_UNICODE_ENTRIES(mp->ma_keys);
+ PyDictUnicodeEntry *newentries = DK_UNICODE_ENTRIES(newkeys);
if (oldkeys->dk_nentries == numentries && mp->ma_keys->dk_kind == DICT_KEYS_UNICODE) {
memcpy(newentries, oldentries, numentries * sizeof(PyDictUnicodeEntry));
}
@@ -1529,10 +2118,10 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
newentries[i] = *ep++;
}
}
- build_indices_unicode(mp->ma_keys, newentries, numentries);
+ build_indices_unicode(newkeys, newentries, numentries);
}
else { // combined unicode -> generic
- PyDictKeyEntry *newentries = DK_ENTRIES(mp->ma_keys);
+ PyDictKeyEntry *newentries = DK_ENTRIES(newkeys);
PyDictUnicodeEntry *ep = oldentries;
for (Py_ssize_t i = 0; i < numentries; i++) {
while (ep->me_value == NULL)
@@ -1542,41 +2131,24 @@ dictresize(PyInterpreterState *interp, PyDictObject *mp,
newentries[i].me_value = ep->me_value;
ep++;
}
- build_indices_generic(mp->ma_keys, newentries, numentries);
+ build_indices_generic(newkeys, newentries, numentries);
}
}
- // We can not use free_keys_object here because key's reference
- // are moved already.
+ set_keys(mp, newkeys);
+
if (oldkeys != Py_EMPTY_KEYS) {
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal(_PyInterpreterState_GET());
+ _Py_DecRefTotal(_PyThreadState_GET());
#endif
assert(oldkeys->dk_kind != DICT_KEYS_SPLIT);
assert(oldkeys->dk_refcnt == 1);
-#if PyDict_MAXFREELIST > 0
- struct _Py_dict_state *state = get_dict_state(interp);
-#ifdef Py_DEBUG
- // dictresize() must not be called after _PyDict_Fini()
- assert(state->keys_numfree != -1);
-#endif
- if (DK_LOG_SIZE(oldkeys) == PyDict_LOG_MINSIZE &&
- DK_IS_UNICODE(oldkeys) &&
- state->keys_numfree < PyDict_MAXFREELIST)
- {
- state->keys_free_list[state->keys_numfree++] = oldkeys;
- OBJECT_STAT_INC(to_freelist);
- }
- else
-#endif
- {
- PyObject_Free(oldkeys);
- }
+ free_keys_object(oldkeys, IS_DICT_SHARED(mp));
}
}
- mp->ma_keys->dk_usable -= numentries;
- mp->ma_keys->dk_nentries = numentries;
+ STORE_KEYS_USABLE(mp->ma_keys, mp->ma_keys->dk_usable - numentries);
+ STORE_KEYS_NENTRIES(mp->ma_keys, numentries);
ASSERT_CONSISTENT(mp);
return 0;
}
@@ -1644,7 +2216,7 @@ _PyDict_FromItems(PyObject *const *keys, Py_ssize_t keys_offset,
for (Py_ssize_t i = 0; i < length; i++) {
PyObject *key = *ks;
PyObject *value = *vs;
- if (PyDict_SetItem(dict, key, value) < 0) {
+ if (setitem_lock_held((PyDictObject *)dict, key, value) < 0) {
Py_DECREF(dict);
return NULL;
}
@@ -1665,21 +2237,18 @@ _PyDict_FromItems(PyObject *const *keys, Py_ssize_t keys_offset,
* function hits a stack-depth error, which can cause this to return NULL
* even if the key is present.
*/
-PyObject *
-PyDict_GetItem(PyObject *op, PyObject *key)
+static PyObject *
+dict_getitem(PyObject *op, PyObject *key, const char *warnmsg)
{
if (!PyDict_Check(op)) {
return NULL;
}
PyDictObject *mp = (PyDictObject *)op;
- Py_hash_t hash;
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1) {
- PyErr_Clear();
- return NULL;
- }
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ PyErr_FormatUnraisable(warnmsg);
+ return NULL;
}
PyThreadState *tstate = _PyThreadState_GET();
@@ -1693,31 +2262,44 @@ PyDict_GetItem(PyObject *op, PyObject *key)
PyObject *value;
Py_ssize_t ix; (void)ix;
-
PyObject *exc = _PyErr_GetRaisedException(tstate);
+#ifdef Py_GIL_DISABLED
+ ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value);
+ Py_XDECREF(value);
+#else
ix = _Py_dict_lookup(mp, key, hash, &value);
+#endif
/* Ignore any exception raised by the lookup */
+ PyObject *exc2 = _PyErr_Occurred(tstate);
+ if (exc2 && !PyErr_GivenExceptionMatches(exc2, PyExc_KeyError)) {
+ PyErr_FormatUnraisable(warnmsg);
+ }
_PyErr_SetRaisedException(tstate, exc);
-
assert(ix >= 0 || value == NULL);
- return value;
+ return value; // borrowed reference
+}
+
+PyObject *
+PyDict_GetItem(PyObject *op, PyObject *key)
+{
+ return dict_getitem(op, key,
+ "Exception ignored in PyDict_GetItem(); consider using "
+ "PyDict_GetItemRef() or PyDict_GetItemWithError()");
}
Py_ssize_t
_PyDict_LookupIndex(PyDictObject *mp, PyObject *key)
{
+ // TODO: Thread safety
PyObject *value;
assert(PyDict_CheckExact((PyObject*)mp));
assert(PyUnicode_CheckExact(key));
- Py_hash_t hash = unicode_get_hash(key);
+ Py_hash_t hash = _PyObject_HashFast(key);
if (hash == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1) {
- return -1;
- }
+ return -1;
}
return _Py_dict_lookup(mp, key, hash, &value);
@@ -1739,9 +2321,112 @@ _PyDict_GetItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
return NULL;
}
+#ifdef Py_GIL_DISABLED
+ ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value);
+ Py_XDECREF(value);
+#else
ix = _Py_dict_lookup(mp, key, hash, &value);
+#endif
assert(ix >= 0 || value == NULL);
- return value;
+ return value; // borrowed reference
+}
+
+/* Gets an item and provides a new reference if the value is present.
+ * Returns 1 if the key is present, 0 if the key is missing, and -1 if an
+ * exception occurred.
+*/
+int
+_PyDict_GetItemRef_KnownHash_LockHeld(PyDictObject *op, PyObject *key,
+ Py_hash_t hash, PyObject **result)
+{
+ PyObject *value;
+ Py_ssize_t ix = _Py_dict_lookup(op, key, hash, &value);
+ assert(ix >= 0 || value == NULL);
+ if (ix == DKIX_ERROR) {
+ *result = NULL;
+ return -1;
+ }
+ if (value == NULL) {
+ *result = NULL;
+ return 0; // missing key
+ }
+ *result = Py_NewRef(value);
+ return 1; // key is present
+}
+
+/* Gets an item and provides a new reference if the value is present.
+ * Returns 1 if the key is present, 0 if the key is missing, and -1 if an
+ * exception occurred.
+*/
+int
+_PyDict_GetItemRef_KnownHash(PyDictObject *op, PyObject *key, Py_hash_t hash, PyObject **result)
+{
+ PyObject *value;
+#ifdef Py_GIL_DISABLED
+ Py_ssize_t ix = _Py_dict_lookup_threadsafe(op, key, hash, &value);
+#else
+ Py_ssize_t ix = _Py_dict_lookup(op, key, hash, &value);
+#endif
+ assert(ix >= 0 || value == NULL);
+ if (ix == DKIX_ERROR) {
+ *result = NULL;
+ return -1;
+ }
+ if (value == NULL) {
+ *result = NULL;
+ return 0; // missing key
+ }
+#ifdef Py_GIL_DISABLED
+ *result = value;
+#else
+ *result = Py_NewRef(value);
+#endif
+ return 1; // key is present
+}
+
+int
+PyDict_GetItemRef(PyObject *op, PyObject *key, PyObject **result)
+{
+ if (!PyDict_Check(op)) {
+ PyErr_BadInternalCall();
+ *result = NULL;
+ return -1;
+ }
+
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ *result = NULL;
+ return -1;
+ }
+
+ return _PyDict_GetItemRef_KnownHash((PyDictObject *)op, key, hash, result);
+}
+
+int
+_PyDict_GetItemRef_Unicode_LockHeld(PyDictObject *op, PyObject *key, PyObject **result)
+{
+ ASSERT_DICT_LOCKED(op);
+ assert(PyUnicode_CheckExact(key));
+
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ *result = NULL;
+ return -1;
+ }
+
+ PyObject *value;
+ Py_ssize_t ix = _Py_dict_lookup(op, key, hash, &value);
+ assert(ix >= 0 || value == NULL);
+ if (ix == DKIX_ERROR) {
+ *result = NULL;
+ return -1;
+ }
+ if (value == NULL) {
+ *result = NULL;
+ return 0; // missing key
+ }
+ *result = Py_NewRef(value);
+ return 1; // key is present
}
/* Variant of PyDict_GetItem() that doesn't suppress exceptions.
@@ -1760,17 +2445,19 @@ PyDict_GetItemWithError(PyObject *op, PyObject *key)
PyErr_BadInternalCall();
return NULL;
}
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1)
- {
- hash = PyObject_Hash(key);
- if (hash == -1) {
- return NULL;
- }
+ hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return NULL;
}
+#ifdef Py_GIL_DISABLED
+ ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value);
+ Py_XDECREF(value);
+#else
ix = _Py_dict_lookup(mp, key, hash, &value);
+#endif
assert(ix >= 0 || value == NULL);
- return value;
+ return value; // borrowed reference
}
PyObject *
@@ -1781,7 +2468,7 @@ _PyDict_GetItemWithError(PyObject *dp, PyObject *kv)
if (hash == -1) {
return NULL;
}
- return _PyDict_GetItem_KnownHash(dp, kv, hash);
+ return _PyDict_GetItem_KnownHash(dp, kv, hash); // borrowed reference
}
PyObject *
@@ -1793,7 +2480,7 @@ _PyDict_GetItemIdWithError(PyObject *dp, _Py_Identifier *key)
return NULL;
Py_hash_t hash = unicode_get_hash(kv);
assert (hash != -1); /* interned strings have their hash value initialised */
- return _PyDict_GetItem_KnownHash(dp, kv, hash);
+ return _PyDict_GetItem_KnownHash(dp, kv, hash); // borrowed reference
}
PyObject *
@@ -1818,6 +2505,8 @@ _PyDict_GetItemStringWithError(PyObject *v, const char *key)
* Raise an exception and return NULL if an error occurred (ex: computing the
* key hash failed, key comparison failed, ...). Return NULL if the key doesn't
* exist. Return the value if the key exists.
+ *
+ * Returns a new reference.
*/
PyObject *
_PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
@@ -1826,42 +2515,42 @@ _PyDict_LoadGlobal(PyDictObject *globals, PyDictObject *builtins, PyObject *key)
Py_hash_t hash;
PyObject *value;
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return NULL;
+ hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return NULL;
}
/* namespace 1: globals */
- ix = _Py_dict_lookup(globals, key, hash, &value);
+ ix = _Py_dict_lookup_threadsafe(globals, key, hash, &value);
if (ix == DKIX_ERROR)
return NULL;
if (ix != DKIX_EMPTY && value != NULL)
return value;
/* namespace 2: builtins */
- ix = _Py_dict_lookup(builtins, key, hash, &value);
+ ix = _Py_dict_lookup_threadsafe(builtins, key, hash, &value);
assert(ix >= 0 || value == NULL);
return value;
}
/* Consumes references to key and value */
-int
-_PyDict_SetItem_Take2(PyDictObject *mp, PyObject *key, PyObject *value)
+static int
+setitem_take2_lock_held(PyDictObject *mp, PyObject *key, PyObject *value)
{
+ ASSERT_DICT_LOCKED(mp);
+
assert(key);
assert(value);
assert(PyDict_Check(mp));
- Py_hash_t hash;
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1) {
- Py_DECREF(key);
- Py_DECREF(value);
- return -1;
- }
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ Py_DECREF(key);
+ Py_DECREF(value);
+ return -1;
}
+
PyInterpreterState *interp = _PyInterpreterState_GET();
+
if (mp->ma_keys == Py_EMPTY_KEYS) {
return insert_to_emptydict(interp, mp, key, hash, value);
}
@@ -1869,6 +2558,16 @@ _PyDict_SetItem_Take2(PyDictObject *mp, PyObject *key, PyObject *value)
return insertdict(interp, mp, key, hash, value);
}
+int
+_PyDict_SetItem_Take2(PyDictObject *mp, PyObject *key, PyObject *value)
+{
+ int res;
+ Py_BEGIN_CRITICAL_SECTION(mp);
+ res = setitem_take2_lock_held(mp, key, value);
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
/* CAUTION: PyDict_SetItem() must guarantee that it won't resize the
* dictionary if it's merely replacing the value for an existing key.
* This means that it's safe to loop over a dictionary with PyDict_Next()
@@ -1888,12 +2587,32 @@ PyDict_SetItem(PyObject *op, PyObject *key, PyObject *value)
Py_NewRef(key), Py_NewRef(value));
}
+static int
+setitem_lock_held(PyDictObject *mp, PyObject *key, PyObject *value)
+{
+ assert(key);
+ assert(value);
+ return setitem_take2_lock_held(mp,
+ Py_NewRef(key), Py_NewRef(value));
+}
+
+
int
-_PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value,
- Py_hash_t hash)
+_PyDict_SetItem_KnownHash_LockHeld(PyDictObject *mp, PyObject *key, PyObject *value,
+ Py_hash_t hash)
{
- PyDictObject *mp;
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (mp->ma_keys == Py_EMPTY_KEYS) {
+ return insert_to_emptydict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value));
+ }
+ /* insertdict() handles any resizing that might be necessary */
+ return insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value));
+}
+int
+_PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value,
+ Py_hash_t hash)
+{
if (!PyDict_Check(op)) {
PyErr_BadInternalCall();
return -1;
@@ -1901,46 +2620,48 @@ _PyDict_SetItem_KnownHash(PyObject *op, PyObject *key, PyObject *value,
assert(key);
assert(value);
assert(hash != -1);
- mp = (PyDictObject *)op;
- PyInterpreterState *interp = _PyInterpreterState_GET();
- if (mp->ma_keys == Py_EMPTY_KEYS) {
- return insert_to_emptydict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value));
- }
- /* insertdict() handles any resizing that might be necessary */
- return insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value));
+ int res;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ res = _PyDict_SetItem_KnownHash_LockHeld((PyDictObject *)op, key, value, hash);
+ Py_END_CRITICAL_SECTION();
+ return res;
}
static void
delete_index_from_values(PyDictValues *values, Py_ssize_t ix)
{
- uint8_t *size_ptr = ((uint8_t *)values)-2;
- int size = *size_ptr;
+ uint8_t *array = get_insertion_order_array(values);
+ int size = values->size;
+ assert(size <= values->capacity);
int i;
- for (i = 1; size_ptr[-i] != ix; i++) {
- assert(i <= size);
+ for (i = 0; array[i] != ix; i++) {
+ assert(i < size);
}
- assert(i <= size);
+ assert(i < size);
+ size--;
for (; i < size; i++) {
- size_ptr[-i] = size_ptr[-i-1];
+ array[i] = array[i+1];
}
- *size_ptr = size -1;
+ values->size = size;
}
-static int
+static void
delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
PyObject *old_value, uint64_t new_version)
{
PyObject *old_key;
+ ASSERT_DICT_LOCKED(mp);
+
Py_ssize_t hashpos = lookdict_index(mp->ma_keys, hash, ix);
assert(hashpos >= 0);
- mp->ma_used--;
+ STORE_USED(mp, mp->ma_used - 1);
mp->ma_version_tag = new_version;
- if (mp->ma_values) {
+ if (_PyDict_HasSplitTable(mp)) {
assert(old_value == mp->ma_values->values[ix]);
- mp->ma_values->values[ix] = NULL;
+ STORE_SPLIT_VALUE(mp, ix, NULL);
assert(ix < SHARED_KEYS_MAX_SIZE);
/* Update order */
delete_index_from_values(mp->ma_values, ix);
@@ -1952,40 +2673,37 @@ delitem_common(PyDictObject *mp, Py_hash_t hash, Py_ssize_t ix,
if (DK_IS_UNICODE(mp->ma_keys)) {
PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[ix];
old_key = ep->me_key;
- ep->me_key = NULL;
- ep->me_value = NULL;
+ STORE_KEY(ep, NULL);
+ STORE_VALUE(ep, NULL);
}
else {
PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[ix];
old_key = ep->me_key;
- ep->me_key = NULL;
- ep->me_value = NULL;
- ep->me_hash = 0;
+ STORE_KEY(ep, NULL);
+ STORE_VALUE(ep, NULL);
+ STORE_HASH(ep, 0);
}
Py_DECREF(old_key);
}
Py_DECREF(old_value);
ASSERT_CONSISTENT(mp);
- return 0;
}
int
PyDict_DelItem(PyObject *op, PyObject *key)
{
- Py_hash_t hash;
assert(key);
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return -1;
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return -1;
}
return _PyDict_DelItem_KnownHash(op, key, hash);
}
-int
-_PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
+static int
+delitem_knownhash_lock_held(PyObject *op, PyObject *key, Py_hash_t hash)
{
Py_ssize_t ix;
PyDictObject *mp;
@@ -1995,6 +2713,9 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
PyErr_BadInternalCall();
return -1;
}
+
+ ASSERT_DICT_LOCKED(op);
+
assert(key);
assert(hash != -1);
mp = (PyDictObject *)op;
@@ -2009,66 +2730,103 @@ _PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, mp, key, NULL);
- return delitem_common(mp, hash, ix, old_value, new_version);
+ delitem_common(mp, hash, ix, old_value, new_version);
+ return 0;
}
-/* This function promises that the predicate -> deletion sequence is atomic
- * (i.e. protected by the GIL), assuming the predicate itself doesn't
- * release the GIL.
- */
int
-_PyDict_DelItemIf(PyObject *op, PyObject *key,
- int (*predicate)(PyObject *value))
+_PyDict_DelItem_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
{
- Py_ssize_t hashpos, ix;
+ int res;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ res = delitem_knownhash_lock_held(op, key, hash);
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
+static int
+delitemif_lock_held(PyObject *op, PyObject *key,
+ int (*predicate)(PyObject *value, void *arg),
+ void *arg)
+{
+ Py_ssize_t ix;
PyDictObject *mp;
Py_hash_t hash;
PyObject *old_value;
int res;
- if (!PyDict_Check(op)) {
- PyErr_BadInternalCall();
- return -1;
- }
+ ASSERT_DICT_LOCKED(op);
+
assert(key);
hash = PyObject_Hash(key);
if (hash == -1)
return -1;
mp = (PyDictObject *)op;
ix = _Py_dict_lookup(mp, key, hash, &old_value);
- if (ix == DKIX_ERROR)
+ if (ix == DKIX_ERROR) {
return -1;
+ }
if (ix == DKIX_EMPTY || old_value == NULL) {
- _PyErr_SetKeyError(key);
- return -1;
+ return 0;
}
- res = predicate(old_value);
+ res = predicate(old_value, arg);
if (res == -1)
return -1;
- hashpos = lookdict_index(mp->ma_keys, hash, ix);
- assert(hashpos >= 0);
-
if (res > 0) {
PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, mp, key, NULL);
- return delitem_common(mp, hashpos, ix, old_value, new_version);
+ delitem_common(mp, hash, ix, old_value, new_version);
+ return 1;
} else {
return 0;
}
}
+/* This function promises that the predicate -> deletion sequence is atomic
+ * (i.e. protected by the GIL or the per-dict mutex in free threaded builds),
+ * assuming the predicate itself doesn't release the GIL (or cause re-entrancy
+ * which would release the per-dict mutex)
+ */
+int
+_PyDict_DelItemIf(PyObject *op, PyObject *key,
+ int (*predicate)(PyObject *value, void *arg),
+ void *arg)
+{
+ assert(PyDict_Check(op));
+ int res;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ res = delitemif_lock_held(op, key, predicate, arg);
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+static void
+clear_embedded_values(PyDictValues *values, Py_ssize_t nentries)
+{
+ PyObject *refs[SHARED_KEYS_MAX_SIZE];
+ assert(nentries <= SHARED_KEYS_MAX_SIZE);
+ for (Py_ssize_t i = 0; i < nentries; i++) {
+ refs[i] = values->values[i];
+ values->values[i] = NULL;
+ }
+ values->size = 0;
+ for (Py_ssize_t i = 0; i < nentries; i++) {
+ Py_XDECREF(refs[i]);
+ }
+}
-void
-PyDict_Clear(PyObject *op)
+static void
+clear_lock_held(PyObject *op)
{
PyDictObject *mp;
PyDictKeysObject *oldkeys;
PyDictValues *oldvalues;
Py_ssize_t i, n;
+ ASSERT_DICT_LOCKED(op);
+
if (!PyDict_Check(op))
return;
mp = ((PyDictObject *)op);
@@ -2081,26 +2839,39 @@ PyDict_Clear(PyObject *op)
PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_CLEARED, mp, NULL, NULL);
- dictkeys_incref(Py_EMPTY_KEYS);
- mp->ma_keys = Py_EMPTY_KEYS;
- mp->ma_values = NULL;
- mp->ma_used = 0;
+ // We don't inc ref empty keys because they're immortal
+ ensure_shared_on_resize(mp);
mp->ma_version_tag = new_version;
- /* ...then clear the keys and values */
- if (oldvalues != NULL) {
- n = oldkeys->dk_nentries;
- for (i = 0; i < n; i++)
- Py_CLEAR(oldvalues->values[i]);
- free_values(oldvalues);
- dictkeys_decref(interp, oldkeys);
+ STORE_USED(mp, 0);
+ if (oldvalues == NULL) {
+ set_keys(mp, Py_EMPTY_KEYS);
+ assert(oldkeys->dk_refcnt == 1);
+ dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
+ }
+ else if (oldvalues->embedded) {
+ clear_embedded_values(oldvalues, oldkeys->dk_nentries);
}
else {
- assert(oldkeys->dk_refcnt == 1);
- dictkeys_decref(interp, oldkeys);
+ set_values(mp, NULL);
+ set_keys(mp, Py_EMPTY_KEYS);
+ n = oldkeys->dk_nentries;
+ for (i = 0; i < n; i++) {
+ Py_CLEAR(oldvalues->values[i]);
+ }
+ free_values(oldvalues, IS_DICT_SHARED(mp));
+ dictkeys_decref(interp, oldkeys, false);
}
ASSERT_CONSISTENT(mp);
}
+void
+PyDict_Clear(PyObject *op)
+{
+ Py_BEGIN_CRITICAL_SECTION(op);
+ clear_lock_held(op);
+ Py_END_CRITICAL_SECTION();
+}
+
/* Internal version of PyDict_Next that returns a hash value in addition
* to the key and value.
* Return 1 on success, return 0 when the reached the end of the dictionary
@@ -2117,16 +2888,16 @@ _PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey,
if (!PyDict_Check(op))
return 0;
+
mp = (PyDictObject *)op;
i = *ppos;
- if (mp->ma_values) {
+ if (_PyDict_HasSplitTable(mp)) {
assert(mp->ma_used <= SHARED_KEYS_MAX_SIZE);
if (i < 0 || i >= mp->ma_used)
return 0;
int index = get_index_from_order(mp, i);
value = mp->ma_values->values[index];
-
- key = DK_UNICODE_ENTRIES(mp->ma_keys)[index].me_key;
+ key = LOAD_SHARED_KEY(DK_UNICODE_ENTRIES(mp->ma_keys)[index].me_key);
hash = unicode_get_hash(key);
assert(value != NULL);
}
@@ -2193,62 +2964,179 @@ PyDict_Next(PyObject *op, Py_ssize_t *ppos, PyObject **pkey, PyObject **pvalue)
return _PyDict_Next(op, ppos, pkey, pvalue, NULL);
}
+
/* Internal version of dict.pop(). */
-PyObject *
-_PyDict_Pop_KnownHash(PyObject *dict, PyObject *key, Py_hash_t hash, PyObject *deflt)
+int
+_PyDict_Pop_KnownHash(PyDictObject *mp, PyObject *key, Py_hash_t hash,
+ PyObject **result)
{
- Py_ssize_t ix;
- PyObject *old_value;
- PyDictObject *mp;
- PyInterpreterState *interp = _PyInterpreterState_GET();
+ assert(PyDict_Check(mp));
- assert(PyDict_Check(dict));
- mp = (PyDictObject *)dict;
+ ASSERT_DICT_LOCKED(mp);
if (mp->ma_used == 0) {
- if (deflt) {
- return Py_NewRef(deflt);
+ if (result) {
+ *result = NULL;
}
- _PyErr_SetKeyError(key);
- return NULL;
+ return 0;
}
- ix = _Py_dict_lookup(mp, key, hash, &old_value);
- if (ix == DKIX_ERROR)
- return NULL;
+
+ PyObject *old_value;
+ Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &old_value);
+ if (ix == DKIX_ERROR) {
+ if (result) {
+ *result = NULL;
+ }
+ return -1;
+ }
+
if (ix == DKIX_EMPTY || old_value == NULL) {
- if (deflt) {
- return Py_NewRef(deflt);
+ if (result) {
+ *result = NULL;
}
- _PyErr_SetKeyError(key);
- return NULL;
+ return 0;
}
+
assert(old_value != NULL);
+ PyInterpreterState *interp = _PyInterpreterState_GET();
uint64_t new_version = _PyDict_NotifyEvent(
interp, PyDict_EVENT_DELETED, mp, key, NULL);
delitem_common(mp, hash, ix, Py_NewRef(old_value), new_version);
ASSERT_CONSISTENT(mp);
- return old_value;
+ if (result) {
+ *result = old_value;
+ }
+ else {
+ Py_DECREF(old_value);
+ }
+ return 1;
+}
+
+static int
+pop_lock_held(PyObject *op, PyObject *key, PyObject **result)
+{
+ ASSERT_DICT_LOCKED(op);
+
+ if (!PyDict_Check(op)) {
+ if (result) {
+ *result = NULL;
+ }
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ PyDictObject *dict = (PyDictObject *)op;
+
+ if (dict->ma_used == 0) {
+ if (result) {
+ *result = NULL;
+ }
+ return 0;
+ }
+
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ if (result) {
+ *result = NULL;
+ }
+ return -1;
+ }
+ return _PyDict_Pop_KnownHash(dict, key, hash, result);
+}
+
+int
+PyDict_Pop(PyObject *op, PyObject *key, PyObject **result)
+{
+ int err;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ err = pop_lock_held(op, key, result);
+ Py_END_CRITICAL_SECTION();
+
+ return err;
+}
+
+
+int
+PyDict_PopString(PyObject *op, const char *key, PyObject **result)
+{
+ PyObject *key_obj = PyUnicode_FromString(key);
+ if (key_obj == NULL) {
+ if (result != NULL) {
+ *result = NULL;
+ }
+ return -1;
+ }
+
+ int res = PyDict_Pop(op, key_obj, result);
+ Py_DECREF(key_obj);
+ return res;
}
+
PyObject *
-_PyDict_Pop(PyObject *dict, PyObject *key, PyObject *deflt)
+_PyDict_Pop(PyObject *dict, PyObject *key, PyObject *default_value)
+{
+ PyObject *result;
+ if (PyDict_Pop(dict, key, &result) == 0) {
+ if (default_value != NULL) {
+ return Py_NewRef(default_value);
+ }
+ _PyErr_SetKeyError(key);
+ return NULL;
+ }
+ return result;
+}
+
+static PyDictObject *
+dict_dict_fromkeys(PyInterpreterState *interp, PyDictObject *mp,
+ PyObject *iterable, PyObject *value)
{
+ PyObject *oldvalue;
+ Py_ssize_t pos = 0;
+ PyObject *key;
Py_hash_t hash;
+ int unicode = DK_IS_UNICODE(((PyDictObject*)iterable)->ma_keys);
+ uint8_t new_size = Py_MAX(
+ estimate_log2_keysize(PyDict_GET_SIZE(iterable)),
+ DK_LOG_SIZE(mp->ma_keys));
+ if (dictresize(interp, mp, new_size, unicode)) {
+ Py_DECREF(mp);
+ return NULL;
+ }
- if (((PyDictObject *)dict)->ma_used == 0) {
- if (deflt) {
- return Py_NewRef(deflt);
+ while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) {
+ if (insertdict(interp, mp,
+ Py_NewRef(key), hash, Py_NewRef(value))) {
+ Py_DECREF(mp);
+ return NULL;
}
- _PyErr_SetKeyError(key);
+ }
+ return mp;
+}
+
+static PyDictObject *
+dict_set_fromkeys(PyInterpreterState *interp, PyDictObject *mp,
+ PyObject *iterable, PyObject *value)
+{
+ Py_ssize_t pos = 0;
+ PyObject *key;
+ Py_hash_t hash;
+ uint8_t new_size = Py_MAX(
+ estimate_log2_keysize(PySet_GET_SIZE(iterable)),
+ DK_LOG_SIZE(mp->ma_keys));
+ if (dictresize(interp, mp, new_size, 0)) {
+ Py_DECREF(mp);
return NULL;
}
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
+
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(iterable);
+ while (_PySet_NextEntryRef(iterable, &pos, &key, &hash)) {
+ if (insertdict(interp, mp, key, hash, Py_NewRef(value))) {
+ Py_DECREF(mp);
return NULL;
+ }
}
- return _PyDict_Pop_KnownHash(dict, key, hash, deflt);
+ return mp;
}
/* Internal version of dict.from_keys(). It is subclass-friendly. */
@@ -2265,49 +3153,22 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
if (d == NULL)
return NULL;
- if (PyDict_CheckExact(d) && ((PyDictObject *)d)->ma_used == 0) {
+
+ if (PyDict_CheckExact(d)) {
if (PyDict_CheckExact(iterable)) {
PyDictObject *mp = (PyDictObject *)d;
- PyObject *oldvalue;
- Py_ssize_t pos = 0;
- PyObject *key;
- Py_hash_t hash;
-
- int unicode = DK_IS_UNICODE(((PyDictObject*)iterable)->ma_keys);
- if (dictresize(interp, mp,
- estimate_log2_keysize(PyDict_GET_SIZE(iterable)),
- unicode)) {
- Py_DECREF(d);
- return NULL;
- }
- while (_PyDict_Next(iterable, &pos, &key, &oldvalue, &hash)) {
- if (insertdict(interp, mp,
- Py_NewRef(key), hash, Py_NewRef(value))) {
- Py_DECREF(d);
- return NULL;
- }
- }
+ Py_BEGIN_CRITICAL_SECTION2(d, iterable);
+ d = (PyObject *)dict_dict_fromkeys(interp, mp, iterable, value);
+ Py_END_CRITICAL_SECTION2();
return d;
}
- if (PyAnySet_CheckExact(iterable)) {
+ else if (PyAnySet_CheckExact(iterable)) {
PyDictObject *mp = (PyDictObject *)d;
- Py_ssize_t pos = 0;
- PyObject *key;
- Py_hash_t hash;
-
- if (dictresize(interp, mp,
- estimate_log2_keysize(PySet_GET_SIZE(iterable)), 0)) {
- Py_DECREF(d);
- return NULL;
- }
- while (_PySet_NextEntry(iterable, &pos, &key, &hash)) {
- if (insertdict(interp, mp, Py_NewRef(key), hash, Py_NewRef(value))) {
- Py_DECREF(d);
- return NULL;
- }
- }
+ Py_BEGIN_CRITICAL_SECTION2(d, iterable);
+ d = (PyObject *)dict_set_fromkeys(interp, mp, iterable, value);
+ Py_END_CRITICAL_SECTION2();
return d;
}
}
@@ -2319,12 +3180,17 @@ _PyDict_FromKeys(PyObject *cls, PyObject *iterable, PyObject *value)
}
if (PyDict_CheckExact(d)) {
+ Py_BEGIN_CRITICAL_SECTION(d);
while ((key = PyIter_Next(it)) != NULL) {
- status = PyDict_SetItem(d, key, value);
+ status = setitem_lock_held((PyDictObject *)d, key, value);
Py_DECREF(key);
- if (status < 0)
- goto Fail;
+ if (status < 0) {
+ assert(PyErr_Occurred());
+ goto dict_iter_exit;
+ }
}
+dict_iter_exit:;
+ Py_END_CRITICAL_SECTION();
} else {
while ((key = PyIter_Next(it)) != NULL) {
status = PyObject_SetItem(d, key, value);
@@ -2348,17 +3214,15 @@ Fail:
/* Methods */
static void
-dict_dealloc(PyDictObject *mp)
+dict_dealloc(PyObject *self)
{
+ PyDictObject *mp = (PyDictObject *)self;
PyInterpreterState *interp = _PyInterpreterState_GET();
- assert(Py_REFCNT(mp) == 0);
- Py_SET_REFCNT(mp, 1);
+ _PyObject_ResurrectStart(self);
_PyDict_NotifyEvent(interp, PyDict_EVENT_DEALLOCATED, mp, NULL, NULL);
- if (Py_REFCNT(mp) > 1) {
- Py_SET_REFCNT(mp, Py_REFCNT(mp) - 1);
+ if (_PyObject_ResurrectEnd(self)) {
return;
}
- Py_SET_REFCNT(mp, 0);
PyDictValues *values = mp->ma_values;
PyDictKeysObject *keys = mp->ma_keys;
Py_ssize_t i, n;
@@ -2367,24 +3231,23 @@ dict_dealloc(PyDictObject *mp)
PyObject_GC_UnTrack(mp);
Py_TRASHCAN_BEGIN(mp, dict_dealloc)
if (values != NULL) {
- for (i = 0, n = mp->ma_keys->dk_nentries; i < n; i++) {
- Py_XDECREF(values->values[i]);
+ if (values->embedded == 0) {
+ for (i = 0, n = values->capacity; i < n; i++) {
+ Py_XDECREF(values->values[i]);
+ }
+ free_values(values, false);
}
- free_values(values);
- dictkeys_decref(interp, keys);
+ dictkeys_decref(interp, keys, false);
}
else if (keys != NULL) {
assert(keys->dk_refcnt == 1 || keys == Py_EMPTY_KEYS);
- dictkeys_decref(interp, keys);
+ dictkeys_decref(interp, keys, false);
}
-#if PyDict_MAXFREELIST > 0
- struct _Py_dict_state *state = get_dict_state(interp);
-#ifdef Py_DEBUG
- // new_dict() must not be called after _PyDict_Fini()
- assert(state->numfree != -1);
-#endif
- if (state->numfree < PyDict_MAXFREELIST && Py_IS_TYPE(mp, &PyDict_Type)) {
- state->free_list[state->numfree++] = mp;
+#ifdef WITH_FREELISTS
+ struct _Py_dict_freelist *freelist = get_dict_freelist();
+ if (freelist->numfree < PyDict_MAXFREELIST && freelist->numfree >=0 &&
+ Py_IS_TYPE(mp, &PyDict_Type)) {
+ freelist->items[freelist->numfree++] = mp;
OBJECT_STAT_INC(to_freelist);
}
else
@@ -2397,13 +3260,16 @@ dict_dealloc(PyDictObject *mp)
static PyObject *
-dict_repr(PyDictObject *mp)
+dict_repr_lock_held(PyObject *self)
{
+ PyDictObject *mp = (PyDictObject *)self;
Py_ssize_t i;
PyObject *key = NULL, *value = NULL;
_PyUnicodeWriter writer;
int first;
+ ASSERT_DICT_LOCKED(mp);
+
i = Py_ReprEnter((PyObject *)mp);
if (i != 0) {
return i > 0 ? PyUnicode_FromString("{...}") : NULL;
@@ -2426,7 +3292,7 @@ dict_repr(PyDictObject *mp)
Note that repr may mutate the dict. */
i = 0;
first = 1;
- while (PyDict_Next((PyObject *)mp, &i, &key, &value)) {
+ while (_PyDict_Next((PyObject *)mp, &i, &key, &value, NULL)) {
PyObject *s;
int res;
@@ -2479,25 +3345,35 @@ error:
return NULL;
}
+static PyObject *
+dict_repr(PyObject *self)
+{
+ PyObject *res;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ res = dict_repr_lock_held(self);
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
static Py_ssize_t
-dict_length(PyDictObject *mp)
+dict_length(PyObject *self)
{
- return mp->ma_used;
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(((PyDictObject *)self)->ma_used);
}
static PyObject *
-dict_subscript(PyDictObject *mp, PyObject *key)
+dict_subscript(PyObject *self, PyObject *key)
{
+ PyDictObject *mp = (PyDictObject *)self;
Py_ssize_t ix;
Py_hash_t hash;
PyObject *value;
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return NULL;
+ hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return NULL;
}
- ix = _Py_dict_lookup(mp, key, hash, &value);
+ ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value);
if (ix == DKIX_ERROR)
return NULL;
if (ix == DKIX_EMPTY || value == NULL) {
@@ -2517,27 +3393,34 @@ dict_subscript(PyDictObject *mp, PyObject *key)
_PyErr_SetKeyError(key);
return NULL;
}
- return Py_NewRef(value);
+ return value;
}
static int
-dict_ass_sub(PyDictObject *mp, PyObject *v, PyObject *w)
+dict_ass_sub(PyObject *mp, PyObject *v, PyObject *w)
{
if (w == NULL)
- return PyDict_DelItem((PyObject *)mp, v);
+ return PyDict_DelItem(mp, v);
else
- return PyDict_SetItem((PyObject *)mp, v, w);
+ return PyDict_SetItem(mp, v, w);
}
static PyMappingMethods dict_as_mapping = {
- (lenfunc)dict_length, /*mp_length*/
- (binaryfunc)dict_subscript, /*mp_subscript*/
- (objobjargproc)dict_ass_sub, /*mp_ass_subscript*/
+ dict_length, /*mp_length*/
+ dict_subscript, /*mp_subscript*/
+ dict_ass_sub, /*mp_ass_subscript*/
};
static PyObject *
-dict_keys(PyDictObject *mp)
+keys_lock_held(PyObject *dict)
{
+ ASSERT_DICT_LOCKED(dict);
+
+ if (dict == NULL || !PyDict_Check(dict)) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+ PyDictObject *mp = (PyDictObject *)dict;
PyObject *v;
Py_ssize_t n;
@@ -2566,9 +3449,27 @@ dict_keys(PyDictObject *mp)
return v;
}
+PyObject *
+PyDict_Keys(PyObject *dict)
+{
+ PyObject *res;
+ Py_BEGIN_CRITICAL_SECTION(dict);
+ res = keys_lock_held(dict);
+ Py_END_CRITICAL_SECTION();
+
+ return res;
+}
+
static PyObject *
-dict_values(PyDictObject *mp)
+values_lock_held(PyObject *dict)
{
+ ASSERT_DICT_LOCKED(dict);
+
+ if (dict == NULL || !PyDict_Check(dict)) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+ PyDictObject *mp = (PyDictObject *)dict;
PyObject *v;
Py_ssize_t n;
@@ -2597,9 +3498,26 @@ dict_values(PyDictObject *mp)
return v;
}
+PyObject *
+PyDict_Values(PyObject *dict)
+{
+ PyObject *res;
+ Py_BEGIN_CRITICAL_SECTION(dict);
+ res = values_lock_held(dict);
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
static PyObject *
-dict_items(PyDictObject *mp)
+items_lock_held(PyObject *dict)
{
+ ASSERT_DICT_LOCKED(dict);
+
+ if (dict == NULL || !PyDict_Check(dict)) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+ PyDictObject *mp = (PyDictObject *)dict;
PyObject *v;
Py_ssize_t i, n;
PyObject *item;
@@ -2643,6 +3561,17 @@ dict_items(PyDictObject *mp)
return v;
}
+PyObject *
+PyDict_Items(PyObject *dict)
+{
+ PyObject *res;
+ Py_BEGIN_CRITICAL_SECTION(dict);
+ res = items_lock_held(dict);
+ Py_END_CRITICAL_SECTION();
+
+ return res;
+}
+
/*[clinic input]
@classmethod
dict.fromkeys
@@ -2667,12 +3596,11 @@ dict_update_arg(PyObject *self, PyObject *arg)
if (PyDict_CheckExact(arg)) {
return PyDict_Merge(self, arg, 1);
}
- PyObject *func;
- if (_PyObject_LookupAttr(arg, &_Py_ID(keys), &func) < 0) {
+ int has_keys = PyObject_HasAttrWithError(arg, &_Py_ID(keys));
+ if (has_keys < 0) {
return -1;
}
- if (func != NULL) {
- Py_DECREF(func);
+ if (has_keys) {
return PyDict_Merge(self, arg, 1);
}
return PyDict_MergeFromSeq2(self, arg, 1);
@@ -2722,8 +3650,8 @@ dict_update(PyObject *self, PyObject *args, PyObject *kwds)
producing iterable objects of length 2.
*/
-int
-PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
+static int
+merge_from_seq2_lock_held(PyObject *d, PyObject *seq2, int override)
{
PyObject *it; /* iter(seq2) */
Py_ssize_t i; /* index into seq2 of current element */
@@ -2775,14 +3703,14 @@ PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
Py_INCREF(key);
Py_INCREF(value);
if (override) {
- if (PyDict_SetItem(d, key, value) < 0) {
+ if (setitem_lock_held((PyDictObject *)d, key, value) < 0) {
Py_DECREF(key);
Py_DECREF(value);
goto Fail;
}
}
else {
- if (PyDict_SetDefault(d, key, value) == NULL) {
+ if (dict_setdefault_ref_lock_held(d, key, value, NULL, 0) < 0) {
Py_DECREF(key);
Py_DECREF(value);
goto Fail;
@@ -2807,140 +3735,169 @@ Return:
return Py_SAFE_DOWNCAST(i, Py_ssize_t, int);
}
-static int
-dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override)
+int
+PyDict_MergeFromSeq2(PyObject *d, PyObject *seq2, int override)
{
- PyDictObject *mp, *other;
-
- assert(0 <= override && override <= 2);
+ int res;
+ Py_BEGIN_CRITICAL_SECTION(d);
+ res = merge_from_seq2_lock_held(d, seq2, override);
+ Py_END_CRITICAL_SECTION();
- /* We accept for the argument either a concrete dictionary object,
- * or an abstract "mapping" object. For the former, we can do
- * things quite efficiently. For the latter, we only require that
- * PyMapping_Keys() and PyObject_GetItem() be supported.
- */
- if (a == NULL || !PyDict_Check(a) || b == NULL) {
- PyErr_BadInternalCall();
- return -1;
- }
- mp = (PyDictObject*)a;
- if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == (getiterfunc)dict_iter)) {
- other = (PyDictObject*)b;
- if (other == mp || other->ma_used == 0)
- /* a.update(a) or a.update({}); nothing to do */
- return 0;
- if (mp->ma_used == 0) {
- /* Since the target dict is empty, PyDict_GetItem()
- * always returns NULL. Setting override to 1
- * skips the unnecessary test.
- */
- override = 1;
- PyDictKeysObject *okeys = other->ma_keys;
+ return res;
+}
- // If other is clean, combined, and just allocated, just clone it.
- if (other->ma_values == NULL &&
- other->ma_used == okeys->dk_nentries &&
- (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE ||
- USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)) {
- uint64_t new_version = _PyDict_NotifyEvent(
- interp, PyDict_EVENT_CLONED, mp, b, NULL);
- PyDictKeysObject *keys = clone_combined_dict_keys(other);
- if (keys == NULL) {
- return -1;
- }
+static int
+dict_dict_merge(PyInterpreterState *interp, PyDictObject *mp, PyDictObject *other, int override)
+{
+ ASSERT_DICT_LOCKED(mp);
+ ASSERT_DICT_LOCKED(other);
- dictkeys_decref(interp, mp->ma_keys);
- mp->ma_keys = keys;
- if (mp->ma_values != NULL) {
- free_values(mp->ma_values);
- mp->ma_values = NULL;
- }
+ if (other == mp || other->ma_used == 0)
+ /* a.update(a) or a.update({}); nothing to do */
+ return 0;
+ if (mp->ma_used == 0) {
+ /* Since the target dict is empty, PyDict_GetItem()
+ * always returns NULL. Setting override to 1
+ * skips the unnecessary test.
+ */
+ override = 1;
+ PyDictKeysObject *okeys = other->ma_keys;
- mp->ma_used = other->ma_used;
- mp->ma_version_tag = new_version;
- ASSERT_CONSISTENT(mp);
+ // If other is clean, combined, and just allocated, just clone it.
+ if (mp->ma_values == NULL &&
+ other->ma_values == NULL &&
+ other->ma_used == okeys->dk_nentries &&
+ (DK_LOG_SIZE(okeys) == PyDict_LOG_MINSIZE ||
+ USABLE_FRACTION(DK_SIZE(okeys)/2) < other->ma_used)
+ ) {
+ uint64_t new_version = _PyDict_NotifyEvent(
+ interp, PyDict_EVENT_CLONED, mp, (PyObject *)other, NULL);
+ PyDictKeysObject *keys = clone_combined_dict_keys(other);
+ if (keys == NULL)
+ return -1;
- if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) {
- /* Maintain tracking. */
- _PyObject_GC_TRACK(mp);
- }
+ ensure_shared_on_resize(mp);
+ dictkeys_decref(interp, mp->ma_keys, IS_DICT_SHARED(mp));
+ set_keys(mp, keys);
+ STORE_USED(mp, other->ma_used);
+ mp->ma_version_tag = new_version;
+ ASSERT_CONSISTENT(mp);
- return 0;
+ if (_PyObject_GC_IS_TRACKED(other) && !_PyObject_GC_IS_TRACKED(mp)) {
+ /* Maintain tracking. */
+ _PyObject_GC_TRACK(mp);
}
+
+ return 0;
}
- /* Do one big resize at the start, rather than
- * incrementally resizing as we insert new items. Expect
- * that there will be no (or few) overlapping keys.
- */
- if (USABLE_FRACTION(DK_SIZE(mp->ma_keys)) < other->ma_used) {
- int unicode = DK_IS_UNICODE(other->ma_keys);
- if (dictresize(interp, mp,
- estimate_log2_keysize(mp->ma_used + other->ma_used),
- unicode)) {
- return -1;
- }
+ }
+ /* Do one big resize at the start, rather than
+ * incrementally resizing as we insert new items. Expect
+ * that there will be no (or few) overlapping keys.
+ */
+ if (USABLE_FRACTION(DK_SIZE(mp->ma_keys)) < other->ma_used) {
+ int unicode = DK_IS_UNICODE(other->ma_keys);
+ if (dictresize(interp, mp,
+ estimate_log2_keysize(mp->ma_used + other->ma_used),
+ unicode)) {
+ return -1;
}
+ }
- Py_ssize_t orig_size = other->ma_keys->dk_nentries;
- Py_ssize_t pos = 0;
- Py_hash_t hash;
- PyObject *key, *value;
+ Py_ssize_t orig_size = other->ma_used;
+ Py_ssize_t pos = 0;
+ Py_hash_t hash;
+ PyObject *key, *value;
- while (_PyDict_Next((PyObject*)other, &pos, &key, &value, &hash)) {
- int err = 0;
- Py_INCREF(key);
- Py_INCREF(value);
- if (override == 1) {
+ while (_PyDict_Next((PyObject*)other, &pos, &key, &value, &hash)) {
+ int err = 0;
+ Py_INCREF(key);
+ Py_INCREF(value);
+ if (override == 1) {
+ err = insertdict(interp, mp,
+ Py_NewRef(key), hash, Py_NewRef(value));
+ }
+ else {
+ err = _PyDict_Contains_KnownHash((PyObject *)mp, key, hash);
+ if (err == 0) {
err = insertdict(interp, mp,
- Py_NewRef(key), hash, Py_NewRef(value));
+ Py_NewRef(key), hash, Py_NewRef(value));
}
- else {
- err = _PyDict_Contains_KnownHash(a, key, hash);
- if (err == 0) {
- err = insertdict(interp, mp,
- Py_NewRef(key), hash, Py_NewRef(value));
- }
- else if (err > 0) {
- if (override != 0) {
- _PyErr_SetKeyError(key);
- Py_DECREF(value);
- Py_DECREF(key);
- return -1;
- }
- err = 0;
+ else if (err > 0) {
+ if (override != 0) {
+ _PyErr_SetKeyError(key);
+ Py_DECREF(value);
+ Py_DECREF(key);
+ return -1;
}
+ err = 0;
}
- Py_DECREF(value);
- Py_DECREF(key);
- if (err != 0)
- return -1;
+ }
+ Py_DECREF(value);
+ Py_DECREF(key);
+ if (err != 0)
+ return -1;
- if (orig_size != other->ma_keys->dk_nentries) {
- PyErr_SetString(PyExc_RuntimeError,
- "dict mutated during update");
- return -1;
- }
+ if (orig_size != other->ma_used) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "dict mutated during update");
+ return -1;
}
}
+ return 0;
+}
+
+static int
+dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override)
+{
+ PyDictObject *mp, *other;
+
+ assert(0 <= override && override <= 2);
+
+ /* We accept for the argument either a concrete dictionary object,
+ * or an abstract "mapping" object. For the former, we can do
+ * things quite efficiently. For the latter, we only require that
+ * PyMapping_Keys() and PyObject_GetItem() be supported.
+ */
+ if (a == NULL || !PyDict_Check(a) || b == NULL) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ mp = (PyDictObject*)a;
+ int res = 0;
+ if (PyDict_Check(b) && (Py_TYPE(b)->tp_iter == dict_iter)) {
+ other = (PyDictObject*)b;
+ int res;
+ Py_BEGIN_CRITICAL_SECTION2(a, b);
+ res = dict_dict_merge(interp, (PyDictObject *)a, other, override);
+ ASSERT_CONSISTENT(a);
+ Py_END_CRITICAL_SECTION2();
+ return res;
+ }
else {
/* Do it the generic, slower way */
+ Py_BEGIN_CRITICAL_SECTION(a);
PyObject *keys = PyMapping_Keys(b);
PyObject *iter;
PyObject *key, *value;
int status;
- if (keys == NULL)
+ if (keys == NULL) {
/* Docstring says this is equivalent to E.keys() so
* if E doesn't have a .keys() method we want
* AttributeError to percolate up. Might as well
* do the same for any other error.
*/
- return -1;
+ res = -1;
+ goto slow_exit;
+ }
iter = PyObject_GetIter(keys);
Py_DECREF(keys);
- if (iter == NULL)
- return -1;
+ if (iter == NULL) {
+ res = -1;
+ goto slow_exit;
+ }
for (key = PyIter_Next(iter); key; key = PyIter_Next(iter)) {
if (override != 1) {
@@ -2955,30 +3912,39 @@ dict_merge(PyInterpreterState *interp, PyObject *a, PyObject *b, int override)
}
Py_DECREF(key);
Py_DECREF(iter);
- return -1;
+ res = -1;
+ goto slow_exit;
}
}
value = PyObject_GetItem(b, key);
if (value == NULL) {
Py_DECREF(iter);
Py_DECREF(key);
- return -1;
+ res = -1;
+ goto slow_exit;
}
- status = PyDict_SetItem(a, key, value);
+ status = setitem_lock_held(mp, key, value);
Py_DECREF(key);
Py_DECREF(value);
if (status < 0) {
Py_DECREF(iter);
+ res = -1;
+ goto slow_exit;
return -1;
}
}
Py_DECREF(iter);
- if (PyErr_Occurred())
+ if (PyErr_Occurred()) {
/* Iterator completed, via error */
- return -1;
+ res = -1;
+ goto slow_exit;
+ }
+
+slow_exit:
+ ASSERT_CONSISTENT(a);
+ Py_END_CRITICAL_SECTION();
+ return res;
}
- ASSERT_CONSISTENT(a);
- return 0;
}
int
@@ -3003,23 +3969,48 @@ _PyDict_MergeEx(PyObject *a, PyObject *b, int override)
return dict_merge(interp, a, b, override);
}
+/*[clinic input]
+dict.copy
+
+Return a shallow copy of the dict.
+[clinic start generated code]*/
+
static PyObject *
-dict_copy(PyDictObject *mp, PyObject *Py_UNUSED(ignored))
+dict_copy_impl(PyDictObject *self)
+/*[clinic end generated code: output=ffb782cf970a5c39 input=73935f042b639de4]*/
{
- return PyDict_Copy((PyObject*)mp);
+ return PyDict_Copy((PyObject *)self);
}
-PyObject *
-PyDict_Copy(PyObject *o)
+/* Copies the values, but does not change the reference
+ * counts of the objects in the array.
+ * Return NULL, but does *not* set an exception on failure */
+static PyDictValues *
+copy_values(PyDictValues *values)
+{
+ PyDictValues *newvalues = new_values(values->capacity);
+ if (newvalues == NULL) {
+ return NULL;
+ }
+ newvalues->size = values->size;
+ uint8_t *values_order = get_insertion_order_array(values);
+ uint8_t *new_values_order = get_insertion_order_array(newvalues);
+ memcpy(new_values_order, values_order, values->capacity);
+ for (int i = 0; i < values->capacity; i++) {
+ newvalues->values[i] = values->values[i];
+ }
+ assert(newvalues->embedded == 0);
+ return newvalues;
+}
+
+static PyObject *
+copy_lock_held(PyObject *o)
{
PyObject *copy;
PyDictObject *mp;
PyInterpreterState *interp = _PyInterpreterState_GET();
- if (o == NULL || !PyDict_Check(o)) {
- PyErr_BadInternalCall();
- return NULL;
- }
+ ASSERT_DICT_LOCKED(o);
mp = (PyDictObject *)o;
if (mp->ma_used == 0) {
@@ -3029,32 +4020,29 @@ PyDict_Copy(PyObject *o)
if (_PyDict_HasSplitTable(mp)) {
PyDictObject *split_copy;
- size_t size = shared_keys_usable_size(mp->ma_keys);
- PyDictValues *newvalues = new_values(size);
- if (newvalues == NULL)
+ PyDictValues *newvalues = copy_values(mp->ma_values);
+ if (newvalues == NULL) {
return PyErr_NoMemory();
+ }
split_copy = PyObject_GC_New(PyDictObject, &PyDict_Type);
if (split_copy == NULL) {
- free_values(newvalues);
+ free_values(newvalues, false);
return NULL;
}
- size_t prefix_size = ((uint8_t *)newvalues)[-1];
- memcpy(((char *)newvalues)-prefix_size, ((char *)mp->ma_values)-prefix_size, prefix_size-1);
+ for (size_t i = 0; i < newvalues->capacity; i++) {
+ Py_XINCREF(newvalues->values[i]);
+ }
split_copy->ma_values = newvalues;
split_copy->ma_keys = mp->ma_keys;
split_copy->ma_used = mp->ma_used;
split_copy->ma_version_tag = DICT_NEXT_VERSION(interp);
dictkeys_incref(mp->ma_keys);
- for (size_t i = 0; i < size; i++) {
- PyObject *value = mp->ma_values->values[i];
- split_copy->ma_values->values[i] = Py_XNewRef(value);
- }
if (_PyObject_GC_IS_TRACKED(mp))
_PyObject_GC_TRACK(split_copy);
return (PyObject *)split_copy;
}
- if (Py_TYPE(mp)->tp_iter == (getiterfunc)dict_iter &&
+ if (Py_TYPE(mp)->tp_iter == dict_iter &&
mp->ma_values == NULL &&
(mp->ma_used >= (mp->ma_keys->dk_nentries * 2) / 3))
{
@@ -3102,44 +4090,31 @@ PyDict_Copy(PyObject *o)
return NULL;
}
-Py_ssize_t
-PyDict_Size(PyObject *mp)
-{
- if (mp == NULL || !PyDict_Check(mp)) {
- PyErr_BadInternalCall();
- return -1;
- }
- return ((PyDictObject *)mp)->ma_used;
-}
-
PyObject *
-PyDict_Keys(PyObject *mp)
+PyDict_Copy(PyObject *o)
{
- if (mp == NULL || !PyDict_Check(mp)) {
+ if (o == NULL || !PyDict_Check(o)) {
PyErr_BadInternalCall();
return NULL;
}
- return dict_keys((PyDictObject *)mp);
-}
-PyObject *
-PyDict_Values(PyObject *mp)
-{
- if (mp == NULL || !PyDict_Check(mp)) {
- PyErr_BadInternalCall();
- return NULL;
- }
- return dict_values((PyDictObject *)mp);
+ PyObject *res;
+ Py_BEGIN_CRITICAL_SECTION(o);
+
+ res = copy_lock_held(o);
+
+ Py_END_CRITICAL_SECTION();
+ return res;
}
-PyObject *
-PyDict_Items(PyObject *mp)
+Py_ssize_t
+PyDict_Size(PyObject *mp)
{
if (mp == NULL || !PyDict_Check(mp)) {
PyErr_BadInternalCall();
- return NULL;
+ return -1;
}
- return dict_items((PyDictObject *)mp);
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(((PyDictObject *)mp)->ma_used);
}
/* Return 1 if dicts equal, 0 if not, -1 if error.
@@ -3147,15 +4122,18 @@ PyDict_Items(PyObject *mp)
* Uses only Py_EQ comparison.
*/
static int
-dict_equal(PyDictObject *a, PyDictObject *b)
+dict_equal_lock_held(PyDictObject *a, PyDictObject *b)
{
Py_ssize_t i;
+ ASSERT_DICT_LOCKED(a);
+ ASSERT_DICT_LOCKED(b);
+
if (a->ma_used != b->ma_used)
/* can't be equal if # of entries differ */
return 0;
/* Same # of entries -- check all of 'em. Exit early on any diff. */
- for (i = 0; i < a->ma_keys->dk_nentries; i++) {
+ for (i = 0; i < LOAD_KEYS_NENTRIES(a->ma_keys); i++) {
PyObject *key, *aval;
Py_hash_t hash;
if (DK_IS_UNICODE(a->ma_keys)) {
@@ -3165,7 +4143,7 @@ dict_equal(PyDictObject *a, PyDictObject *b)
continue;
}
hash = unicode_get_hash(key);
- if (a->ma_values)
+ if (_PyDict_HasSplitTable(a))
aval = a->ma_values->values[i];
else
aval = ep->me_value;
@@ -3205,6 +4183,17 @@ dict_equal(PyDictObject *a, PyDictObject *b)
return 1;
}
+static int
+dict_equal(PyDictObject *a, PyDictObject *b)
+{
+ int res;
+ Py_BEGIN_CRITICAL_SECTION2(a, b);
+ res = dict_equal_lock_held(a, b);
+ Py_END_CRITICAL_SECTION2();
+
+ return res;
+}
+
static PyObject *
dict_richcompare(PyObject *v, PyObject *w, int op)
{
@@ -3240,25 +4229,18 @@ static PyObject *
dict___contains__(PyDictObject *self, PyObject *key)
/*[clinic end generated code: output=a3d03db709ed6e6b input=fe1cb42ad831e820]*/
{
- register PyDictObject *mp = self;
- Py_hash_t hash;
- Py_ssize_t ix;
- PyObject *value;
-
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return NULL;
- }
- ix = _Py_dict_lookup(mp, key, hash, &value);
- if (ix == DKIX_ERROR)
+ int contains = PyDict_Contains((PyObject *)self, key);
+ if (contains < 0) {
return NULL;
- if (ix == DKIX_EMPTY || value == NULL)
- Py_RETURN_FALSE;
- Py_RETURN_TRUE;
+ }
+ if (contains) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
}
/*[clinic input]
+@critical_section
dict.get
key: object
@@ -3270,121 +4252,147 @@ Return the value for key if key is in the dictionary, else default.
static PyObject *
dict_get_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
-/*[clinic end generated code: output=bba707729dee05bf input=279ddb5790b6b107]*/
+/*[clinic end generated code: output=bba707729dee05bf input=a631d3f18f584c60]*/
{
PyObject *val = NULL;
Py_hash_t hash;
Py_ssize_t ix;
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return NULL;
+ hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return NULL;
}
- ix = _Py_dict_lookup(self, key, hash, &val);
+ ix = _Py_dict_lookup_threadsafe(self, key, hash, &val);
if (ix == DKIX_ERROR)
return NULL;
if (ix == DKIX_EMPTY || val == NULL) {
- val = default_value;
+ val = Py_NewRef(default_value);
}
- return Py_NewRef(val);
+ return val;
}
-PyObject *
-PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
+static int
+dict_setdefault_ref_lock_held(PyObject *d, PyObject *key, PyObject *default_value,
+ PyObject **result, int incref_result)
{
PyDictObject *mp = (PyDictObject *)d;
PyObject *value;
Py_hash_t hash;
+ Py_ssize_t ix;
PyInterpreterState *interp = _PyInterpreterState_GET();
+ ASSERT_DICT_LOCKED(d);
+
if (!PyDict_Check(d)) {
PyErr_BadInternalCall();
- return NULL;
+ if (result) {
+ *result = NULL;
+ }
+ return -1;
}
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return NULL;
+ hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ if (result) {
+ *result = NULL;
+ }
+ return -1;
}
if (mp->ma_keys == Py_EMPTY_KEYS) {
if (insert_to_emptydict(interp, mp, Py_NewRef(key), hash,
- Py_NewRef(defaultobj)) < 0) {
- return NULL;
+ Py_NewRef(default_value)) < 0) {
+ if (result) {
+ *result = NULL;
+ }
+ return -1;
}
- return defaultobj;
- }
-
- if (!PyUnicode_CheckExact(key) && DK_IS_UNICODE(mp->ma_keys)) {
- if (insertion_resize(interp, mp, 0) < 0) {
- return NULL;
+ if (result) {
+ *result = incref_result ? Py_NewRef(default_value) : default_value;
}
+ return 0;
}
- Py_ssize_t ix = _Py_dict_lookup(mp, key, hash, &value);
- if (ix == DKIX_ERROR)
- return NULL;
-
- if (ix == DKIX_EMPTY) {
- value = defaultobj;
- if (mp->ma_keys->dk_usable <= 0) {
- if (insertion_resize(interp, mp, 1) < 0) {
- return NULL;
+ if (_PyDict_HasSplitTable(mp) && PyUnicode_CheckExact(key)) {
+ ix = insert_split_key(mp->ma_keys, key, hash);
+ if (ix != DKIX_EMPTY) {
+ PyObject *value = mp->ma_values->values[ix];
+ int already_present = value != NULL;
+ if (!already_present) {
+ insert_split_value(interp, mp, key, default_value, ix);
+ value = default_value;
}
- }
- uint64_t new_version = _PyDict_NotifyEvent(
- interp, PyDict_EVENT_ADDED, mp, key, defaultobj);
- mp->ma_keys->dk_version = 0;
- Py_ssize_t hashpos = find_empty_slot(mp->ma_keys, hash);
- dictkeys_set_index(mp->ma_keys, hashpos, mp->ma_keys->dk_nentries);
- if (DK_IS_UNICODE(mp->ma_keys)) {
- assert(PyUnicode_CheckExact(key));
- PyDictUnicodeEntry *ep = &DK_UNICODE_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
- ep->me_key = Py_NewRef(key);
- if (_PyDict_HasSplitTable(mp)) {
- Py_ssize_t index = (int)mp->ma_keys->dk_nentries;
- assert(index < SHARED_KEYS_MAX_SIZE);
- assert(mp->ma_values->values[index] == NULL);
- mp->ma_values->values[index] = Py_NewRef(value);
- _PyDictValues_AddToInsertionOrder(mp->ma_values, index);
+ if (result) {
+ *result = incref_result ? Py_NewRef(value) : value;
}
- else {
- ep->me_value = Py_NewRef(value);
+ return already_present;
+ }
+ // No space in shared keys. Go to insert_combined_dict() below.
+ }
+ else {
+ ix = _Py_dict_lookup(mp, key, hash, &value);
+ if (ix == DKIX_ERROR) {
+ if (result) {
+ *result = NULL;
}
+ return -1;
}
- else {
- PyDictKeyEntry *ep = &DK_ENTRIES(mp->ma_keys)[mp->ma_keys->dk_nentries];
- ep->me_key = Py_NewRef(key);
- ep->me_hash = hash;
- ep->me_value = Py_NewRef(value);
+ }
+
+ if (ix == DKIX_EMPTY) {
+ value = default_value;
+
+ // See comment to this function in insertdict.
+ if (insert_combined_dict(interp, mp, hash, Py_NewRef(key), Py_NewRef(value)) < 0) {
+ Py_DECREF(key);
+ Py_DECREF(value);
+ if (result) {
+ *result = NULL;
+ }
+ return -1;
}
+
MAINTAIN_TRACKING(mp, key, value);
- mp->ma_used++;
- mp->ma_version_tag = new_version;
- mp->ma_keys->dk_usable--;
- mp->ma_keys->dk_nentries++;
+ STORE_USED(mp, mp->ma_used + 1);
assert(mp->ma_keys->dk_usable >= 0);
- }
- else if (value == NULL) {
- uint64_t new_version = _PyDict_NotifyEvent(
- interp, PyDict_EVENT_ADDED, mp, key, defaultobj);
- value = defaultobj;
- assert(_PyDict_HasSplitTable(mp));
- assert(mp->ma_values->values[ix] == NULL);
- MAINTAIN_TRACKING(mp, key, value);
- mp->ma_values->values[ix] = Py_NewRef(value);
- _PyDictValues_AddToInsertionOrder(mp->ma_values, ix);
- mp->ma_used++;
- mp->ma_version_tag = new_version;
+ ASSERT_CONSISTENT(mp);
+ if (result) {
+ *result = incref_result ? Py_NewRef(value) : value;
+ }
+ return 0;
}
+ assert(value != NULL);
ASSERT_CONSISTENT(mp);
- return value;
+ if (result) {
+ *result = incref_result ? Py_NewRef(value) : value;
+ }
+ return 1;
+}
+
+int
+PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value,
+ PyObject **result)
+{
+ int res;
+ Py_BEGIN_CRITICAL_SECTION(d);
+ res = dict_setdefault_ref_lock_held(d, key, default_value, result, 1);
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
+PyObject *
+PyDict_SetDefault(PyObject *d, PyObject *key, PyObject *defaultobj)
+{
+ PyObject *result;
+ Py_BEGIN_CRITICAL_SECTION(d);
+ dict_setdefault_ref_lock_held(d, key, defaultobj, &result, 0);
+ Py_END_CRITICAL_SECTION();
+ return result;
}
/*[clinic input]
+@critical_section
dict.setdefault
key: object
@@ -3399,18 +4407,25 @@ Return the value for key if key is in the dictionary, else default.
static PyObject *
dict_setdefault_impl(PyDictObject *self, PyObject *key,
PyObject *default_value)
-/*[clinic end generated code: output=f8c1101ebf69e220 input=0f063756e815fd9d]*/
+/*[clinic end generated code: output=f8c1101ebf69e220 input=9237af9a0a224302]*/
{
PyObject *val;
-
- val = PyDict_SetDefault((PyObject *)self, key, default_value);
- return Py_XNewRef(val);
+ dict_setdefault_ref_lock_held((PyObject *)self, key, default_value, &val, 1);
+ return val;
}
+
+/*[clinic input]
+dict.clear
+
+Remove all items from the dict.
+[clinic start generated code]*/
+
static PyObject *
-dict_clear(PyDictObject *mp, PyObject *Py_UNUSED(ignored))
+dict_clear_impl(PyDictObject *self)
+/*[clinic end generated code: output=5139a830df00830a input=0bf729baba97a4c2]*/
{
- PyDict_Clear((PyObject *)mp);
+ PyDict_Clear((PyObject *)self);
Py_RETURN_NONE;
}
@@ -3435,6 +4450,7 @@ dict_pop_impl(PyDictObject *self, PyObject *key, PyObject *default_value)
}
/*[clinic input]
+@critical_section
dict.popitem
Remove and return a (key, value) pair as a 2-tuple.
@@ -3445,13 +4461,15 @@ Raises KeyError if the dict is empty.
static PyObject *
dict_popitem_impl(PyDictObject *self)
-/*[clinic end generated code: output=e65fcb04420d230d input=1c38a49f21f64941]*/
+/*[clinic end generated code: output=e65fcb04420d230d input=ef28b4da5f0f762e]*/
{
Py_ssize_t i, j;
PyObject *res;
uint64_t new_version;
PyInterpreterState *interp = _PyInterpreterState_GET();
+ ASSERT_DICT_LOCKED(self);
+
/* Allocate the result tuple before checking the size. Believe it
* or not, this allocation could trigger a garbage collection which
* could empty the dict, so if we checked the size first and that
@@ -3470,8 +4488,8 @@ dict_popitem_impl(PyDictObject *self)
return NULL;
}
/* Convert split table to combined table */
- if (self->ma_keys->dk_kind == DICT_KEYS_SPLIT) {
- if (dictresize(interp, self, DK_LOG_SIZE(self->ma_keys), 1)) {
+ if (_PyDict_HasSplitTable(self)) {
+ if (dictresize(interp, self, DK_LOG_SIZE(self->ma_keys), 1) < 0) {
Py_DECREF(res);
return NULL;
}
@@ -3494,8 +4512,8 @@ dict_popitem_impl(PyDictObject *self)
interp, PyDict_EVENT_DELETED, self, key, NULL);
hash = unicode_get_hash(key);
value = ep0[i].me_value;
- ep0[i].me_key = NULL;
- ep0[i].me_value = NULL;
+ STORE_KEY(&ep0[i], NULL);
+ STORE_VALUE(&ep0[i], NULL);
}
else {
PyDictKeyEntry *ep0 = DK_ENTRIES(self->ma_keys);
@@ -3510,9 +4528,9 @@ dict_popitem_impl(PyDictObject *self)
interp, PyDict_EVENT_DELETED, self, key, NULL);
hash = ep0[i].me_hash;
value = ep0[i].me_value;
- ep0[i].me_key = NULL;
- ep0[i].me_hash = -1;
- ep0[i].me_value = NULL;
+ STORE_KEY(&ep0[i], NULL);
+ STORE_HASH(&ep0[i], -1);
+ STORE_VALUE(&ep0[i], NULL);
}
j = lookdict_index(self->ma_keys, hash, i);
@@ -3523,8 +4541,8 @@ dict_popitem_impl(PyDictObject *self)
PyTuple_SET_ITEM(res, 0, key);
PyTuple_SET_ITEM(res, 1, value);
/* We can't dk_usable++ since there is DKIX_DUMMY in indices */
- self->ma_keys->dk_nentries = i;
- self->ma_used--;
+ STORE_KEYS_NENTRIES(self->ma_keys, i);
+ STORE_USED(self, self->ma_used - 1);
self->ma_version_tag = new_version;
ASSERT_CONSISTENT(self);
return res;
@@ -3538,7 +4556,7 @@ dict_traverse(PyObject *op, visitproc visit, void *arg)
Py_ssize_t i, n = keys->dk_nentries;
if (DK_IS_UNICODE(keys)) {
- if (mp->ma_values != NULL) {
+ if (_PyDict_HasSplitTable(mp)) {
for (i = 0; i < n; i++) {
Py_VISIT(mp->ma_values->values[i]);
}
@@ -3571,11 +4589,11 @@ dict_tp_clear(PyObject *op)
static PyObject *dictiter_new(PyDictObject *, PyTypeObject *);
-Py_ssize_t
-_PyDict_SizeOf(PyDictObject *mp)
+static Py_ssize_t
+sizeof_lock_held(PyDictObject *mp)
{
size_t res = _PyObject_SIZE(Py_TYPE(mp));
- if (mp->ma_values) {
+ if (_PyDict_HasSplitTable(mp)) {
res += shared_keys_usable_size(mp->ma_keys) * sizeof(PyObject*);
}
/* If the dictionary is split, the keys portion is accounted-for
@@ -3587,6 +4605,17 @@ _PyDict_SizeOf(PyDictObject *mp)
return (Py_ssize_t)res;
}
+Py_ssize_t
+_PyDict_SizeOf(PyDictObject *mp)
+{
+ Py_ssize_t res;
+ Py_BEGIN_CRITICAL_SECTION(mp);
+ res = sizeof_lock_held(mp);
+ Py_END_CRITICAL_SECTION();
+
+ return res;
+}
+
size_t
_PyDict_KeysSize(PyDictKeysObject *keys)
{
@@ -3598,10 +4627,17 @@ _PyDict_KeysSize(PyDictKeysObject *keys)
return size;
}
+/*[clinic input]
+dict.__sizeof__
+
+Return the size of the dict in memory, in bytes.
+[clinic start generated code]*/
+
static PyObject *
-dict_sizeof(PyDictObject *mp, PyObject *Py_UNUSED(ignored))
+dict___sizeof___impl(PyDictObject *self)
+/*[clinic end generated code: output=44279379b3824bda input=4fec4ddfc44a4d1a]*/
{
- return PyLong_FromSsize_t(_PyDict_SizeOf(mp));
+ return PyLong_FromSsize_t(_PyDict_SizeOf(self));
}
static PyObject *
@@ -3633,56 +4669,31 @@ dict_ior(PyObject *self, PyObject *other)
PyDoc_STRVAR(getitem__doc__,
"__getitem__($self, key, /)\n--\n\nReturn self[key].");
-PyDoc_STRVAR(sizeof__doc__,
-"D.__sizeof__() -> size of D in memory, in bytes");
-
PyDoc_STRVAR(update__doc__,
"D.update([E, ]**F) -> None. Update D from mapping/iterable E and F.\n\
If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k]\n\
If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v\n\
In either case, this is followed by: for k in F: D[k] = F[k]");
-PyDoc_STRVAR(clear__doc__,
-"D.clear() -> None. Remove all items from D.");
-
-PyDoc_STRVAR(copy__doc__,
-"D.copy() -> a shallow copy of D");
-
/* Forward */
-static PyObject *dictkeys_new(PyObject *, PyObject *);
-static PyObject *dictitems_new(PyObject *, PyObject *);
-static PyObject *dictvalues_new(PyObject *, PyObject *);
-
-PyDoc_STRVAR(keys__doc__,
- "D.keys() -> a set-like object providing a view on D's keys");
-PyDoc_STRVAR(items__doc__,
- "D.items() -> a set-like object providing a view on D's items");
-PyDoc_STRVAR(values__doc__,
- "D.values() -> an object providing a view on D's values");
static PyMethodDef mapp_methods[] = {
DICT___CONTAINS___METHODDEF
- {"__getitem__", _PyCFunction_CAST(dict_subscript), METH_O | METH_COEXIST,
+ {"__getitem__", dict_subscript, METH_O | METH_COEXIST,
getitem__doc__},
- {"__sizeof__", _PyCFunction_CAST(dict_sizeof), METH_NOARGS,
- sizeof__doc__},
+ DICT___SIZEOF___METHODDEF
DICT_GET_METHODDEF
DICT_SETDEFAULT_METHODDEF
DICT_POP_METHODDEF
DICT_POPITEM_METHODDEF
- {"keys", dictkeys_new, METH_NOARGS,
- keys__doc__},
- {"items", dictitems_new, METH_NOARGS,
- items__doc__},
- {"values", dictvalues_new, METH_NOARGS,
- values__doc__},
+ DICT_KEYS_METHODDEF
+ DICT_ITEMS_METHODDEF
+ DICT_VALUES_METHODDEF
{"update", _PyCFunction_CAST(dict_update), METH_VARARGS | METH_KEYWORDS,
update__doc__},
DICT_FROMKEYS_METHODDEF
- {"clear", (PyCFunction)dict_clear, METH_NOARGS,
- clear__doc__},
- {"copy", (PyCFunction)dict_copy, METH_NOARGS,
- copy__doc__},
+ DICT_CLEAR_METHODDEF
+ DICT_COPY_METHODDEF
DICT___REVERSED___METHODDEF
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* sentinel */
@@ -3692,20 +4703,25 @@ static PyMethodDef mapp_methods[] = {
int
PyDict_Contains(PyObject *op, PyObject *key)
{
- Py_hash_t hash;
- Py_ssize_t ix;
- PyDictObject *mp = (PyDictObject *)op;
- PyObject *value;
+ Py_hash_t hash = _PyObject_HashFast(key);
- if (!PyUnicode_CheckExact(key) || (hash = unicode_get_hash(key)) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return -1;
+ if (hash == -1) {
+ return -1;
}
- ix = _Py_dict_lookup(mp, key, hash, &value);
- if (ix == DKIX_ERROR)
+
+ return _PyDict_Contains_KnownHash(op, key, hash);
+}
+
+int
+PyDict_ContainsString(PyObject *op, const char *key)
+{
+ PyObject *key_obj = PyUnicode_FromString(key);
+ if (key_obj == NULL) {
return -1;
- return (ix != DKIX_EMPTY && value != NULL);
+ }
+ int res = PyDict_Contains(op, key_obj);
+ Py_DECREF(key_obj);
+ return res;
}
/* Internal version of PyDict_Contains used when the hash value is already known */
@@ -3716,10 +4732,20 @@ _PyDict_Contains_KnownHash(PyObject *op, PyObject *key, Py_hash_t hash)
PyObject *value;
Py_ssize_t ix;
+#ifdef Py_GIL_DISABLED
+ ix = _Py_dict_lookup_threadsafe(mp, key, hash, &value);
+#else
ix = _Py_dict_lookup(mp, key, hash, &value);
+#endif
if (ix == DKIX_ERROR)
return -1;
- return (ix != DKIX_EMPTY && value != NULL);
+ if (ix != DKIX_EMPTY && value != NULL) {
+#ifdef Py_GIL_DISABLED
+ Py_DECREF(value);
+#endif
+ return 1;
+ }
+ return 0;
}
int
@@ -3824,8 +4850,9 @@ dict_vectorcall(PyObject *type, PyObject * const*args,
}
static PyObject *
-dict_iter(PyDictObject *dict)
+dict_iter(PyObject *self)
{
+ PyDictObject *dict = (PyDictObject *)self;
return dictiter_new(dict, &PyDictIterKey_Type);
}
@@ -3845,12 +4872,12 @@ PyTypeObject PyDict_Type = {
"dict",
sizeof(PyDictObject),
0,
- (destructor)dict_dealloc, /* tp_dealloc */
+ dict_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)dict_repr, /* tp_repr */
+ dict_repr, /* tp_repr */
&dict_as_number, /* tp_as_number */
&dict_as_sequence, /* tp_as_sequence */
&dict_as_mapping, /* tp_as_mapping */
@@ -3868,7 +4895,7 @@ PyTypeObject PyDict_Type = {
dict_tp_clear, /* tp_clear */
dict_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)dict_iter, /* tp_iter */
+ dict_iter, /* tp_iter */
0, /* tp_iternext */
mapp_methods, /* tp_methods */
0, /* tp_members */
@@ -3893,12 +4920,29 @@ PyDict_GetItemString(PyObject *v, const char *key)
PyObject *kv, *rv;
kv = PyUnicode_FromString(key);
if (kv == NULL) {
- PyErr_Clear();
+ PyErr_FormatUnraisable(
+ "Exception ignored in PyDict_GetItemString(); consider using "
+ "PyDict_GetItemRefString()");
return NULL;
}
- rv = PyDict_GetItem(v, kv);
+ rv = dict_getitem(v, kv,
+ "Exception ignored in PyDict_GetItemString(); consider using "
+ "PyDict_GetItemRefString()");
Py_DECREF(kv);
- return rv;
+ return rv; // borrowed reference
+}
+
+int
+PyDict_GetItemStringRef(PyObject *v, const char *key, PyObject **result)
+{
+ PyObject *key_obj = PyUnicode_FromString(key);
+ if (key_obj == NULL) {
+ *result = NULL;
+ return -1;
+ }
+ int res = PyDict_GetItemRef(v, key_obj, result);
+ Py_DECREF(key_obj);
+ return res;
}
int
@@ -3962,22 +5006,24 @@ typedef struct {
static PyObject *
dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
{
+ Py_ssize_t used;
dictiterobject *di;
di = PyObject_GC_New(dictiterobject, itertype);
if (di == NULL) {
return NULL;
}
di->di_dict = (PyDictObject*)Py_NewRef(dict);
- di->di_used = dict->ma_used;
- di->len = dict->ma_used;
+ used = FT_ATOMIC_LOAD_SSIZE_RELAXED(dict->ma_used);
+ di->di_used = used;
+ di->len = used;
if (itertype == &PyDictRevIterKey_Type ||
itertype == &PyDictRevIterItem_Type ||
itertype == &PyDictRevIterValue_Type) {
- if (dict->ma_values) {
- di->di_pos = dict->ma_used - 1;
+ if (_PyDict_HasSplitTable(dict)) {
+ di->di_pos = used - 1;
}
else {
- di->di_pos = dict->ma_keys->dk_nentries - 1;
+ di->di_pos = load_keys_nentries(dict) - 1;
}
}
else {
@@ -3999,8 +5045,9 @@ dictiter_new(PyDictObject *dict, PyTypeObject *itertype)
}
static void
-dictiter_dealloc(dictiterobject *di)
+dictiter_dealloc(PyObject *self)
{
+ dictiterobject *di = (dictiterobject *)self;
/* bpo-31095: UnTrack is needed before calling any callbacks */
_PyObject_GC_UNTRACK(di);
Py_XDECREF(di->di_dict);
@@ -4009,19 +5056,21 @@ dictiter_dealloc(dictiterobject *di)
}
static int
-dictiter_traverse(dictiterobject *di, visitproc visit, void *arg)
+dictiter_traverse(PyObject *self, visitproc visit, void *arg)
{
+ dictiterobject *di = (dictiterobject *)self;
Py_VISIT(di->di_dict);
Py_VISIT(di->di_result);
return 0;
}
static PyObject *
-dictiter_len(dictiterobject *di, PyObject *Py_UNUSED(ignored))
+dictiter_len(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ dictiterobject *di = (dictiterobject *)self;
Py_ssize_t len = 0;
- if (di->di_dict != NULL && di->di_used == di->di_dict->ma_used)
- len = di->len;
+ if (di->di_dict != NULL && di->di_used == FT_ATOMIC_LOAD_SSIZE_RELAXED(di->di_dict->ma_used))
+ len = FT_ATOMIC_LOAD_SSIZE_RELAXED(di->len);
return PyLong_FromSize_t(len);
}
@@ -4029,29 +5078,36 @@ PyDoc_STRVAR(length_hint_doc,
"Private method returning an estimate of len(list(it)).");
static PyObject *
-dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored));
+dictiter_reduce(PyObject *di, PyObject *Py_UNUSED(ignored));
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
static PyMethodDef dictiter_methods[] = {
- {"__length_hint__", _PyCFunction_CAST(dictiter_len), METH_NOARGS,
+ {"__length_hint__", dictiter_len, METH_NOARGS,
length_hint_doc},
- {"__reduce__", _PyCFunction_CAST(dictiter_reduce), METH_NOARGS,
+ {"__reduce__", dictiter_reduce, METH_NOARGS,
reduce_doc},
{NULL, NULL} /* sentinel */
};
+#ifdef Py_GIL_DISABLED
+
+static int
+dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self,
+ PyObject **out_key, PyObject **out_value);
+
+#else /* Py_GIL_DISABLED */
+
static PyObject*
-dictiter_iternextkey(dictiterobject *di)
+dictiter_iternextkey_lock_held(PyDictObject *d, PyObject *self)
{
+ dictiterobject *di = (dictiterobject *)self;
PyObject *key;
Py_ssize_t i;
PyDictKeysObject *k;
- PyDictObject *d = di->di_dict;
- if (d == NULL)
- return NULL;
assert (PyDict_Check(d));
+ ASSERT_DICT_LOCKED(d);
if (di->di_used != d->ma_used) {
PyErr_SetString(PyExc_RuntimeError,
@@ -4063,11 +5119,11 @@ dictiter_iternextkey(dictiterobject *di)
i = di->di_pos;
k = d->ma_keys;
assert(i >= 0);
- if (d->ma_values) {
+ if (_PyDict_HasSplitTable(d)) {
if (i >= d->ma_used)
goto fail;
int index = get_index_from_order(d, i);
- key = DK_UNICODE_ENTRIES(k)[index].me_key;
+ key = LOAD_SHARED_KEY(DK_UNICODE_ENTRIES(k)[index].me_key);
assert(d->ma_values->values[index] != NULL);
}
else {
@@ -4109,13 +5165,36 @@ fail:
return NULL;
}
+#endif /* Py_GIL_DISABLED */
+
+static PyObject*
+dictiter_iternextkey(PyObject *self)
+{
+ dictiterobject *di = (dictiterobject *)self;
+ PyDictObject *d = di->di_dict;
+
+ if (d == NULL)
+ return NULL;
+
+ PyObject *value;
+#ifdef Py_GIL_DISABLED
+ if (dictiter_iternext_threadsafe(d, self, &value, NULL) < 0) {
+ value = NULL;
+ }
+#else
+ value = dictiter_iternextkey_lock_held(d, self);
+#endif
+
+ return value;
+}
+
PyTypeObject PyDictIterKey_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict_keyiterator", /* tp_name */
sizeof(dictiterobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)dictiter_dealloc, /* tp_dealloc */
+ dictiter_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
@@ -4132,26 +5211,27 @@ PyTypeObject PyDictIterKey_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
- (traverseproc)dictiter_traverse, /* tp_traverse */
+ dictiter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
- (iternextfunc)dictiter_iternextkey, /* tp_iternext */
+ dictiter_iternextkey, /* tp_iternext */
dictiter_methods, /* tp_methods */
0,
};
+#ifndef Py_GIL_DISABLED
+
static PyObject *
-dictiter_iternextvalue(dictiterobject *di)
+dictiter_iternextvalue_lock_held(PyDictObject *d, PyObject *self)
{
+ dictiterobject *di = (dictiterobject *)self;
PyObject *value;
Py_ssize_t i;
- PyDictObject *d = di->di_dict;
- if (d == NULL)
- return NULL;
assert (PyDict_Check(d));
+ ASSERT_DICT_LOCKED(d);
if (di->di_used != d->ma_used) {
PyErr_SetString(PyExc_RuntimeError,
@@ -4162,7 +5242,7 @@ dictiter_iternextvalue(dictiterobject *di)
i = di->di_pos;
assert(i >= 0);
- if (d->ma_values) {
+ if (_PyDict_HasSplitTable(d)) {
if (i >= d->ma_used)
goto fail;
int index = get_index_from_order(d, i);
@@ -4208,13 +5288,36 @@ fail:
return NULL;
}
+#endif /* Py_GIL_DISABLED */
+
+static PyObject *
+dictiter_iternextvalue(PyObject *self)
+{
+ dictiterobject *di = (dictiterobject *)self;
+ PyDictObject *d = di->di_dict;
+
+ if (d == NULL)
+ return NULL;
+
+ PyObject *value;
+#ifdef Py_GIL_DISABLED
+ if (dictiter_iternext_threadsafe(d, self, NULL, &value) < 0) {
+ value = NULL;
+ }
+#else
+ value = dictiter_iternextvalue_lock_held(d, self);
+#endif
+
+ return value;
+}
+
PyTypeObject PyDictIterValue_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict_valueiterator", /* tp_name */
sizeof(dictiterobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)dictiter_dealloc, /* tp_dealloc */
+ dictiter_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
@@ -4231,41 +5334,42 @@ PyTypeObject PyDictIterValue_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
- (traverseproc)dictiter_traverse, /* tp_traverse */
+ dictiter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
- (iternextfunc)dictiter_iternextvalue, /* tp_iternext */
+ dictiter_iternextvalue, /* tp_iternext */
dictiter_methods, /* tp_methods */
0,
};
-static PyObject *
-dictiter_iternextitem(dictiterobject *di)
+static int
+dictiter_iternextitem_lock_held(PyDictObject *d, PyObject *self,
+ PyObject **out_key, PyObject **out_value)
{
- PyObject *key, *value, *result;
+ dictiterobject *di = (dictiterobject *)self;
+ PyObject *key, *value;
Py_ssize_t i;
- PyDictObject *d = di->di_dict;
- if (d == NULL)
- return NULL;
assert (PyDict_Check(d));
+ ASSERT_DICT_LOCKED(d);
if (di->di_used != d->ma_used) {
PyErr_SetString(PyExc_RuntimeError,
"dictionary changed size during iteration");
di->di_used = -1; /* Make this state sticky */
- return NULL;
+ return -1;
}
- i = di->di_pos;
+ i = FT_ATOMIC_LOAD_SSIZE_RELAXED(di->di_pos);
+
assert(i >= 0);
- if (d->ma_values) {
+ if (_PyDict_HasSplitTable(d)) {
if (i >= d->ma_used)
goto fail;
int index = get_index_from_order(d, i);
- key = DK_UNICODE_ENTRIES(d->ma_keys)[index].me_key;
+ key = LOAD_SHARED_KEY(DK_UNICODE_ENTRIES(d->ma_keys)[index].me_key);
value = d->ma_values->values[index];
assert(value != NULL);
}
@@ -4302,33 +5406,222 @@ dictiter_iternextitem(dictiterobject *di)
}
di->di_pos = i+1;
di->len--;
- result = di->di_result;
- if (Py_REFCNT(result) == 1) {
- PyObject *oldkey = PyTuple_GET_ITEM(result, 0);
- PyObject *oldvalue = PyTuple_GET_ITEM(result, 1);
- PyTuple_SET_ITEM(result, 0, Py_NewRef(key));
- PyTuple_SET_ITEM(result, 1, Py_NewRef(value));
- Py_INCREF(result);
- Py_DECREF(oldkey);
- Py_DECREF(oldvalue);
- // bpo-42536: The GC may have untracked this result tuple. Since we're
- // recycling it, make sure it's tracked again:
- if (!_PyObject_GC_IS_TRACKED(result)) {
- _PyObject_GC_TRACK(result);
+ if (out_key != NULL) {
+ *out_key = Py_NewRef(key);
+ }
+ if (out_value != NULL) {
+ *out_value = Py_NewRef(value);
+ }
+ return 0;
+
+fail:
+ di->di_dict = NULL;
+ Py_DECREF(d);
+ return -1;
+}
+
+#ifdef Py_GIL_DISABLED
+
+// Grabs the key and/or value from the provided locations and if successful
+// returns them with an increased reference count. If either one is unsucessful
+// nothing is incref'd and returns -1.
+static int
+acquire_key_value(PyObject **key_loc, PyObject *value, PyObject **value_loc,
+ PyObject **out_key, PyObject **out_value)
+{
+ if (out_key) {
+ *out_key = _Py_TryXGetRef(key_loc);
+ if (*out_key == NULL) {
+ return -1;
+ }
+ }
+
+ if (out_value) {
+ if (!_Py_TryIncrefCompare(value_loc, value)) {
+ if (out_key) {
+ Py_DECREF(*out_key);
+ }
+ return -1;
+ }
+ *out_value = value;
+ }
+
+ return 0;
+}
+
+static int
+dictiter_iternext_threadsafe(PyDictObject *d, PyObject *self,
+ PyObject **out_key, PyObject **out_value)
+{
+ int res;
+ dictiterobject *di = (dictiterobject *)self;
+ Py_ssize_t i;
+ PyDictKeysObject *k;
+
+ assert (PyDict_Check(d));
+
+ if (di->di_used != _Py_atomic_load_ssize_relaxed(&d->ma_used)) {
+ PyErr_SetString(PyExc_RuntimeError,
+ "dictionary changed size during iteration");
+ di->di_used = -1; /* Make this state sticky */
+ return -1;
+ }
+
+ ensure_shared_on_read(d);
+
+ i = _Py_atomic_load_ssize_relaxed(&di->di_pos);
+ k = _Py_atomic_load_ptr_relaxed(&d->ma_keys);
+ assert(i >= 0);
+ if (_PyDict_HasSplitTable(d)) {
+ PyDictValues *values = _Py_atomic_load_ptr_relaxed(&d->ma_values);
+ if (values == NULL) {
+ goto concurrent_modification;
+ }
+
+ Py_ssize_t used = (Py_ssize_t)_Py_atomic_load_uint8(&values->size);
+ if (i >= used) {
+ goto fail;
+ }
+
+ // We're racing against writes to the order from delete_index_from_values, but
+ // single threaded can suffer from concurrent modification to those as well and
+ // can have either duplicated or skipped attributes, so we strive to do no better
+ // here.
+ int index = get_index_from_order(d, i);
+ PyObject *value = _Py_atomic_load_ptr(&values->values[index]);
+ if (acquire_key_value(&DK_UNICODE_ENTRIES(k)[index].me_key, value,
+ &values->values[index], out_key, out_value) < 0) {
+ goto try_locked;
}
}
else {
- result = PyTuple_New(2);
- if (result == NULL)
- return NULL;
- PyTuple_SET_ITEM(result, 0, Py_NewRef(key));
- PyTuple_SET_ITEM(result, 1, Py_NewRef(value));
+ Py_ssize_t n = _Py_atomic_load_ssize_relaxed(&k->dk_nentries);
+ if (DK_IS_UNICODE(k)) {
+ PyDictUnicodeEntry *entry_ptr = &DK_UNICODE_ENTRIES(k)[i];
+ PyObject *value;
+ while (i < n &&
+ (value = _Py_atomic_load_ptr(&entry_ptr->me_value)) == NULL) {
+ entry_ptr++;
+ i++;
+ }
+ if (i >= n)
+ goto fail;
+
+ if (acquire_key_value(&entry_ptr->me_key, value,
+ &entry_ptr->me_value, out_key, out_value) < 0) {
+ goto try_locked;
+ }
+ }
+ else {
+ PyDictKeyEntry *entry_ptr = &DK_ENTRIES(k)[i];
+ PyObject *value;
+ while (i < n &&
+ (value = _Py_atomic_load_ptr(&entry_ptr->me_value)) == NULL) {
+ entry_ptr++;
+ i++;
+ }
+
+ if (i >= n)
+ goto fail;
+
+ if (acquire_key_value(&entry_ptr->me_key, value,
+ &entry_ptr->me_value, out_key, out_value) < 0) {
+ goto try_locked;
+ }
+ }
}
- return result;
+ // We found an element (key), but did not expect it
+ Py_ssize_t len;
+ if ((len = _Py_atomic_load_ssize_relaxed(&di->len)) == 0) {
+ goto concurrent_modification;
+ }
+
+ _Py_atomic_store_ssize_relaxed(&di->di_pos, i + 1);
+ _Py_atomic_store_ssize_relaxed(&di->len, len - 1);
+ return 0;
+
+concurrent_modification:
+ PyErr_SetString(PyExc_RuntimeError,
+ "dictionary keys changed during iteration");
fail:
di->di_dict = NULL;
Py_DECREF(d);
+ return -1;
+
+try_locked:
+ Py_BEGIN_CRITICAL_SECTION(d);
+ res = dictiter_iternextitem_lock_held(d, self, out_key, out_value);
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
+#endif
+
+static bool
+has_unique_reference(PyObject *op)
+{
+#ifdef Py_GIL_DISABLED
+ return (_Py_IsOwnedByCurrentThread(op) &&
+ op->ob_ref_local == 1 &&
+ _Py_atomic_load_ssize_relaxed(&op->ob_ref_shared) == 0);
+#else
+ return Py_REFCNT(op) == 1;
+#endif
+}
+
+static bool
+acquire_iter_result(PyObject *result)
+{
+ if (has_unique_reference(result)) {
+ Py_INCREF(result);
+ return true;
+ }
+ return false;
+}
+
+static PyObject *
+dictiter_iternextitem(PyObject *self)
+{
+ dictiterobject *di = (dictiterobject *)self;
+ PyDictObject *d = di->di_dict;
+
+ if (d == NULL)
+ return NULL;
+
+ PyObject *key, *value;
+#ifdef Py_GIL_DISABLED
+ if (dictiter_iternext_threadsafe(d, self, &key, &value) == 0) {
+#else
+ if (dictiter_iternextitem_lock_held(d, self, &key, &value) == 0) {
+
+#endif
+ PyObject *result = di->di_result;
+ if (acquire_iter_result(result)) {
+ PyObject *oldkey = PyTuple_GET_ITEM(result, 0);
+ PyObject *oldvalue = PyTuple_GET_ITEM(result, 1);
+ PyTuple_SET_ITEM(result, 0, key);
+ PyTuple_SET_ITEM(result, 1, value);
+ Py_DECREF(oldkey);
+ Py_DECREF(oldvalue);
+ // bpo-42536: The GC may have untracked this result tuple. Since we're
+ // recycling it, make sure it's tracked again:
+ if (!_PyObject_GC_IS_TRACKED(result)) {
+ _PyObject_GC_TRACK(result);
+ }
+ }
+ else {
+ result = PyTuple_New(2);
+ if (result == NULL) {
+ Py_DECREF(key);
+ Py_DECREF(value);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(result, 0, key);
+ PyTuple_SET_ITEM(result, 1, value);
+ }
+ return result;
+ }
return NULL;
}
@@ -4338,7 +5631,7 @@ PyTypeObject PyDictIterItem_Type = {
sizeof(dictiterobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)dictiter_dealloc, /* tp_dealloc */
+ dictiter_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
@@ -4355,12 +5648,12 @@ PyTypeObject PyDictIterItem_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
- (traverseproc)dictiter_traverse, /* tp_traverse */
+ dictiter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
- (iternextfunc)dictiter_iternextitem, /* tp_iternext */
+ dictiter_iternextitem, /* tp_iternext */
dictiter_methods, /* tp_methods */
0,
};
@@ -4369,14 +5662,12 @@ PyTypeObject PyDictIterItem_Type = {
/* dictreviter */
static PyObject *
-dictreviter_iternext(dictiterobject *di)
+dictreviter_iter_lock_held(PyDictObject *d, PyObject *self)
{
- PyDictObject *d = di->di_dict;
+ dictiterobject *di = (dictiterobject *)self;
- if (d == NULL) {
- return NULL;
- }
assert (PyDict_Check(d));
+ ASSERT_DICT_LOCKED(d);
if (di->di_used != d->ma_used) {
PyErr_SetString(PyExc_RuntimeError,
@@ -4392,9 +5683,9 @@ dictreviter_iternext(dictiterobject *di)
if (i < 0) {
goto fail;
}
- if (d->ma_values) {
+ if (_PyDict_HasSplitTable(d)) {
int index = get_index_from_order(d, i);
- key = DK_UNICODE_ENTRIES(k)[index].me_key;
+ key = LOAD_SHARED_KEY(DK_UNICODE_ENTRIES(k)[index].me_key);
value = d->ma_values->values[index];
assert (value != NULL);
}
@@ -4467,15 +5758,32 @@ fail:
return NULL;
}
+static PyObject *
+dictreviter_iternext(PyObject *self)
+{
+ dictiterobject *di = (dictiterobject *)self;
+ PyDictObject *d = di->di_dict;
+
+ if (d == NULL)
+ return NULL;
+
+ PyObject *value;
+ Py_BEGIN_CRITICAL_SECTION(d);
+ value = dictreviter_iter_lock_held(d, self);
+ Py_END_CRITICAL_SECTION();
+
+ return value;
+}
+
PyTypeObject PyDictRevIterKey_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict_reversekeyiterator",
sizeof(dictiterobject),
- .tp_dealloc = (destructor)dictiter_dealloc,
+ .tp_dealloc = dictiter_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
- .tp_traverse = (traverseproc)dictiter_traverse,
+ .tp_traverse = dictiter_traverse,
.tp_iter = PyObject_SelfIter,
- .tp_iternext = (iternextfunc)dictreviter_iternext,
+ .tp_iternext = dictreviter_iternext,
.tp_methods = dictiter_methods
};
@@ -4495,8 +5803,9 @@ dict___reversed___impl(PyDictObject *self)
}
static PyObject *
-dictiter_reduce(dictiterobject *di, PyObject *Py_UNUSED(ignored))
+dictiter_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ dictiterobject *di = (dictiterobject *)self;
/* copy the iterator state */
dictiterobject tmp = *di;
Py_XINCREF(tmp.di_dict);
@@ -4512,11 +5821,11 @@ PyTypeObject PyDictRevIterItem_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict_reverseitemiterator",
sizeof(dictiterobject),
- .tp_dealloc = (destructor)dictiter_dealloc,
+ .tp_dealloc = dictiter_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
- .tp_traverse = (traverseproc)dictiter_traverse,
+ .tp_traverse = dictiter_traverse,
.tp_iter = PyObject_SelfIter,
- .tp_iternext = (iternextfunc)dictreviter_iternext,
+ .tp_iternext = dictreviter_iternext,
.tp_methods = dictiter_methods
};
@@ -4524,11 +5833,11 @@ PyTypeObject PyDictRevIterValue_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"dict_reversevalueiterator",
sizeof(dictiterobject),
- .tp_dealloc = (destructor)dictiter_dealloc,
+ .tp_dealloc = dictiter_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
- .tp_traverse = (traverseproc)dictiter_traverse,
+ .tp_traverse = dictiter_traverse,
.tp_iter = PyObject_SelfIter,
- .tp_iternext = (iternextfunc)dictreviter_iternext,
+ .tp_iternext = dictreviter_iternext,
.tp_methods = dictiter_methods
};
@@ -4539,8 +5848,9 @@ PyTypeObject PyDictRevIterValue_Type = {
/* The instance lay-out is the same for all three; but the type differs. */
static void
-dictview_dealloc(_PyDictViewObject *dv)
+dictview_dealloc(PyObject *self)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
/* bpo-31095: UnTrack is needed before calling any callbacks */
_PyObject_GC_UNTRACK(dv);
Py_XDECREF(dv->dv_dict);
@@ -4548,18 +5858,20 @@ dictview_dealloc(_PyDictViewObject *dv)
}
static int
-dictview_traverse(_PyDictViewObject *dv, visitproc visit, void *arg)
+dictview_traverse(PyObject *self, visitproc visit, void *arg)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
Py_VISIT(dv->dv_dict);
return 0;
}
static Py_ssize_t
-dictview_len(_PyDictViewObject *dv)
+dictview_len(PyObject *self)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
Py_ssize_t len = 0;
if (dv->dv_dict != NULL)
- len = dv->dv_dict->ma_used;
+ len = FT_ATOMIC_LOAD_SSIZE_RELAXED(dv->dv_dict->ma_used);
return len;
}
@@ -4598,7 +5910,7 @@ dictview_mapping(PyObject *view, void *Py_UNUSED(ignored)) {
static PyGetSetDef dictview_getset[] = {
{"mapping", dictview_mapping, (setter)NULL,
- "dictionary that this view refers to", NULL},
+ PyDoc_STR("dictionary that this view refers to"), NULL},
{0}
};
@@ -4696,8 +6008,9 @@ dictview_richcompare(PyObject *self, PyObject *other, int op)
}
static PyObject *
-dictview_repr(_PyDictViewObject *dv)
+dictview_repr(PyObject *self)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
PyObject *seq;
PyObject *result = NULL;
Py_ssize_t rc;
@@ -4721,8 +6034,9 @@ Done:
/*** dict_keys ***/
static PyObject *
-dictkeys_iter(_PyDictViewObject *dv)
+dictkeys_iter(PyObject *self)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
@@ -4730,22 +6044,23 @@ dictkeys_iter(_PyDictViewObject *dv)
}
static int
-dictkeys_contains(_PyDictViewObject *dv, PyObject *obj)
+dictkeys_contains(PyObject *self, PyObject *obj)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
if (dv->dv_dict == NULL)
return 0;
return PyDict_Contains((PyObject *)dv->dv_dict, obj);
}
static PySequenceMethods dictkeys_as_sequence = {
- (lenfunc)dictview_len, /* sq_length */
+ dictview_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
- (objobjproc)dictkeys_contains, /* sq_contains */
+ dictkeys_contains, /* sq_contains */
};
// Create a set object from dictviews object.
@@ -4785,7 +6100,7 @@ dictviews_sub(PyObject *self, PyObject *other)
}
static int
-dictitems_contains(_PyDictViewObject *dv, PyObject *obj);
+dictitems_contains(PyObject *dv, PyObject *obj);
PyObject *
_PyDictView_Intersect(PyObject* self, PyObject *other)
@@ -4795,7 +6110,7 @@ _PyDictView_Intersect(PyObject* self, PyObject *other)
PyObject *key;
Py_ssize_t len_self;
int rv;
- int (*dict_contains)(_PyDictViewObject *, PyObject *);
+ objobjproc dict_contains;
/* Python interpreter swaps parameters when dict view
is on right side of & */
@@ -4805,7 +6120,7 @@ _PyDictView_Intersect(PyObject* self, PyObject *other)
self = tmp;
}
- len_self = dictview_len((_PyDictViewObject *)self);
+ len_self = dictview_len(self);
/* if other is a set and self is smaller than other,
reuse set intersection logic */
@@ -4817,7 +6132,7 @@ _PyDictView_Intersect(PyObject* self, PyObject *other)
/* if other is another dict view, and it is bigger than self,
swap them */
if (PyDictViewSet_Check(other)) {
- Py_ssize_t len_other = dictview_len((_PyDictViewObject *)other);
+ Py_ssize_t len_other = dictview_len(other);
if (len_other > len_self) {
PyObject *tmp = other;
other = self;
@@ -4847,7 +6162,7 @@ _PyDictView_Intersect(PyObject* self, PyObject *other)
}
while ((key = PyIter_Next(it)) != NULL) {
- rv = dict_contains((_PyDictViewObject *)self, key);
+ rv = dict_contains(self, key);
if (rv < 0) {
goto error;
}
@@ -4888,14 +6203,12 @@ dictviews_or(PyObject* self, PyObject *other)
}
static PyObject *
-dictitems_xor(PyObject *self, PyObject *other)
+dictitems_xor_lock_held(PyObject *d1, PyObject *d2)
{
- assert(PyDictItems_Check(self));
- assert(PyDictItems_Check(other));
- PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict;
- PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict;
+ ASSERT_DICT_LOCKED(d1);
+ ASSERT_DICT_LOCKED(d2);
- PyObject *temp_dict = PyDict_Copy(d1);
+ PyObject *temp_dict = copy_lock_held(d1);
if (temp_dict == NULL) {
return NULL;
}
@@ -4973,6 +6286,22 @@ error:
return NULL;
}
+static PyObject *
+dictitems_xor(PyObject *self, PyObject *other)
+{
+ assert(PyDictItems_Check(self));
+ assert(PyDictItems_Check(other));
+ PyObject *d1 = (PyObject *)((_PyDictViewObject *)self)->dv_dict;
+ PyObject *d2 = (PyObject *)((_PyDictViewObject *)other)->dv_dict;
+
+ PyObject *res;
+ Py_BEGIN_CRITICAL_SECTION2(d1, d2);
+ res = dictitems_xor_lock_held(d1, d2);
+ Py_END_CRITICAL_SECTION2();
+
+ return res;
+}
+
static PyObject*
dictviews_xor(PyObject* self, PyObject *other)
{
@@ -5021,7 +6350,7 @@ dictviews_isdisjoint(PyObject *self, PyObject *other)
PyObject *item = NULL;
if (self == other) {
- if (dictview_len((_PyDictViewObject *)self) == 0)
+ if (dictview_len(self) == 0)
Py_RETURN_TRUE;
else
Py_RETURN_FALSE;
@@ -5030,7 +6359,7 @@ dictviews_isdisjoint(PyObject *self, PyObject *other)
/* Iterate over the shorter object (only if other is a set,
* because PySequence_Contains may be expensive otherwise): */
if (PyAnySet_Check(other) || PyDictViewSet_Check(other)) {
- Py_ssize_t len_self = dictview_len((_PyDictViewObject *)self);
+ Py_ssize_t len_self = dictview_len(self);
Py_ssize_t len_other = PyObject_Size(other);
if (len_other == -1)
return NULL;
@@ -5068,15 +6397,15 @@ dictviews_isdisjoint(PyObject *self, PyObject *other)
PyDoc_STRVAR(isdisjoint_doc,
"Return True if the view and the given iterable have a null intersection.");
-static PyObject* dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored));
+static PyObject* dictkeys_reversed(PyObject *dv, PyObject *Py_UNUSED(ignored));
PyDoc_STRVAR(reversed_keys_doc,
"Return a reverse iterator over the dict keys.");
static PyMethodDef dictkeys_methods[] = {
- {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O,
+ {"isdisjoint", dictviews_isdisjoint, METH_O,
isdisjoint_doc},
- {"__reversed__", _PyCFunction_CAST(dictkeys_reversed), METH_NOARGS,
+ {"__reversed__", dictkeys_reversed, METH_NOARGS,
reversed_keys_doc},
{NULL, NULL} /* sentinel */
};
@@ -5087,12 +6416,12 @@ PyTypeObject PyDictKeys_Type = {
sizeof(_PyDictViewObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)dictview_dealloc, /* tp_dealloc */
+ dictview_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)dictview_repr, /* tp_repr */
+ dictview_repr, /* tp_repr */
&dictviews_as_number, /* tp_as_number */
&dictkeys_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
@@ -5104,25 +6433,33 @@ PyTypeObject PyDictKeys_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
- (traverseproc)dictview_traverse, /* tp_traverse */
+ dictview_traverse, /* tp_traverse */
0, /* tp_clear */
dictview_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)dictkeys_iter, /* tp_iter */
+ dictkeys_iter, /* tp_iter */
0, /* tp_iternext */
dictkeys_methods, /* tp_methods */
.tp_getset = dictview_getset,
};
+/*[clinic input]
+dict.keys
+
+Return a set-like object providing a view on the dict's keys.
+[clinic start generated code]*/
+
static PyObject *
-dictkeys_new(PyObject *dict, PyObject *Py_UNUSED(ignored))
+dict_keys_impl(PyDictObject *self)
+/*[clinic end generated code: output=aac2830c62990358 input=42f48a7a771212a7]*/
{
- return _PyDictView_New(dict, &PyDictKeys_Type);
+ return _PyDictView_New((PyObject *)self, &PyDictKeys_Type);
}
static PyObject *
-dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored))
+dictkeys_reversed(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
@@ -5132,8 +6469,9 @@ dictkeys_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored))
/*** dict_items ***/
static PyObject *
-dictitems_iter(_PyDictViewObject *dv)
+dictitems_iter(PyObject *self)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
@@ -5141,8 +6479,9 @@ dictitems_iter(_PyDictViewObject *dv)
}
static int
-dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
+dictitems_contains(PyObject *self, PyObject *obj)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
int result;
PyObject *key, *value, *found;
if (dv->dv_dict == NULL)
@@ -5151,38 +6490,34 @@ dictitems_contains(_PyDictViewObject *dv, PyObject *obj)
return 0;
key = PyTuple_GET_ITEM(obj, 0);
value = PyTuple_GET_ITEM(obj, 1);
- found = PyDict_GetItemWithError((PyObject *)dv->dv_dict, key);
- if (found == NULL) {
- if (PyErr_Occurred())
- return -1;
- return 0;
+ result = PyDict_GetItemRef((PyObject *)dv->dv_dict, key, &found);
+ if (result == 1) {
+ result = PyObject_RichCompareBool(found, value, Py_EQ);
+ Py_DECREF(found);
}
- Py_INCREF(found);
- result = PyObject_RichCompareBool(found, value, Py_EQ);
- Py_DECREF(found);
return result;
}
static PySequenceMethods dictitems_as_sequence = {
- (lenfunc)dictview_len, /* sq_length */
+ dictview_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
- (objobjproc)dictitems_contains, /* sq_contains */
+ dictitems_contains, /* sq_contains */
};
-static PyObject* dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored));
+static PyObject* dictitems_reversed(PyObject *dv, PyObject *Py_UNUSED(ignored));
PyDoc_STRVAR(reversed_items_doc,
"Return a reverse iterator over the dict items.");
static PyMethodDef dictitems_methods[] = {
- {"isdisjoint", (PyCFunction)dictviews_isdisjoint, METH_O,
+ {"isdisjoint", dictviews_isdisjoint, METH_O,
isdisjoint_doc},
- {"__reversed__", (PyCFunction)dictitems_reversed, METH_NOARGS,
+ {"__reversed__", dictitems_reversed, METH_NOARGS,
reversed_items_doc},
{NULL, NULL} /* sentinel */
};
@@ -5193,12 +6528,12 @@ PyTypeObject PyDictItems_Type = {
sizeof(_PyDictViewObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)dictview_dealloc, /* tp_dealloc */
+ dictview_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)dictview_repr, /* tp_repr */
+ dictview_repr, /* tp_repr */
&dictviews_as_number, /* tp_as_number */
&dictitems_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
@@ -5210,25 +6545,33 @@ PyTypeObject PyDictItems_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
- (traverseproc)dictview_traverse, /* tp_traverse */
+ dictview_traverse, /* tp_traverse */
0, /* tp_clear */
dictview_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)dictitems_iter, /* tp_iter */
+ dictitems_iter, /* tp_iter */
0, /* tp_iternext */
dictitems_methods, /* tp_methods */
.tp_getset = dictview_getset,
};
+/*[clinic input]
+dict.items
+
+Return a set-like object providing a view on the dict's items.
+[clinic start generated code]*/
+
static PyObject *
-dictitems_new(PyObject *dict, PyObject *Py_UNUSED(ignored))
+dict_items_impl(PyDictObject *self)
+/*[clinic end generated code: output=88c7db7150c7909a input=87c822872eb71f5a]*/
{
- return _PyDictView_New(dict, &PyDictItems_Type);
+ return _PyDictView_New((PyObject *)self, &PyDictItems_Type);
}
static PyObject *
-dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored))
+dictitems_reversed(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
@@ -5238,8 +6581,9 @@ dictitems_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored))
/*** dict_values ***/
static PyObject *
-dictvalues_iter(_PyDictViewObject *dv)
+dictvalues_iter(PyObject *self)
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
@@ -5247,7 +6591,7 @@ dictvalues_iter(_PyDictViewObject *dv)
}
static PySequenceMethods dictvalues_as_sequence = {
- (lenfunc)dictview_len, /* sq_length */
+ dictview_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
@@ -5257,13 +6601,13 @@ static PySequenceMethods dictvalues_as_sequence = {
(objobjproc)0, /* sq_contains */
};
-static PyObject* dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored));
+static PyObject* dictvalues_reversed(PyObject *dv, PyObject *Py_UNUSED(ignored));
PyDoc_STRVAR(reversed_values_doc,
"Return a reverse iterator over the dict values.");
static PyMethodDef dictvalues_methods[] = {
- {"__reversed__", (PyCFunction)dictvalues_reversed, METH_NOARGS,
+ {"__reversed__", dictvalues_reversed, METH_NOARGS,
reversed_values_doc},
{NULL, NULL} /* sentinel */
};
@@ -5274,12 +6618,12 @@ PyTypeObject PyDictValues_Type = {
sizeof(_PyDictViewObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)dictview_dealloc, /* tp_dealloc */
+ dictview_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)dictview_repr, /* tp_repr */
+ dictview_repr, /* tp_repr */
0, /* tp_as_number */
&dictvalues_as_sequence, /* tp_as_sequence */
0, /* tp_as_mapping */
@@ -5291,25 +6635,33 @@ PyTypeObject PyDictValues_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
- (traverseproc)dictview_traverse, /* tp_traverse */
+ dictview_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)dictvalues_iter, /* tp_iter */
+ dictvalues_iter, /* tp_iter */
0, /* tp_iternext */
dictvalues_methods, /* tp_methods */
.tp_getset = dictview_getset,
};
+/*[clinic input]
+dict.values
+
+Return an object providing a view on the dict's values.
+[clinic start generated code]*/
+
static PyObject *
-dictvalues_new(PyObject *dict, PyObject *Py_UNUSED(ignored))
+dict_values_impl(PyDictObject *self)
+/*[clinic end generated code: output=ce9f2e9e8a959dd4 input=b46944f85493b230]*/
{
- return _PyDictView_New(dict, &PyDictValues_Type);
+ return _PyDictView_New((PyObject *)self, &PyDictValues_Type);
}
static PyObject *
-dictvalues_reversed(_PyDictViewObject *dv, PyObject *Py_UNUSED(ignored))
+dictvalues_reversed(PyObject *self, PyObject *Py_UNUSED(ignored))
{
+ _PyDictViewObject *dv = (_PyDictViewObject *)self;
if (dv->dv_dict == NULL) {
Py_RETURN_NONE;
}
@@ -5337,63 +6689,43 @@ _PyDict_NewKeysForClass(void)
return keys;
}
-#define CACHED_KEYS(tp) (((PyHeapTypeObject*)tp)->ht_cached_keys)
-
-static int
-init_inline_values(PyObject *obj, PyTypeObject *tp)
+void
+_PyObject_InitInlineValues(PyObject *obj, PyTypeObject *tp)
{
assert(tp->tp_flags & Py_TPFLAGS_HEAPTYPE);
- // assert(type->tp_dictoffset > 0); -- TO DO Update this assert.
+ assert(tp->tp_flags & Py_TPFLAGS_INLINE_VALUES);
assert(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
PyDictKeysObject *keys = CACHED_KEYS(tp);
assert(keys != NULL);
+ OBJECT_STAT_INC(inline_values);
+#ifdef Py_GIL_DISABLED
+ Py_ssize_t usable = _Py_atomic_load_ssize_relaxed(&keys->dk_usable);
+ if (usable > 1) {
+ LOCK_KEYS(keys);
+ if (keys->dk_usable > 1) {
+ _Py_atomic_store_ssize(&keys->dk_usable, keys->dk_usable - 1);
+ }
+ UNLOCK_KEYS(keys);
+ }
+#else
if (keys->dk_usable > 1) {
keys->dk_usable--;
}
+#endif
size_t size = shared_keys_usable_size(keys);
- PyDictValues *values = new_values(size);
- if (values == NULL) {
- PyErr_NoMemory();
- return -1;
- }
- assert(((uint8_t *)values)[-1] >= (size + 2));
- ((uint8_t *)values)[-2] = 0;
+ PyDictValues *values = _PyObject_InlineValues(obj);
+ assert(size < 256);
+ values->capacity = (uint8_t)size;
+ values->size = 0;
+ values->embedded = 1;
+ values->valid = 1;
for (size_t i = 0; i < size; i++) {
values->values[i] = NULL;
}
- _PyDictOrValues_SetValues(_PyObject_DictOrValuesPointer(obj), values);
- return 0;
-}
-
-int
-_PyObject_InitializeDict(PyObject *obj)
-{
- PyInterpreterState *interp = _PyInterpreterState_GET();
- PyTypeObject *tp = Py_TYPE(obj);
- if (tp->tp_dictoffset == 0) {
- return 0;
- }
- if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
- OBJECT_STAT_INC(new_values);
- return init_inline_values(obj, tp);
- }
- PyObject *dict;
- if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) {
- dictkeys_incref(CACHED_KEYS(tp));
- dict = new_dict_with_shared_keys(interp, CACHED_KEYS(tp));
- }
- else {
- dict = PyDict_New();
- }
- if (dict == NULL) {
- return -1;
- }
- PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
- *dictptr = dict;
- return 0;
+ _PyObject_ManagedDictPointer(obj)->dict = NULL;
}
-static PyObject *
+static PyDictObject *
make_dict_from_instance_attributes(PyInterpreterState *interp,
PyDictKeysObject *keys, PyDictValues *values)
{
@@ -5408,82 +6740,245 @@ make_dict_from_instance_attributes(PyInterpreterState *interp,
track += _PyObject_GC_MAY_BE_TRACKED(val);
}
}
- PyObject *res = new_dict(interp, keys, values, used, 0);
+ PyDictObject *res = (PyDictObject *)new_dict(interp, keys, values, used, 0);
if (track && res) {
_PyObject_GC_TRACK(res);
}
return res;
}
-PyObject *
-_PyObject_MakeDictFromInstanceAttributes(PyObject *obj, PyDictValues *values)
+PyDictObject *
+_PyObject_MaterializeManagedDict_LockHeld(PyObject *obj)
{
- PyInterpreterState *interp = _PyInterpreterState_GET();
- PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
+ ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(obj);
+
OBJECT_STAT_INC(dict_materialized_on_request);
- return make_dict_from_instance_attributes(interp, keys, values);
+
+ PyDictValues *values = _PyObject_InlineValues(obj);
+ PyDictObject *dict;
+ if (values->valid) {
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
+ dict = make_dict_from_instance_attributes(interp, keys, values);
+ }
+ else {
+ dict = (PyDictObject *)PyDict_New();
+ }
+ FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict,
+ dict);
+ return dict;
+}
+
+PyDictObject *
+_PyObject_MaterializeManagedDict(PyObject *obj)
+{
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ if (dict != NULL) {
+ return dict;
+ }
+
+ Py_BEGIN_CRITICAL_SECTION(obj);
+
+#ifdef Py_GIL_DISABLED
+ dict = _PyObject_GetManagedDict(obj);
+ if (dict != NULL) {
+ // We raced with another thread creating the dict
+ goto exit;
+ }
+#endif
+ dict = _PyObject_MaterializeManagedDict_LockHeld(obj);
+
+#ifdef Py_GIL_DISABLED
+exit:
+#endif
+ Py_END_CRITICAL_SECTION();
+ return dict;
}
int
-_PyObject_StoreInstanceAttribute(PyObject *obj, PyDictValues *values,
+_PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value)
+{
+ if (value == NULL) {
+ Py_hash_t hash = _PyObject_HashFast(name);
+ if (hash == -1) {
+ return -1;
+ }
+ return delitem_knownhash_lock_held((PyObject *)dict, name, hash);
+ } else {
+ return setitem_lock_held(dict, name, value);
+ }
+}
+
+// Called with either the object's lock or the dict's lock held
+// depending on whether or not a dict has been materialized for
+// the object.
+static int
+store_instance_attr_lock_held(PyObject *obj, PyDictValues *values,
PyObject *name, PyObject *value)
{
- PyInterpreterState *interp = _PyInterpreterState_GET();
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
assert(keys != NULL);
assert(values != NULL);
- assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
Py_ssize_t ix = DKIX_EMPTY;
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ assert(dict == NULL || ((PyDictObject *)dict)->ma_values == values);
if (PyUnicode_CheckExact(name)) {
- ix = insert_into_dictkeys(keys, name);
- }
- if (ix == DKIX_EMPTY) {
+ Py_hash_t hash = unicode_get_hash(name);
+ if (hash == -1) {
+ hash = PyUnicode_Type.tp_hash(name);
+ assert(hash != -1);
+ }
+
+ ix = insert_split_key(keys, name, hash);
+
#ifdef Py_STATS
- if (PyUnicode_CheckExact(name)) {
- if (shared_keys_usable_size(keys) == SHARED_KEYS_MAX_SIZE) {
- OBJECT_STAT_INC(dict_materialized_too_big);
+ if (ix == DKIX_EMPTY) {
+ if (PyUnicode_CheckExact(name)) {
+ if (shared_keys_usable_size(keys) == SHARED_KEYS_MAX_SIZE) {
+ OBJECT_STAT_INC(dict_materialized_too_big);
+ }
+ else {
+ OBJECT_STAT_INC(dict_materialized_new_key);
+ }
}
else {
- OBJECT_STAT_INC(dict_materialized_new_key);
+ OBJECT_STAT_INC(dict_materialized_str_subclass);
}
}
- else {
- OBJECT_STAT_INC(dict_materialized_str_subclass);
- }
#endif
- PyObject *dict = make_dict_from_instance_attributes(
- interp, keys, values);
+ }
+
+ if (ix == DKIX_EMPTY) {
+ int res;
if (dict == NULL) {
- return -1;
- }
- _PyObject_DictOrValuesPointer(obj)->dict = dict;
- if (value == NULL) {
- return PyDict_DelItem(dict, name);
- }
- else {
- return PyDict_SetItem(dict, name, value);
+ // Make the dict but don't publish it in the object
+ // so that no one else will see it.
+ dict = make_dict_from_instance_attributes(PyInterpreterState_Get(), keys, values);
+ if (dict == NULL ||
+ _PyDict_SetItem_LockHeld(dict, name, value) < 0) {
+ Py_XDECREF(dict);
+ return -1;
+ }
+
+ FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict,
+ (PyDictObject *)dict);
+ return 0;
}
+
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(dict);
+
+ res = _PyDict_SetItem_LockHeld(dict, name, value);
+ return res;
}
+
PyObject *old_value = values->values[ix];
- values->values[ix] = Py_XNewRef(value);
- if (old_value == NULL) {
- if (value == NULL) {
- PyErr_Format(PyExc_AttributeError,
- "'%.100s' object has no attribute '%U'",
- Py_TYPE(obj)->tp_name, name);
- return -1;
+ if (old_value == NULL && value == NULL) {
+ PyErr_Format(PyExc_AttributeError,
+ "'%.100s' object has no attribute '%U'",
+ Py_TYPE(obj)->tp_name, name);
+ return -1;
+ }
+
+ if (dict) {
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ PyDict_WatchEvent event = (old_value == NULL ? PyDict_EVENT_ADDED :
+ value == NULL ? PyDict_EVENT_DELETED :
+ PyDict_EVENT_MODIFIED);
+ _PyDict_NotifyEvent(interp, event, dict, name, value);
+ if (value) {
+ MAINTAIN_TRACKING(dict, name, value);
}
+ }
+
+ FT_ATOMIC_STORE_PTR_RELEASE(values->values[ix], Py_XNewRef(value));
+
+ if (old_value == NULL) {
_PyDictValues_AddToInsertionOrder(values, ix);
+ if (dict) {
+ assert(dict->ma_values == values);
+ STORE_USED(dict, dict->ma_used + 1);
+ }
}
else {
if (value == NULL) {
delete_index_from_values(values, ix);
+ if (dict) {
+ assert(dict->ma_values == values);
+ STORE_USED(dict, dict->ma_used - 1);
+ }
}
Py_DECREF(old_value);
}
return 0;
}
+static inline int
+store_instance_attr_dict(PyObject *obj, PyDictObject *dict, PyObject *name, PyObject *value)
+{
+ PyDictValues *values = _PyObject_InlineValues(obj);
+ int res;
+ Py_BEGIN_CRITICAL_SECTION(dict);
+ if (dict->ma_values == values) {
+ res = store_instance_attr_lock_held(obj, values, name, value);
+ }
+ else {
+ res = _PyDict_SetItem_LockHeld(dict, name, value);
+ }
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
+int
+_PyObject_StoreInstanceAttribute(PyObject *obj, PyObject *name, PyObject *value)
+{
+ PyDictValues *values = _PyObject_InlineValues(obj);
+ if (!FT_ATOMIC_LOAD_UINT8(values->valid)) {
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ if (dict == NULL) {
+ dict = (PyDictObject *)PyObject_GenericGetDict(obj, NULL);
+ if (dict == NULL) {
+ return -1;
+ }
+ int res = store_instance_attr_dict(obj, dict, name, value);
+ Py_DECREF(dict);
+ return res;
+ }
+ return store_instance_attr_dict(obj, dict, name, value);
+ }
+
+#ifdef Py_GIL_DISABLED
+ // We have a valid inline values, at least for now... There are two potential
+ // races with having the values become invalid. One is the dictionary
+ // being detached from the object. The other is if someone is inserting
+ // into the dictionary directly and therefore causing it to resize.
+ //
+ // If we haven't materialized the dictionary yet we lock on the object, which
+ // will also be used to prevent the dictionary from being materialized while
+ // we're doing the insertion. If we race and the dictionary gets created
+ // then we'll need to release the object lock and lock the dictionary to
+ // prevent resizing.
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ if (dict == NULL) {
+ int res;
+ Py_BEGIN_CRITICAL_SECTION(obj);
+ dict = _PyObject_GetManagedDict(obj);
+
+ if (dict == NULL) {
+ res = store_instance_attr_lock_held(obj, values, name, value);
+ }
+ Py_END_CRITICAL_SECTION();
+
+ if (dict == NULL) {
+ return res;
+ }
+ }
+ return store_instance_attr_dict(obj, dict, name, value);
+#else
+ return store_instance_attr_lock_held(obj, values, name, value);
+#endif
+}
+
/* Sanity check for managed dicts */
#if 0
#define CHECK(val) assert(val); if (!(val)) { return 0; }
@@ -5493,9 +6988,9 @@ _PyObject_ManagedDictValidityCheck(PyObject *obj)
{
PyTypeObject *tp = Py_TYPE(obj);
CHECK(tp->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(*dorv_ptr)) {
- PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
+ PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj);
+ if (_PyManagedDictPointer_IsValues(*managed_dict)) {
+ PyDictValues *values = _PyManagedDictPointer_GetValues(*managed_dict);
int size = ((uint8_t *)values)[-2];
int count = 0;
PyDictKeysObject *keys = CACHED_KEYS(tp);
@@ -5507,27 +7002,87 @@ _PyObject_ManagedDictValidityCheck(PyObject *obj)
CHECK(size == count);
}
else {
- if (dorv_ptr->dict != NULL) {
- CHECK(PyDict_Check(dorv_ptr->dict));
+ if (managed_dict->dict != NULL) {
+ CHECK(PyDict_Check(managed_dict->dict));
}
}
return 1;
}
#endif
-PyObject *
-_PyObject_GetInstanceAttribute(PyObject *obj, PyDictValues *values,
- PyObject *name)
+// Attempts to get an instance attribute from the inline values. Returns true
+// if successful, or false if the caller needs to lookup in the dictionary.
+bool
+_PyObject_TryGetInstanceAttribute(PyObject *obj, PyObject *name, PyObject **attr)
{
assert(PyUnicode_CheckExact(name));
+ PyDictValues *values = _PyObject_InlineValues(obj);
+ if (!FT_ATOMIC_LOAD_UINT8(values->valid)) {
+ return false;
+ }
+
PyDictKeysObject *keys = CACHED_KEYS(Py_TYPE(obj));
assert(keys != NULL);
- Py_ssize_t ix = _PyDictKeys_StringLookup(keys, name);
+ Py_ssize_t ix = _PyDictKeys_StringLookupSplit(keys, name);
if (ix == DKIX_EMPTY) {
- return NULL;
+ *attr = NULL;
+ return true;
}
+
+#ifdef Py_GIL_DISABLED
+ PyObject *value = _Py_atomic_load_ptr_acquire(&values->values[ix]);
+ if (value == NULL || _Py_TryIncrefCompare(&values->values[ix], value)) {
+ *attr = value;
+ return true;
+ }
+
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ if (dict == NULL) {
+ // No dict, lock the object to prevent one from being
+ // materialized...
+ bool success = false;
+ Py_BEGIN_CRITICAL_SECTION(obj);
+
+ dict = _PyObject_GetManagedDict(obj);
+ if (dict == NULL) {
+ // Still no dict, we can read from the values
+ assert(values->valid);
+ value = values->values[ix];
+ *attr = _Py_XNewRefWithLock(value);
+ success = true;
+ }
+
+ Py_END_CRITICAL_SECTION();
+
+ if (success) {
+ return true;
+ }
+ }
+
+ // We have a dictionary, we'll need to lock it to prevent
+ // the values from being resized.
+ assert(dict != NULL);
+
+ bool success;
+ Py_BEGIN_CRITICAL_SECTION(dict);
+
+ if (dict->ma_values == values && FT_ATOMIC_LOAD_UINT8(values->valid)) {
+ value = _Py_atomic_load_ptr_relaxed(&values->values[ix]);
+ *attr = _Py_XNewRefWithLock(value);
+ success = true;
+ } else {
+ // Caller needs to lookup from the dictionary
+ success = false;
+ }
+
+ Py_END_CRITICAL_SECTION();
+
+ return success;
+#else
PyObject *value = values->values[ix];
- return Py_XNewRef(value);
+ *attr = Py_XNewRef(value);
+ return true;
+#endif
}
int
@@ -5537,121 +7092,257 @@ _PyObject_IsInstanceDictEmpty(PyObject *obj)
if (tp->tp_dictoffset == 0) {
return 1;
}
- PyObject *dict;
- if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(dorv)) {
+ PyDictObject *dict;
+ if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
+ PyDictValues *values = _PyObject_InlineValues(obj);
+ if (FT_ATOMIC_LOAD_UINT8(values->valid)) {
PyDictKeysObject *keys = CACHED_KEYS(tp);
for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
- if (_PyDictOrValues_GetValues(dorv)->values[i] != NULL) {
+ if (FT_ATOMIC_LOAD_PTR_RELAXED(values->values[i]) != NULL) {
return 0;
}
}
return 1;
}
- dict = _PyDictOrValues_GetDict(dorv);
+ dict = _PyObject_GetManagedDict(obj);
+ }
+ else if (tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
+ dict = _PyObject_GetManagedDict(obj);
}
else {
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
- dict = *dictptr;
+ dict = (PyDictObject *)*dictptr;
}
if (dict == NULL) {
return 1;
}
- return ((PyDictObject *)dict)->ma_used == 0;
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(((PyDictObject *)dict)->ma_used) == 0;
}
-void
-_PyObject_FreeInstanceAttributes(PyObject *self)
+int
+PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
{
- PyTypeObject *tp = Py_TYPE(self);
- assert(Py_TYPE(self)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(self);
- if (!_PyDictOrValues_IsValues(dorv)) {
- return;
+ PyTypeObject *tp = Py_TYPE(obj);
+ if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
+ return 0;
}
- PyDictValues *values = _PyDictOrValues_GetValues(dorv);
- PyDictKeysObject *keys = CACHED_KEYS(tp);
- for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
- Py_XDECREF(values->values[i]);
+ PyDictObject *dict = _PyObject_ManagedDictPointer(obj)->dict;
+ if (dict != NULL) {
+ // GH-130327: If there's a managed dictionary available, we should
+ // *always* traverse it. The dict is responsible for traversing the
+ // inline values if it points to them.
+ Py_VISIT(dict);
+ }
+ else if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
+ PyDictValues *values = _PyObject_InlineValues(obj);
+ if (values->valid) {
+ for (Py_ssize_t i = 0; i < values->capacity; i++) {
+ Py_VISIT(values->values[i]);
+ }
+ }
+ }
+ return 0;
+}
+
+static void
+set_dict_inline_values(PyObject *obj, PyDictObject *new_dict)
+{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(obj);
+
+ PyDictValues *values = _PyObject_InlineValues(obj);
+
+ Py_XINCREF(new_dict);
+ FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict, new_dict);
+
+ if (values->valid) {
+ FT_ATOMIC_STORE_UINT8(values->valid, 0);
+ for (Py_ssize_t i = 0; i < values->capacity; i++) {
+ Py_CLEAR(values->values[i]);
+ }
}
- free_values(values);
}
int
-_PyObject_VisitManagedDict(PyObject *obj, visitproc visit, void *arg)
+_PyObject_SetManagedDict(PyObject *obj, PyObject *new_dict)
{
+ assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ assert(_PyObject_InlineValuesConsistencyCheck(obj));
+ int err = 0;
PyTypeObject *tp = Py_TYPE(obj);
- if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
- return 0;
- }
- assert(tp->tp_dictoffset);
- PyDictOrValues dorv = *_PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(dorv)) {
- PyDictValues *values = _PyDictOrValues_GetValues(dorv);
- PyDictKeysObject *keys = CACHED_KEYS(tp);
- for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
- Py_VISIT(values->values[i]);
+ if (tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ if (dict == NULL) {
+#ifdef Py_GIL_DISABLED
+ Py_BEGIN_CRITICAL_SECTION(obj);
+
+ dict = _PyObject_ManagedDictPointer(obj)->dict;
+ if (dict == NULL) {
+ set_dict_inline_values(obj, (PyDictObject *)new_dict);
+ }
+
+ Py_END_CRITICAL_SECTION();
+
+ if (dict == NULL) {
+ return 0;
+ }
+#else
+ set_dict_inline_values(obj, (PyDictObject *)new_dict);
+ return 0;
+#endif
+ }
+
+ Py_BEGIN_CRITICAL_SECTION2(dict, obj);
+
+ // We've locked dict, but the actual dict could have changed
+ // since we locked it.
+ dict = _PyObject_ManagedDictPointer(obj)->dict;
+ err = _PyDict_DetachFromObject(dict, obj);
+ if (err == 0) {
+ FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
+ (PyDictObject *)Py_XNewRef(new_dict));
+ }
+ Py_END_CRITICAL_SECTION2();
+
+ if (err == 0) {
+ Py_XDECREF(dict);
}
}
else {
- PyObject *dict = _PyDictOrValues_GetDict(dorv);
- Py_VISIT(dict);
+ PyDictObject *dict;
+
+ Py_BEGIN_CRITICAL_SECTION(obj);
+
+ dict = _PyObject_ManagedDictPointer(obj)->dict;
+
+ FT_ATOMIC_STORE_PTR(_PyObject_ManagedDictPointer(obj)->dict,
+ (PyDictObject *)Py_XNewRef(new_dict));
+
+ Py_END_CRITICAL_SECTION();
+
+ Py_XDECREF(dict);
}
- return 0;
+ assert(_PyObject_InlineValuesConsistencyCheck(obj));
+ return err;
}
void
-_PyObject_ClearManagedDict(PyObject *obj)
+PyObject_ClearManagedDict(PyObject *obj)
{
- PyTypeObject *tp = Py_TYPE(obj);
- if((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
- return;
+ if (_PyObject_SetManagedDict(obj, NULL) < 0) {
+ PyErr_WriteUnraisable(NULL);
}
- PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(*dorv_ptr)) {
- PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
- PyDictKeysObject *keys = CACHED_KEYS(tp);
- for (Py_ssize_t i = 0; i < keys->dk_nentries; i++) {
- Py_CLEAR(values->values[i]);
+}
+
+int
+_PyDict_DetachFromObject(PyDictObject *mp, PyObject *obj)
+{
+ ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(obj);
+ assert(_PyObject_ManagedDictPointer(obj)->dict == mp);
+ assert(_PyObject_InlineValuesConsistencyCheck(obj));
+
+ if (FT_ATOMIC_LOAD_PTR_RELAXED(mp->ma_values) != _PyObject_InlineValues(obj)) {
+ return 0;
+ }
+
+ // We could be called with an unlocked dict when the caller knows the
+ // values are already detached, so we assert after inline values check.
+ ASSERT_WORLD_STOPPED_OR_OBJ_LOCKED(mp);
+ assert(mp->ma_values->embedded == 1);
+ assert(mp->ma_values->valid == 1);
+ assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES);
+
+ PyDictValues *values = copy_values(mp->ma_values);
+
+ if (values == NULL) {
+ /* Out of memory. Clear the dict */
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ PyDictKeysObject *oldkeys = mp->ma_keys;
+ set_keys(mp, Py_EMPTY_KEYS);
+ dictkeys_decref(interp, oldkeys, IS_DICT_SHARED(mp));
+ STORE_USED(mp, 0);
+ PyErr_NoMemory();
+ return -1;
+ }
+ mp->ma_values = values;
+
+ FT_ATOMIC_STORE_UINT8(_PyObject_InlineValues(obj)->valid, 0);
+
+ assert(_PyObject_InlineValuesConsistencyCheck(obj));
+ ASSERT_CONSISTENT(mp);
+ return 0;
+}
+
+static inline PyObject *
+ensure_managed_dict(PyObject *obj)
+{
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ if (dict == NULL) {
+ PyTypeObject *tp = Py_TYPE(obj);
+ if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
+ FT_ATOMIC_LOAD_UINT8(_PyObject_InlineValues(obj)->valid)) {
+ dict = _PyObject_MaterializeManagedDict(obj);
+ }
+ else {
+#ifdef Py_GIL_DISABLED
+ // Check again that we're not racing with someone else creating the dict
+ Py_BEGIN_CRITICAL_SECTION(obj);
+ dict = _PyObject_GetManagedDict(obj);
+ if (dict != NULL) {
+ goto done;
+ }
+#endif
+ dict = (PyDictObject *)new_dict_with_shared_keys(_PyInterpreterState_GET(),
+ CACHED_KEYS(tp));
+ FT_ATOMIC_STORE_PTR_RELEASE(_PyObject_ManagedDictPointer(obj)->dict,
+ (PyDictObject *)dict);
+
+#ifdef Py_GIL_DISABLED
+done:
+ Py_END_CRITICAL_SECTION();
+#endif
}
- dorv_ptr->dict = NULL;
- free_values(values);
}
- else {
- PyObject *dict = dorv_ptr->dict;
- if (dict) {
- dorv_ptr->dict = NULL;
- Py_DECREF(dict);
+ return (PyObject *)dict;
+}
+
+static inline PyObject *
+ensure_nonmanaged_dict(PyObject *obj, PyObject **dictptr)
+{
+ PyDictKeysObject *cached;
+
+ PyObject *dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*dictptr);
+ if (dict == NULL) {
+#ifdef Py_GIL_DISABLED
+ Py_BEGIN_CRITICAL_SECTION(obj);
+ dict = *dictptr;
+ if (dict != NULL) {
+ goto done;
}
+#endif
+ PyTypeObject *tp = Py_TYPE(obj);
+ if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) {
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ assert(!_PyType_HasFeature(tp, Py_TPFLAGS_INLINE_VALUES));
+ dict = new_dict_with_shared_keys(interp, cached);
+ }
+ else {
+ dict = PyDict_New();
+ }
+ FT_ATOMIC_STORE_PTR_RELEASE(*dictptr, dict);
+#ifdef Py_GIL_DISABLED
+done:
+ Py_END_CRITICAL_SECTION();
+#endif
}
+ return dict;
}
PyObject *
PyObject_GenericGetDict(PyObject *obj, void *context)
{
- PyObject *dict;
- PyInterpreterState *interp = _PyInterpreterState_GET();
PyTypeObject *tp = Py_TYPE(obj);
if (_PyType_HasFeature(tp, Py_TPFLAGS_MANAGED_DICT)) {
- PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(*dorv_ptr)) {
- PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
- OBJECT_STAT_INC(dict_materialized_on_request);
- dict = make_dict_from_instance_attributes(
- interp, CACHED_KEYS(tp), values);
- if (dict != NULL) {
- dorv_ptr->dict = dict;
- }
- }
- else {
- dict = _PyDictOrValues_GetDict(*dorv_ptr);
- if (dict == NULL) {
- dictkeys_incref(CACHED_KEYS(tp));
- dict = new_dict_with_shared_keys(interp, CACHED_KEYS(tp));
- dorv_ptr->dict = dict;
- }
- }
+ return Py_XNewRef(ensure_managed_dict(obj));
}
else {
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
@@ -5660,63 +7351,28 @@ PyObject_GenericGetDict(PyObject *obj, void *context)
"This object has no __dict__");
return NULL;
}
- dict = *dictptr;
- if (dict == NULL) {
- PyTypeObject *tp = Py_TYPE(obj);
- if (_PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE) && CACHED_KEYS(tp)) {
- dictkeys_incref(CACHED_KEYS(tp));
- *dictptr = dict = new_dict_with_shared_keys(
- interp, CACHED_KEYS(tp));
- }
- else {
- *dictptr = dict = PyDict_New();
- }
- }
+
+ return Py_XNewRef(ensure_nonmanaged_dict(obj, dictptr));
}
- return Py_XNewRef(dict);
}
int
-_PyObjectDict_SetItem(PyTypeObject *tp, PyObject **dictptr,
+_PyObjectDict_SetItem(PyTypeObject *tp, PyObject *obj, PyObject **dictptr,
PyObject *key, PyObject *value)
{
PyObject *dict;
int res;
- PyDictKeysObject *cached;
- PyInterpreterState *interp = _PyInterpreterState_GET();
assert(dictptr != NULL);
- if ((tp->tp_flags & Py_TPFLAGS_HEAPTYPE) && (cached = CACHED_KEYS(tp))) {
- assert(dictptr != NULL);
- dict = *dictptr;
- if (dict == NULL) {
- dictkeys_incref(cached);
- dict = new_dict_with_shared_keys(interp, cached);
- if (dict == NULL)
- return -1;
- *dictptr = dict;
- }
- if (value == NULL) {
- res = PyDict_DelItem(dict, key);
- }
- else {
- res = PyDict_SetItem(dict, key, value);
- }
- } else {
- dict = *dictptr;
- if (dict == NULL) {
- dict = PyDict_New();
- if (dict == NULL)
- return -1;
- *dictptr = dict;
- }
- if (value == NULL) {
- res = PyDict_DelItem(dict, key);
- } else {
- res = PyDict_SetItem(dict, key, value);
- }
+ dict = ensure_nonmanaged_dict(obj, dictptr);
+ if (dict == NULL) {
+ return -1;
}
+
+ Py_BEGIN_CRITICAL_SECTION(dict);
+ res = _PyDict_SetItem_LockHeld((PyDictObject *)dict, key, value);
ASSERT_CONSISTENT(dict);
+ Py_END_CRITICAL_SECTION();
return res;
}
@@ -5724,7 +7380,7 @@ void
_PyDictKeys_DecRef(PyDictKeysObject *keys)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
- dictkeys_decref(interp, keys);
+ dictkeys_decref(interp, keys, false);
}
uint32_t _PyDictKeys_GetVersionForCurrentState(PyInterpreterState *interp,
@@ -5790,7 +7446,8 @@ PyDict_AddWatcher(PyDict_WatchCallback callback)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
- for (int i = 0; i < DICT_MAX_WATCHERS; i++) {
+ /* Start at 2, as 0 and 1 are reserved for CPython */
+ for (int i = 2; i < DICT_MAX_WATCHERS; i++) {
if (!interp->dict_state.watchers[i]) {
interp->dict_state.watchers[i] = callback;
return i;
@@ -5840,16 +7497,32 @@ _PyDict_SendEvent(int watcher_bits,
// unraisablehook keep a reference to it, so we don't pass the
// dict as context, just an informative string message. Dict
// repr can call arbitrary code, so we invent a simpler version.
- PyObject *context = PyUnicode_FromFormat(
- "%s watcher callback for <dict at %p>",
+ PyErr_FormatUnraisable(
+ "Exception ignored in %s watcher callback for <dict at %p>",
dict_event_name(event), mp);
- if (context == NULL) {
- context = Py_NewRef(Py_None);
- }
- PyErr_WriteUnraisable(context);
- Py_DECREF(context);
}
}
watcher_bits >>= 1;
}
}
+
+#ifndef NDEBUG
+static int
+_PyObject_InlineValuesConsistencyCheck(PyObject *obj)
+{
+ if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) == 0) {
+ return 1;
+ }
+ assert(Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT);
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ if (dict == NULL) {
+ return 1;
+ }
+ if (dict->ma_values == _PyObject_InlineValues(obj) ||
+ _PyObject_InlineValues(obj)->valid == 0) {
+ return 1;
+ }
+ assert(0);
+ return 0;
+}
+#endif
diff --git a/contrib/tools/python3/Objects/enumobject.c b/contrib/tools/python3/Objects/enumobject.c
index c9d90584c26..bffe7172a39 100644
--- a/contrib/tools/python3/Objects/enumobject.c
+++ b/contrib/tools/python3/Objects/enumobject.c
@@ -3,6 +3,7 @@
#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_long.h" // _PyLong_GetOne()
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
#include "pycore_object.h" // _PyObject_GC_TRACK()
#include "clinic/enumobject.c.h"
@@ -144,7 +145,7 @@ enumerate_vectorcall(PyObject *type, PyObject *const *args,
}
PyErr_Format(PyExc_TypeError,
- "enumerate() takes at most 2 arguments (%d given)", nargs + nkwargs);
+ "enumerate() takes at most 2 arguments (%zd given)", nargs + nkwargs);
return NULL;
}
diff --git a/contrib/tools/python3/Objects/exceptions.c b/contrib/tools/python3/Objects/exceptions.c
index c579563db75..1eb2a3bbe24 100644
--- a/contrib/tools/python3/Objects/exceptions.c
+++ b/contrib/tools/python3/Objects/exceptions.c
@@ -4,15 +4,16 @@
* Thanks go to Tim Peters and Michael Hudson for debugging.
*/
-#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdbool.h>
+#include "pycore_abstract.h" // _PyObject_RealIsSubclass()
#include "pycore_ceval.h" // _Py_EnterRecursiveCall
-#include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException
#include "pycore_exceptions.h" // struct _Py_exc_state
#include "pycore_initconfig.h"
+#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_object.h"
-#include "structmember.h" // PyMemberDef
+#include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException
+
#include "osdefs.h" // SEP
@@ -208,7 +209,7 @@ BaseException_add_note(PyObject *self, PyObject *note)
}
PyObject *notes;
- if (_PyObject_LookupAttr(self, &_Py_ID(__notes__), &notes) < 0) {
+ if (PyObject_GetOptionalAttr(self, &_Py_ID(__notes__), &notes) < 0) {
return NULL;
}
if (notes == NULL) {
@@ -439,7 +440,7 @@ PyExceptionClass_Name(PyObject *ob)
}
static struct PyMemberDef BaseException_members[] = {
- {"__suppress_context__", T_BOOL,
+ {"__suppress_context__", Py_T_BOOL,
offsetof(PyBaseExceptionObject, suppress_context)},
{NULL}
};
@@ -509,10 +510,10 @@ static PyTypeObject _PyExc_ ## EXCNAME = { \
}; \
PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
-#define MiddlingExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCDOC) \
-static PyTypeObject _PyExc_ ## EXCNAME = { \
+#define MiddlingExtendsExceptionEx(EXCBASE, EXCNAME, PYEXCNAME, EXCSTORE, EXCDOC) \
+PyTypeObject _PyExc_ ## EXCNAME = { \
PyVarObject_HEAD_INIT(NULL, 0) \
- # EXCNAME, \
+ # PYEXCNAME, \
sizeof(Py ## EXCSTORE ## Object), \
0, (destructor)EXCSTORE ## _dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 0, 0, \
@@ -521,18 +522,22 @@ static PyTypeObject _PyExc_ ## EXCNAME = { \
(inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, 0, 0, 0, &_ ## EXCBASE, \
0, 0, 0, offsetof(Py ## EXCSTORE ## Object, dict), \
(initproc)EXCSTORE ## _init, 0, 0, \
-}; \
-PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
+};
+
+#define MiddlingExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCDOC) \
+ static MiddlingExtendsExceptionEx( \
+ EXCBASE, EXCNAME, EXCNAME, EXCSTORE, EXCDOC); \
+ PyObject *PyExc_ ## EXCNAME = (PyObject *)&_PyExc_ ## EXCNAME
#define ComplexExtendsException(EXCBASE, EXCNAME, EXCSTORE, EXCNEW, \
EXCMETHODS, EXCMEMBERS, EXCGETSET, \
- EXCSTR, EXCDOC) \
+ EXCSTR, EXCREPR, EXCDOC) \
static PyTypeObject _PyExc_ ## EXCNAME = { \
PyVarObject_HEAD_INIT(NULL, 0) \
# EXCNAME, \
sizeof(Py ## EXCSTORE ## Object), 0, \
- (destructor)EXCSTORE ## _dealloc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
- (reprfunc)EXCSTR, 0, 0, 0, \
+ (destructor)EXCSTORE ## _dealloc, 0, 0, 0, 0, (reprfunc)EXCREPR, 0, 0, 0, \
+ 0, 0, (reprfunc)EXCSTR, 0, 0, 0, \
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, \
PyDoc_STR(EXCDOC), (traverseproc)EXCSTORE ## _traverse, \
(inquiry)EXCSTORE ## _clear, 0, 0, 0, 0, EXCMETHODS, \
@@ -569,7 +574,7 @@ SimpleExtendsException(PyExc_Exception, StopAsyncIteration,
*/
static PyMemberDef StopIteration_members[] = {
- {"value", T_OBJECT, offsetof(PyStopIterationObject, value), 0,
+ {"value", _Py_T_OBJECT, offsetof(PyStopIterationObject, value), 0,
PyDoc_STR("generator return value")},
{NULL} /* Sentinel */
};
@@ -614,7 +619,7 @@ StopIteration_traverse(PyStopIterationObject *self, visitproc visit, void *arg)
}
ComplexExtendsException(PyExc_Exception, StopIteration, StopIteration,
- 0, 0, StopIteration_members, 0, 0,
+ 0, 0, StopIteration_members, 0, 0, 0,
"Signal the end from iterator.__next__().");
@@ -671,13 +676,13 @@ SystemExit_traverse(PySystemExitObject *self, visitproc visit, void *arg)
}
static PyMemberDef SystemExit_members[] = {
- {"code", T_OBJECT, offsetof(PySystemExitObject, code), 0,
+ {"code", _Py_T_OBJECT, offsetof(PySystemExitObject, code), 0,
PyDoc_STR("exception code")},
{NULL} /* Sentinel */
};
ComplexExtendsException(PyExc_BaseException, SystemExit, SystemExit,
- 0, 0, SystemExit_members, 0, 0,
+ 0, 0, SystemExit_members, 0, 0, 0,
"Request to exit from the interpreter.");
/*
@@ -702,6 +707,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyObject *message = NULL;
PyObject *exceptions = NULL;
+ PyObject *exceptions_str = NULL;
if (!PyArg_ParseTuple(args,
"UO:BaseExceptionGroup.__new__",
@@ -717,6 +723,18 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return NULL;
}
+ /* Save initial exceptions sequence as a string in case sequence is mutated */
+ if (!PyList_Check(exceptions) && !PyTuple_Check(exceptions)) {
+ exceptions_str = PyObject_Repr(exceptions);
+ if (exceptions_str == NULL) {
+ /* We don't hold a reference to exceptions, so clear it before
+ * attempting a decref in the cleanup.
+ */
+ exceptions = NULL;
+ goto error;
+ }
+ }
+
exceptions = PySequence_Tuple(exceptions);
if (!exceptions) {
return NULL;
@@ -741,7 +759,7 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (!PyExceptionInstance_Check(exc)) {
PyErr_Format(
PyExc_ValueError,
- "Item %d of second argument (exceptions) is not an exception",
+ "Item %zd of second argument (exceptions) is not an exception",
i);
goto error;
}
@@ -800,9 +818,11 @@ BaseExceptionGroup_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
self->msg = Py_NewRef(message);
self->excs = exceptions;
+ self->excs_str = exceptions_str;
return (PyObject*)self;
error:
- Py_DECREF(exceptions);
+ Py_XDECREF(exceptions);
+ Py_XDECREF(exceptions_str);
return NULL;
}
@@ -841,6 +861,7 @@ BaseExceptionGroup_clear(PyBaseExceptionGroupObject *self)
{
Py_CLEAR(self->msg);
Py_CLEAR(self->excs);
+ Py_CLEAR(self->excs_str);
return BaseException_clear((PyBaseExceptionObject *)self);
}
@@ -858,6 +879,7 @@ BaseExceptionGroup_traverse(PyBaseExceptionGroupObject *self,
{
Py_VISIT(self->msg);
Py_VISIT(self->excs);
+ Py_VISIT(self->excs_str);
return BaseException_traverse((PyBaseExceptionObject *)self, visit, arg);
}
@@ -875,13 +897,56 @@ BaseExceptionGroup_str(PyBaseExceptionGroupObject *self)
}
static PyObject *
-BaseExceptionGroup_derive(PyObject *self_, PyObject *args)
+BaseExceptionGroup_repr(PyBaseExceptionGroupObject *self)
{
- PyBaseExceptionGroupObject *self = _PyBaseExceptionGroupObject_cast(self_);
- PyObject *excs = NULL;
- if (!PyArg_ParseTuple(args, "O", &excs)) {
- return NULL;
+ assert(self->msg);
+
+ PyObject *exceptions_str = NULL;
+
+ /* Use the saved exceptions string for custom sequences. */
+ if (self->excs_str) {
+ exceptions_str = Py_NewRef(self->excs_str);
+ }
+ else {
+ assert(self->excs);
+
+ /* Older versions delegated to BaseException, inserting the current
+ * value of self.args[1]; but this can be mutable and go out-of-sync
+ * with self.exceptions. Instead, use self.exceptions for accuracy,
+ * making it look like self.args[1] for backwards compatibility. */
+ if (PyList_Check(PyTuple_GET_ITEM(self->args, 1))) {
+ PyObject *exceptions_list = PySequence_List(self->excs);
+ if (!exceptions_list) {
+ return NULL;
+ }
+
+ exceptions_str = PyObject_Repr(exceptions_list);
+ Py_DECREF(exceptions_list);
+ }
+ else {
+ exceptions_str = PyObject_Repr(self->excs);
+ }
+
+ if (!exceptions_str) {
+ return NULL;
+ }
}
+
+ assert(exceptions_str != NULL);
+
+ const char *name = _PyType_Name(Py_TYPE(self));
+ PyObject *repr = PyUnicode_FromFormat(
+ "%s(%R, %U)", name,
+ self->msg, exceptions_str);
+
+ Py_DECREF(exceptions_str);
+ return repr;
+}
+
+static PyObject *
+BaseExceptionGroup_derive(PyObject *self_, PyObject *excs)
+{
+ PyBaseExceptionGroupObject *self = _PyBaseExceptionGroupObject_cast(self_);
PyObject *init_args = PyTuple_Pack(2, self->msg, excs);
if (!init_args) {
return NULL;
@@ -941,7 +1006,7 @@ exceptiongroup_subset(
PyException_SetCause(eg, PyException_GetCause(orig));
PyObject *notes;
- if (_PyObject_LookupAttr(orig, &_Py_ID(__notes__), &notes) < 0) {
+ if (PyObject_GetOptionalAttr(orig, &_Py_ID(__notes__), &notes) < 0) {
goto error;
}
if (notes) {
@@ -992,7 +1057,7 @@ get_matcher_type(PyObject *value,
{
assert(value);
- if (PyFunction_Check(value)) {
+ if (PyCallable_Check(value) && !PyType_Check(value)) {
*type = EXCEPTION_GROUP_MATCH_BY_PREDICATE;
return 0;
}
@@ -1016,7 +1081,7 @@ get_matcher_type(PyObject *value,
error:
PyErr_SetString(
PyExc_TypeError,
- "expected a function, exception type or tuple of exception types");
+ "expected an exception type, a tuple of exception types, or a callable (other than a class)");
return -1;
}
@@ -1032,7 +1097,7 @@ exceptiongroup_split_check_match(PyObject *exc,
return PyErr_GivenExceptionMatches(exc, matcher_value);
}
case EXCEPTION_GROUP_MATCH_BY_PREDICATE: {
- assert(PyFunction_Check(matcher_value));
+ assert(PyCallable_Check(matcher_value) && !PyType_Check(matcher_value));
PyObject *exc_matches = PyObject_CallOneArg(matcher_value, exc);
if (exc_matches == NULL) {
return -1;
@@ -1175,13 +1240,8 @@ done:
}
static PyObject *
-BaseExceptionGroup_split(PyObject *self, PyObject *args)
+BaseExceptionGroup_split(PyObject *self, PyObject *matcher_value)
{
- PyObject *matcher_value = NULL;
- if (!PyArg_UnpackTuple(args, "split", 1, 1, &matcher_value)) {
- return NULL;
- }
-
_exceptiongroup_split_matcher_type matcher_type;
if (get_matcher_type(matcher_value, &matcher_type) < 0) {
return NULL;
@@ -1206,13 +1266,8 @@ BaseExceptionGroup_split(PyObject *self, PyObject *args)
}
static PyObject *
-BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
+BaseExceptionGroup_subgroup(PyObject *self, PyObject *matcher_value)
{
- PyObject *matcher_value = NULL;
- if (!PyArg_UnpackTuple(args, "subgroup", 1, 1, &matcher_value)) {
- return NULL;
- }
-
_exceptiongroup_split_matcher_type matcher_type;
if (get_matcher_type(matcher_value, &matcher_type) < 0) {
return NULL;
@@ -1457,13 +1512,13 @@ PyUnstable_Exc_PrepReraiseStar(PyObject *orig, PyObject *excs)
PyObject *exc = PyList_GET_ITEM(excs, i);
if (exc == NULL || !(PyExceptionInstance_Check(exc) || Py_IsNone(exc))) {
PyErr_Format(PyExc_TypeError,
- "item %d of excs is not an exception", i);
+ "item %zd of excs is not an exception", i);
return NULL;
}
}
/* Make sure that orig has something as traceback, in the interpreter
- * it always does becuase it's a raised exception.
+ * it always does because it's a raised exception.
*/
PyObject *tb = PyException_GetTraceback(orig);
@@ -1477,9 +1532,9 @@ PyUnstable_Exc_PrepReraiseStar(PyObject *orig, PyObject *excs)
}
static PyMemberDef BaseExceptionGroup_members[] = {
- {"message", T_OBJECT, offsetof(PyBaseExceptionGroupObject, msg), READONLY,
+ {"message", _Py_T_OBJECT, offsetof(PyBaseExceptionGroupObject, msg), Py_READONLY,
PyDoc_STR("exception message")},
- {"exceptions", T_OBJECT, offsetof(PyBaseExceptionGroupObject, excs), READONLY,
+ {"exceptions", _Py_T_OBJECT, offsetof(PyBaseExceptionGroupObject, excs), Py_READONLY,
PyDoc_STR("nested exceptions")},
{NULL} /* Sentinel */
};
@@ -1487,16 +1542,16 @@ static PyMemberDef BaseExceptionGroup_members[] = {
static PyMethodDef BaseExceptionGroup_methods[] = {
{"__class_getitem__", (PyCFunction)Py_GenericAlias,
METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
- {"derive", (PyCFunction)BaseExceptionGroup_derive, METH_VARARGS},
- {"split", (PyCFunction)BaseExceptionGroup_split, METH_VARARGS},
- {"subgroup", (PyCFunction)BaseExceptionGroup_subgroup, METH_VARARGS},
+ {"derive", (PyCFunction)BaseExceptionGroup_derive, METH_O},
+ {"split", (PyCFunction)BaseExceptionGroup_split, METH_O},
+ {"subgroup", (PyCFunction)BaseExceptionGroup_subgroup, METH_O},
{NULL}
};
ComplexExtendsException(PyExc_BaseException, BaseExceptionGroup,
BaseExceptionGroup, BaseExceptionGroup_new /* new */,
BaseExceptionGroup_methods, BaseExceptionGroup_members,
- 0 /* getset */, BaseExceptionGroup_str,
+ 0 /* getset */, BaseExceptionGroup_str, BaseExceptionGroup_repr,
"A combination of multiple unrelated exceptions.");
/*
@@ -1654,13 +1709,13 @@ ImportError_reduce(PyImportErrorObject *self, PyObject *Py_UNUSED(ignored))
}
static PyMemberDef ImportError_members[] = {
- {"msg", T_OBJECT, offsetof(PyImportErrorObject, msg), 0,
+ {"msg", _Py_T_OBJECT, offsetof(PyImportErrorObject, msg), 0,
PyDoc_STR("exception message")},
- {"name", T_OBJECT, offsetof(PyImportErrorObject, name), 0,
+ {"name", _Py_T_OBJECT, offsetof(PyImportErrorObject, name), 0,
PyDoc_STR("module name")},
- {"path", T_OBJECT, offsetof(PyImportErrorObject, path), 0,
+ {"path", _Py_T_OBJECT, offsetof(PyImportErrorObject, path), 0,
PyDoc_STR("module path")},
- {"name_from", T_OBJECT, offsetof(PyImportErrorObject, name_from), 0,
+ {"name_from", _Py_T_OBJECT, offsetof(PyImportErrorObject, name_from), 0,
PyDoc_STR("name imported from module")},
{NULL} /* Sentinel */
};
@@ -1673,7 +1728,7 @@ static PyMethodDef ImportError_methods[] = {
ComplexExtendsException(PyExc_Exception, ImportError,
ImportError, 0 /* new */,
ImportError_methods, ImportError_members,
- 0 /* getset */, ImportError_str,
+ 0 /* getset */, ImportError_str, 0,
"Import can't find module, or can't find name in "
"module.");
@@ -2103,16 +2158,16 @@ OSError_written_set(PyOSErrorObject *self, PyObject *arg, void *context)
}
static PyMemberDef OSError_members[] = {
- {"errno", T_OBJECT, offsetof(PyOSErrorObject, myerrno), 0,
+ {"errno", _Py_T_OBJECT, offsetof(PyOSErrorObject, myerrno), 0,
PyDoc_STR("POSIX exception code")},
- {"strerror", T_OBJECT, offsetof(PyOSErrorObject, strerror), 0,
+ {"strerror", _Py_T_OBJECT, offsetof(PyOSErrorObject, strerror), 0,
PyDoc_STR("exception strerror")},
- {"filename", T_OBJECT, offsetof(PyOSErrorObject, filename), 0,
+ {"filename", _Py_T_OBJECT, offsetof(PyOSErrorObject, filename), 0,
PyDoc_STR("exception filename")},
- {"filename2", T_OBJECT, offsetof(PyOSErrorObject, filename2), 0,
+ {"filename2", _Py_T_OBJECT, offsetof(PyOSErrorObject, filename2), 0,
PyDoc_STR("second exception filename")},
#ifdef MS_WINDOWS
- {"winerror", T_OBJECT, offsetof(PyOSErrorObject, winerror), 0,
+ {"winerror", _Py_T_OBJECT, offsetof(PyOSErrorObject, winerror), 0,
PyDoc_STR("Win32 exception code")},
#endif
{NULL} /* Sentinel */
@@ -2133,7 +2188,7 @@ static PyGetSetDef OSError_getset[] = {
ComplexExtendsException(PyExc_Exception, OSError,
OSError, OSError_new,
OSError_methods, OSError_members, OSError_getset,
- OSError_str,
+ OSError_str, 0,
"Base class for I/O related errors.");
@@ -2190,6 +2245,10 @@ SimpleExtendsException(PyExc_Exception, RuntimeError,
SimpleExtendsException(PyExc_RuntimeError, RecursionError,
"Recursion limit exceeded.");
+// PythonFinalizationError extends RuntimeError
+SimpleExtendsException(PyExc_RuntimeError, PythonFinalizationError,
+ "Operation blocked during Python finalization.");
+
/*
* NotImplementedError extends RuntimeError
*/
@@ -2249,7 +2308,7 @@ NameError_traverse(PyNameErrorObject *self, visitproc visit, void *arg)
}
static PyMemberDef NameError_members[] = {
- {"name", T_OBJECT, offsetof(PyNameErrorObject, name), 0, PyDoc_STR("name")},
+ {"name", _Py_T_OBJECT, offsetof(PyNameErrorObject, name), 0, PyDoc_STR("name")},
{NULL} /* Sentinel */
};
@@ -2260,7 +2319,7 @@ static PyMethodDef NameError_methods[] = {
ComplexExtendsException(PyExc_Exception, NameError,
NameError, 0,
NameError_methods, NameError_members,
- 0, BaseException_str, "Name not found globally.");
+ 0, BaseException_str, 0, "Name not found globally.");
/*
* UnboundLocalError extends NameError
@@ -2368,8 +2427,8 @@ AttributeError_reduce(PyAttributeErrorObject *self, PyObject *Py_UNUSED(ignored)
}
static PyMemberDef AttributeError_members[] = {
- {"name", T_OBJECT, offsetof(PyAttributeErrorObject, name), 0, PyDoc_STR("attribute name")},
- {"obj", T_OBJECT, offsetof(PyAttributeErrorObject, obj), 0, PyDoc_STR("object")},
+ {"name", _Py_T_OBJECT, offsetof(PyAttributeErrorObject, name), 0, PyDoc_STR("attribute name")},
+ {"obj", _Py_T_OBJECT, offsetof(PyAttributeErrorObject, obj), 0, PyDoc_STR("object")},
{NULL} /* Sentinel */
};
@@ -2382,7 +2441,7 @@ static PyMethodDef AttributeError_methods[] = {
ComplexExtendsException(PyExc_Exception, AttributeError,
AttributeError, 0,
AttributeError_methods, AttributeError_members,
- 0, BaseException_str, "Attribute not found.");
+ 0, BaseException_str, 0, "Attribute not found.");
/*
* SyntaxError extends Exception
@@ -2407,22 +2466,23 @@ SyntaxError_init(PySyntaxErrorObject *self, PyObject *args, PyObject *kwds)
return -1;
}
- self->end_lineno = NULL;
- self->end_offset = NULL;
+ PyObject *filename, *lineno, *offset, *text;
+ PyObject *end_lineno = NULL;
+ PyObject *end_offset = NULL;
if (!PyArg_ParseTuple(info, "OOOO|OO",
- &self->filename, &self->lineno,
- &self->offset, &self->text,
- &self->end_lineno, &self->end_offset)) {
+ &filename, &lineno,
+ &offset, &text,
+ &end_lineno, &end_offset)) {
Py_DECREF(info);
return -1;
}
- Py_INCREF(self->filename);
- Py_INCREF(self->lineno);
- Py_INCREF(self->offset);
- Py_INCREF(self->text);
- Py_XINCREF(self->end_lineno);
- Py_XINCREF(self->end_offset);
+ Py_XSETREF(self->filename, Py_NewRef(filename));
+ Py_XSETREF(self->lineno, Py_NewRef(lineno));
+ Py_XSETREF(self->offset, Py_NewRef(offset));
+ Py_XSETREF(self->text, Py_NewRef(text));
+ Py_XSETREF(self->end_lineno, Py_XNewRef(end_lineno));
+ Py_XSETREF(self->end_offset, Py_XNewRef(end_offset));
Py_DECREF(info);
if (self->end_lineno != NULL && self->end_offset == NULL) {
@@ -2479,8 +2539,6 @@ my_basename(PyObject *name)
int kind;
const void *data;
- if (PyUnicode_READY(name))
- return NULL;
kind = PyUnicode_KIND(name);
data = PyUnicode_DATA(name);
size = PyUnicode_GET_LENGTH(name);
@@ -2543,21 +2601,21 @@ SyntaxError_str(PySyntaxErrorObject *self)
}
static PyMemberDef SyntaxError_members[] = {
- {"msg", T_OBJECT, offsetof(PySyntaxErrorObject, msg), 0,
+ {"msg", _Py_T_OBJECT, offsetof(PySyntaxErrorObject, msg), 0,
PyDoc_STR("exception msg")},
- {"filename", T_OBJECT, offsetof(PySyntaxErrorObject, filename), 0,
+ {"filename", _Py_T_OBJECT, offsetof(PySyntaxErrorObject, filename), 0,
PyDoc_STR("exception filename")},
- {"lineno", T_OBJECT, offsetof(PySyntaxErrorObject, lineno), 0,
+ {"lineno", _Py_T_OBJECT, offsetof(PySyntaxErrorObject, lineno), 0,
PyDoc_STR("exception lineno")},
- {"offset", T_OBJECT, offsetof(PySyntaxErrorObject, offset), 0,
+ {"offset", _Py_T_OBJECT, offsetof(PySyntaxErrorObject, offset), 0,
PyDoc_STR("exception offset")},
- {"text", T_OBJECT, offsetof(PySyntaxErrorObject, text), 0,
+ {"text", _Py_T_OBJECT, offsetof(PySyntaxErrorObject, text), 0,
PyDoc_STR("exception text")},
- {"end_lineno", T_OBJECT, offsetof(PySyntaxErrorObject, end_lineno), 0,
+ {"end_lineno", _Py_T_OBJECT, offsetof(PySyntaxErrorObject, end_lineno), 0,
PyDoc_STR("exception end lineno")},
- {"end_offset", T_OBJECT, offsetof(PySyntaxErrorObject, end_offset), 0,
+ {"end_offset", _Py_T_OBJECT, offsetof(PySyntaxErrorObject, end_offset), 0,
PyDoc_STR("exception end offset")},
- {"print_file_and_line", T_OBJECT,
+ {"print_file_and_line", _Py_T_OBJECT,
offsetof(PySyntaxErrorObject, print_file_and_line), 0,
PyDoc_STR("exception print_file_and_line")},
{NULL} /* Sentinel */
@@ -2565,7 +2623,7 @@ static PyMemberDef SyntaxError_members[] = {
ComplexExtendsException(PyExc_Exception, SyntaxError, SyntaxError,
0, 0, SyntaxError_members, 0,
- SyntaxError_str, "Invalid syntax.");
+ SyntaxError_str, 0, "Invalid syntax.");
/*
@@ -2581,6 +2639,11 @@ MiddlingExtendsException(PyExc_SyntaxError, IndentationError, SyntaxError,
MiddlingExtendsException(PyExc_IndentationError, TabError, SyntaxError,
"Improper mixture of spaces and tabs.");
+/*
+ * IncompleteInputError extends SyntaxError
+ */
+MiddlingExtendsExceptionEx(PyExc_SyntaxError, IncompleteInputError, _IncompleteInputError,
+ SyntaxError, "incomplete input.");
/*
* LookupError extends Exception
@@ -2618,7 +2681,7 @@ KeyError_str(PyBaseExceptionObject *self)
}
ComplexExtendsException(PyExc_LookupError, KeyError, BaseException,
- 0, 0, 0, 0, KeyError_str, "Mapping key not found.");
+ 0, 0, 0, 0, KeyError_str, 0, "Mapping key not found.");
/*
@@ -2912,15 +2975,15 @@ UnicodeError_traverse(PyUnicodeErrorObject *self, visitproc visit, void *arg)
}
static PyMemberDef UnicodeError_members[] = {
- {"encoding", T_OBJECT, offsetof(PyUnicodeErrorObject, encoding), 0,
+ {"encoding", _Py_T_OBJECT, offsetof(PyUnicodeErrorObject, encoding), 0,
PyDoc_STR("exception encoding")},
- {"object", T_OBJECT, offsetof(PyUnicodeErrorObject, object), 0,
+ {"object", _Py_T_OBJECT, offsetof(PyUnicodeErrorObject, object), 0,
PyDoc_STR("exception object")},
- {"start", T_PYSSIZET, offsetof(PyUnicodeErrorObject, start), 0,
+ {"start", Py_T_PYSSIZET, offsetof(PyUnicodeErrorObject, start), 0,
PyDoc_STR("exception start")},
- {"end", T_PYSSIZET, offsetof(PyUnicodeErrorObject, end), 0,
+ {"end", Py_T_PYSSIZET, offsetof(PyUnicodeErrorObject, end), 0,
PyDoc_STR("exception end")},
- {"reason", T_OBJECT, offsetof(PyUnicodeErrorObject, reason), 0,
+ {"reason", _Py_T_OBJECT, offsetof(PyUnicodeErrorObject, reason), 0,
PyDoc_STR("exception reason")},
{NULL} /* Sentinel */
};
@@ -3315,36 +3378,43 @@ SimpleExtendsException(PyExc_Exception, ReferenceError,
#define MEMERRORS_SAVE 16
+#ifdef Py_GIL_DISABLED
+# define MEMERRORS_LOCK(state) PyMutex_LockFlags(&state->memerrors_lock, _Py_LOCK_DONT_DETACH)
+# define MEMERRORS_UNLOCK(state) PyMutex_Unlock(&state->memerrors_lock)
+#else
+# define MEMERRORS_LOCK(state) ((void)0)
+# define MEMERRORS_UNLOCK(state) ((void)0)
+#endif
+
static PyObject *
get_memory_error(int allow_allocation, PyObject *args, PyObject *kwds)
{
- PyBaseExceptionObject *self;
+ PyBaseExceptionObject *self = NULL;
struct _Py_exc_state *state = get_exc_state();
- if (state->memerrors_freelist == NULL) {
- if (!allow_allocation) {
- PyInterpreterState *interp = _PyInterpreterState_GET();
- return Py_NewRef(
- &_Py_INTERP_SINGLETON(interp, last_resort_memory_error));
- }
- PyObject *result = BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds);
- return result;
- }
- /* Fetch object from freelist and revive it */
- self = state->memerrors_freelist;
- self->args = PyTuple_New(0);
- /* This shouldn't happen since the empty tuple is persistent */
+ MEMERRORS_LOCK(state);
+ if (state->memerrors_freelist != NULL) {
+ /* Fetch MemoryError from freelist and initialize it */
+ self = state->memerrors_freelist;
+ state->memerrors_freelist = (PyBaseExceptionObject *) self->dict;
+ state->memerrors_numfree--;
+ self->dict = NULL;
+ self->args = (PyObject *)&_Py_SINGLETON(tuple_empty);
+ _Py_NewReference((PyObject *)self);
+ _PyObject_GC_TRACK(self);
+ }
+ MEMERRORS_UNLOCK(state);
- if (self->args == NULL) {
- return NULL;
+ if (self != NULL) {
+ return (PyObject *)self;
}
- state->memerrors_freelist = (PyBaseExceptionObject *) self->dict;
- state->memerrors_numfree--;
- self->dict = NULL;
- _Py_NewReference((PyObject *)self);
- _PyObject_GC_TRACK(self);
- return (PyObject *)self;
+ if (!allow_allocation) {
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ return Py_NewRef(
+ &_Py_INTERP_SINGLETON(interp, last_resort_memory_error));
+ }
+ return BaseException_new((PyTypeObject *)PyExc_MemoryError, args, kwds);
}
static PyObject *
@@ -3389,14 +3459,17 @@ MemoryError_dealloc(PyBaseExceptionObject *self)
}
struct _Py_exc_state *state = get_exc_state();
- if (state->memerrors_numfree >= MEMERRORS_SAVE) {
- Py_TYPE(self)->tp_free((PyObject *)self);
- }
- else {
+ MEMERRORS_LOCK(state);
+ if (state->memerrors_numfree < MEMERRORS_SAVE) {
self->dict = (PyObject *) state->memerrors_freelist;
state->memerrors_freelist = self;
state->memerrors_numfree++;
+ MEMERRORS_UNLOCK(state);
+ return;
}
+ MEMERRORS_UNLOCK(state);
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
}
static int
@@ -3570,7 +3643,6 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning,
#undef EOPNOTSUPP
#undef EPROTONOSUPPORT
#undef EPROTOTYPE
-#undef ETIMEDOUT
#undef EWOULDBLOCK
#if defined(WSAEALREADY) && !defined(EALREADY)
@@ -3591,9 +3663,6 @@ SimpleExtendsException(PyExc_Warning, ResourceWarning,
#if defined(WSAESHUTDOWN) && !defined(ESHUTDOWN)
#define ESHUTDOWN WSAESHUTDOWN
#endif
-#if defined(WSAETIMEDOUT) && !defined(ETIMEDOUT)
-#define ETIMEDOUT WSAETIMEDOUT
-#endif
#if defined(WSAEWOULDBLOCK) && !defined(EWOULDBLOCK)
#define EWOULDBLOCK WSAEWOULDBLOCK
#endif
@@ -3671,10 +3740,12 @@ static struct static_exception static_exceptions[] = {
// Level 4: Other subclasses
ITEM(IndentationError), // base: SyntaxError(Exception)
+ {&_PyExc_IncompleteInputError, "_IncompleteInputError"}, // base: SyntaxError(Exception)
ITEM(IndexError), // base: LookupError(Exception)
ITEM(KeyError), // base: LookupError(Exception)
ITEM(ModuleNotFoundError), // base: ImportError(Exception)
ITEM(NotImplementedError), // base: RuntimeError(Exception)
+ ITEM(PythonFinalizationError), // base: RuntimeError(Exception)
ITEM(RecursionError), // base: RuntimeError(Exception)
ITEM(UnboundLocalError), // base: NameError(Exception)
ITEM(UnicodeError), // base: ValueError(Exception)
@@ -3714,7 +3785,7 @@ _PyExc_FiniTypes(PyInterpreterState *interp)
{
for (Py_ssize_t i=Py_ARRAY_LENGTH(static_exceptions) - 1; i >= 0; i--) {
PyTypeObject *exc = static_exceptions[i].exc;
- _PyStaticType_Dealloc(interp, exc);
+ _PyStaticType_FiniBuiltin(interp, exc);
}
}
@@ -3777,6 +3848,9 @@ _PyExc_InitState(PyInterpreterState *interp)
#endif
ADD_ERRNO(ProcessLookupError, ESRCH);
ADD_ERRNO(TimeoutError, ETIMEDOUT);
+#ifdef WSAETIMEDOUT
+ ADD_ERRNO(TimeoutError, WSAETIMEDOUT);
+#endif
return _PyStatus_OK();
diff --git a/contrib/tools/python3/Objects/fileobject.c b/contrib/tools/python3/Objects/fileobject.c
index e99e155f2b8..bae49d367b6 100644
--- a/contrib/tools/python3/Objects/fileobject.c
+++ b/contrib/tools/python3/Objects/fileobject.c
@@ -1,19 +1,22 @@
/* File object implementation (what's left of it -- see io.py) */
-#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_runtime.h" // _PyRuntime
+#ifdef HAVE_UNISTD_H
+# include <unistd.h> // isatty()
+#endif
+
#if defined(HAVE_GETC_UNLOCKED) && !defined(_Py_MEMORY_SANITIZER)
-/* clang MemorySanitizer doesn't yet understand getc_unlocked. */
-#define GETC(f) getc_unlocked(f)
-#define FLOCKFILE(f) flockfile(f)
-#define FUNLOCKFILE(f) funlockfile(f)
+ /* clang MemorySanitizer doesn't yet understand getc_unlocked. */
+# define GETC(f) getc_unlocked(f)
+# define FLOCKFILE(f) flockfile(f)
+# define FUNLOCKFILE(f) funlockfile(f)
#else
-#define GETC(f) getc(f)
-#define FLOCKFILE(f)
-#define FUNLOCKFILE(f)
+# define GETC(f) getc(f)
+# define FLOCKFILE(f)
+# define FUNLOCKFILE(f)
#endif
/* Newline flags */
@@ -22,10 +25,6 @@
#define NEWLINE_LF 2 /* \n newline seen */
#define NEWLINE_CRLF 4 /* \r\n newline seen */
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/* External C interface */
PyObject *
@@ -81,13 +80,7 @@ PyFile_GetLine(PyObject *f, int n)
"EOF when reading a line");
}
else if (s[len-1] == '\n') {
- if (Py_REFCNT(result) == 1)
- _PyBytes_Resize(&result, len-1);
- else {
- PyObject *v;
- v = PyBytes_FromStringAndSize(s, len-1);
- Py_SETREF(result, v);
- }
+ (void) _PyBytes_Resize(&result, len-1);
}
}
if (n < 0 && result != NULL && PyUnicode_Check(result)) {
@@ -175,9 +168,16 @@ PyObject_AsFileDescriptor(PyObject *o)
PyObject *meth;
if (PyLong_Check(o)) {
- fd = _PyLong_AsInt(o);
+ if (PyBool_Check(o)) {
+ if (PyErr_WarnEx(PyExc_RuntimeWarning,
+ "bool is used as a file descriptor", 1))
+ {
+ return -1;
+ }
+ }
+ fd = PyLong_AsInt(o);
}
- else if (_PyObject_LookupAttr(o, &_Py_ID(fileno), &meth) < 0) {
+ else if (PyObject_GetOptionalAttr(o, &_Py_ID(fileno), &meth) < 0) {
return -1;
}
else if (meth != NULL) {
@@ -187,7 +187,7 @@ PyObject_AsFileDescriptor(PyObject *o)
return -1;
if (PyLong_Check(fno)) {
- fd = _PyLong_AsInt(fno);
+ fd = PyLong_AsInt(fno);
Py_DECREF(fno);
}
else {
@@ -530,6 +530,13 @@ PyFile_OpenCode(const char *utf8path)
}
-#ifdef __cplusplus
+int
+_PyFile_Flush(PyObject *file)
+{
+ PyObject *tmp = PyObject_CallMethodNoArgs(file, &_Py_ID(flush));
+ if (tmp == NULL) {
+ return -1;
+ }
+ Py_DECREF(tmp);
+ return 0;
}
-#endif
diff --git a/contrib/tools/python3/Objects/floatobject.c b/contrib/tools/python3/Objects/floatobject.c
index 92d40e8acad..986edd597d6 100644
--- a/contrib/tools/python3/Objects/floatobject.c
+++ b/contrib/tools/python3/Objects/floatobject.c
@@ -4,18 +4,19 @@
for any kind of float exception without losing portability. */
#include "Python.h"
+#include "pycore_abstract.h" // _PyNumber_Index()
#include "pycore_dtoa.h" // _Py_dg_dtoa()
#include "pycore_floatobject.h" // _PyFloat_FormatAdvancedWriter()
#include "pycore_initconfig.h" // _PyStatus_OK()
-#include "pycore_interp.h" // _PyInterpreterState.float_state
+#include "pycore_interp.h" // _Py_float_freelist
#include "pycore_long.h" // _PyLong_GetOne()
-#include "pycore_object.h" // _PyObject_Init()
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
+#include "pycore_object.h" // _PyObject_Init(), _PyDebugAllocatorStats()
#include "pycore_pymath.h" // _PY_SHORT_FLOAT_REPR
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_structseq.h" // _PyStructSequence_FiniBuiltin()
-#include <ctype.h>
-#include <float.h>
+#include <float.h> // DBL_MAX
#include <stdlib.h> // strtol()
/*[clinic input]
@@ -25,17 +26,13 @@ class float "PyObject *" "&PyFloat_Type"
#include "clinic/floatobject.c.h"
-#ifndef PyFloat_MAXFREELIST
-# define PyFloat_MAXFREELIST 100
-#endif
-
-
-#if PyFloat_MAXFREELIST > 0
-static struct _Py_float_state *
-get_float_state(void)
+#ifdef WITH_FREELISTS
+static struct _Py_float_freelist *
+get_float_freelist(void)
{
- PyInterpreterState *interp = _PyInterpreterState_GET();
- return &interp->float_state;
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
+ assert(freelists != NULL);
+ return &freelists->floats;
}
#endif
@@ -136,16 +133,12 @@ PyObject *
PyFloat_FromDouble(double fval)
{
PyFloatObject *op;
-#if PyFloat_MAXFREELIST > 0
- struct _Py_float_state *state = get_float_state();
- op = state->free_list;
+#ifdef WITH_FREELISTS
+ struct _Py_float_freelist *float_freelist = get_float_freelist();
+ op = float_freelist->items;
if (op != NULL) {
-#ifdef Py_DEBUG
- // PyFloat_FromDouble() must not be called after _PyFloat_Fini()
- assert(state->numfree != -1);
-#endif
- state->free_list = (PyFloatObject *) Py_TYPE(op);
- state->numfree--;
+ float_freelist->items = (PyFloatObject *) Py_TYPE(op);
+ float_freelist->numfree--;
OBJECT_STAT_INC(from_freelist);
}
else
@@ -256,19 +249,15 @@ _PyFloat_ExactDealloc(PyObject *obj)
{
assert(PyFloat_CheckExact(obj));
PyFloatObject *op = (PyFloatObject *)obj;
-#if PyFloat_MAXFREELIST > 0
- struct _Py_float_state *state = get_float_state();
-#ifdef Py_DEBUG
- // float_dealloc() must not be called after _PyFloat_Fini()
- assert(state->numfree != -1);
-#endif
- if (state->numfree >= PyFloat_MAXFREELIST) {
+#ifdef WITH_FREELISTS
+ struct _Py_float_freelist *float_freelist = get_float_freelist();
+ if (float_freelist->numfree >= PyFloat_MAXFREELIST || float_freelist->numfree < 0) {
PyObject_Free(op);
return;
}
- state->numfree++;
- Py_SET_TYPE(op, (PyTypeObject *)state->free_list);
- state->free_list = op;
+ float_freelist->numfree++;
+ Py_SET_TYPE(op, (PyTypeObject *)float_freelist->items);
+ float_freelist->items = op;
OBJECT_STAT_INC(to_freelist);
#else
PyObject_Free(op);
@@ -279,7 +268,7 @@ static void
float_dealloc(PyObject *op)
{
assert(PyFloat_Check(op));
-#if PyFloat_MAXFREELIST > 0
+#ifdef WITH_FREELISTS
if (PyFloat_CheckExact(op)) {
_PyFloat_ExactDealloc(op);
}
@@ -480,82 +469,67 @@ float_richcompare(PyObject *v, PyObject *w, int op)
assert(vsign != 0); /* if vsign were 0, then since wsign is
* not 0, we would have taken the
* vsign != wsign branch at the start */
- /* We want to work with non-negative numbers. */
- if (vsign < 0) {
- /* "Multiply both sides" by -1; this also swaps the
- * comparator.
- */
- i = -i;
- op = _Py_SwappedOp[op];
- }
- assert(i > 0.0);
(void) frexp(i, &exponent);
/* exponent is the # of bits in v before the radix point;
* we know that nbits (the # of bits in w) > 48 at this point
*/
if (exponent < 0 || (size_t)exponent < nbits) {
- i = 1.0;
- j = 2.0;
+ j = i;
+ i = 0.0;
goto Compare;
}
if ((size_t)exponent > nbits) {
- i = 2.0;
- j = 1.0;
+ j = 0.0;
goto Compare;
}
/* v and w have the same number of bits before the radix
- * point. Construct two ints that have the same comparison
- * outcome.
+ * point. Construct an int from the integer part of v and
+ * update op if necessary, so comparing two ints has the same outcome.
*/
{
double fracpart;
double intpart;
PyObject *result = NULL;
PyObject *vv = NULL;
- PyObject *ww = w;
- if (wsign < 0) {
- ww = PyNumber_Negative(w);
- if (ww == NULL)
- goto Error;
+ fracpart = modf(i, &intpart);
+ if (fracpart != 0.0) {
+ switch (op) {
+ /* Non-integer float never equals to an int. */
+ case Py_EQ:
+ Py_RETURN_FALSE;
+ case Py_NE:
+ Py_RETURN_TRUE;
+ /* For non-integer float, v <= w <=> v < w.
+ * If v > 0: trunc(v) < v < trunc(v) + 1
+ * v < w => trunc(v) < w
+ * trunc(v) < w => trunc(v) + 1 <= w => v < w
+ * If v < 0: trunc(v) - 1 < v < trunc(v)
+ * v < w => trunc(v) - 1 < w => trunc(v) <= w
+ * trunc(v) <= w => v < w
+ */
+ case Py_LT:
+ case Py_LE:
+ op = vsign > 0 ? Py_LT : Py_LE;
+ break;
+ /* The same as above, but with opposite directions. */
+ case Py_GT:
+ case Py_GE:
+ op = vsign > 0 ? Py_GE : Py_GT;
+ break;
+ }
}
- else
- Py_INCREF(ww);
- fracpart = modf(i, &intpart);
vv = PyLong_FromDouble(intpart);
if (vv == NULL)
goto Error;
- if (fracpart != 0.0) {
- /* Shift left, and or a 1 bit into vv
- * to represent the lost fraction.
- */
- PyObject *temp;
-
- temp = _PyLong_Lshift(ww, 1);
- if (temp == NULL)
- goto Error;
- Py_SETREF(ww, temp);
-
- temp = _PyLong_Lshift(vv, 1);
- if (temp == NULL)
- goto Error;
- Py_SETREF(vv, temp);
-
- temp = PyNumber_Or(vv, _PyLong_GetOne());
- if (temp == NULL)
- goto Error;
- Py_SETREF(vv, temp);
- }
-
- r = PyObject_RichCompareBool(vv, ww, op);
+ r = PyObject_RichCompareBool(vv, w, op);
if (r < 0)
goto Error;
result = PyBool_FromLong(r);
Error:
Py_XDECREF(vv);
- Py_XDECREF(ww);
return result;
}
} /* else if (PyLong_Check(w)) */
@@ -650,7 +624,7 @@ float_rem(PyObject *v, PyObject *w)
CONVERT_TO_DOUBLE(w, wx);
if (wx == 0.0) {
PyErr_SetString(PyExc_ZeroDivisionError,
- "float modulo");
+ "float modulo by zero");
return NULL;
}
mod = fmod(vx, wx);
@@ -2006,28 +1980,23 @@ _PyFloat_InitTypes(PyInterpreterState *interp)
}
void
-_PyFloat_ClearFreeList(PyInterpreterState *interp)
+_PyFloat_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization)
{
-#if PyFloat_MAXFREELIST > 0
- struct _Py_float_state *state = &interp->float_state;
- PyFloatObject *f = state->free_list;
+#ifdef WITH_FREELISTS
+ struct _Py_float_freelist *state = &freelists->floats;
+ PyFloatObject *f = state->items;
while (f != NULL) {
PyFloatObject *next = (PyFloatObject*) Py_TYPE(f);
PyObject_Free(f);
f = next;
}
- state->free_list = NULL;
- state->numfree = 0;
-#endif
-}
-
-void
-_PyFloat_Fini(PyInterpreterState *interp)
-{
- _PyFloat_ClearFreeList(interp);
-#if defined(Py_DEBUG) && PyFloat_MAXFREELIST > 0
- struct _Py_float_state *state = &interp->float_state;
- state->numfree = -1;
+ state->items = NULL;
+ if (is_finalization) {
+ state->numfree = -1;
+ }
+ else {
+ state->numfree = 0;
+ }
#endif
}
@@ -2041,11 +2010,11 @@ _PyFloat_FiniType(PyInterpreterState *interp)
void
_PyFloat_DebugMallocStats(FILE *out)
{
-#if PyFloat_MAXFREELIST > 0
- struct _Py_float_state *state = get_float_state();
+#ifdef WITH_FREELISTS
+ struct _Py_float_freelist *float_freelist = get_float_freelist();
_PyDebugAllocatorStats(out,
"free PyFloatObject",
- state->numfree, sizeof(PyFloatObject));
+ float_freelist->numfree, sizeof(PyFloatObject));
#endif
}
diff --git a/contrib/tools/python3/Objects/frameobject.c b/contrib/tools/python3/Objects/frameobject.c
index d33c3cde526..467c3fe56e9 100644
--- a/contrib/tools/python3/Objects/frameobject.c
+++ b/contrib/tools/python3/Objects/frameobject.c
@@ -5,22 +5,862 @@
#include "pycore_code.h" // CO_FAST_LOCAL, etc.
#include "pycore_function.h" // _PyFunction_FromConstructor()
#include "pycore_moduleobject.h" // _PyModule_GetDict()
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
-#include "pycore_opcode.h" // _PyOpcode_Caches
+#include "pycore_opcode_metadata.h" // _PyOpcode_Deopt, _PyOpcode_Caches
+
#include "frameobject.h" // PyFrameObject
#include "pycore_frame.h"
#include "opcode.h" // EXTENDED_ARG
-#include "structmember.h" // PyMemberDef
+
#define OFF(x) offsetof(PyFrameObject, x)
+
+// Returns borrowed reference or NULL
+static PyObject *
+framelocalsproxy_getval(_PyInterpreterFrame *frame, PyCodeObject *co, int i)
+{
+ PyObject **fast = _PyFrame_GetLocalsArray(frame);
+ _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
+
+ PyObject *value = fast[i];
+ PyObject *cell = NULL;
+
+ if (value == NULL) {
+ return NULL;
+ }
+
+ if (kind == CO_FAST_FREE || kind & CO_FAST_CELL) {
+ // The cell was set when the frame was created from
+ // the function's closure.
+ // GH-128396: With PEP 709, it's possible to have a fast variable in
+ // an inlined comprehension that has the same name as the cell variable
+ // in the frame, where the `kind` obtained from frame can not guarantee
+ // that the variable is a cell.
+ // If the variable is not a cell, we are okay with it and we can simply
+ // return the value.
+ if (PyCell_Check(value)) {
+ cell = value;
+ }
+ }
+
+ if (cell != NULL) {
+ value = PyCell_GET(cell);
+ }
+
+ if (value == NULL) {
+ return NULL;
+ }
+
+ return value;
+}
+
+static int
+framelocalsproxy_getkeyindex(PyFrameObject *frame, PyObject* key, bool read)
+{
+ /*
+ * Returns -2 (!) if an error occurred; exception will be set.
+ * Returns the fast locals index of the key on success:
+ * - if read == true, returns the index if the value is not NULL
+ * - if read == false, returns the index if the value is not hidden
+ * Otherwise returns -1.
+ */
+
+ PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
+
+ // Ensure that the key is hashable.
+ Py_hash_t key_hash = PyObject_Hash(key);
+ if (key_hash == -1) {
+ return -2;
+ }
+ bool found = false;
+
+ // We do 2 loops here because it's highly possible the key is interned
+ // and we can do a pointer comparison.
+ for (int i = 0; i < co->co_nlocalsplus; i++) {
+ PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
+ if (name == key) {
+ if (read) {
+ if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) {
+ return i;
+ }
+ } else {
+ if (!(_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_HIDDEN)) {
+ return i;
+ }
+ }
+ found = true;
+ }
+ }
+ if (found) {
+ // This is an attempt to read an unset local variable or
+ // write to a variable that is hidden from regular write operations
+ return -1;
+ }
+ // This is unlikely, but we need to make sure. This means the key
+ // is not interned.
+ for (int i = 0; i < co->co_nlocalsplus; i++) {
+ PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
+ Py_hash_t name_hash = PyObject_Hash(name);
+ assert(name_hash != -1); // keys are exact unicode
+ if (name_hash != key_hash) {
+ continue;
+ }
+ int same = PyObject_RichCompareBool(name, key, Py_EQ);
+ if (same < 0) {
+ return -2;
+ }
+ if (same) {
+ if (read) {
+ if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) {
+ return i;
+ }
+ } else {
+ if (!(_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_HIDDEN)) {
+ return i;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static PyObject *
+framelocalsproxy_getitem(PyObject *self, PyObject *key)
+{
+ PyFrameObject* frame = ((PyFrameLocalsProxyObject*)self)->frame;
+ PyCodeObject* co = _PyFrame_GetCode(frame->f_frame);
+
+ int i = framelocalsproxy_getkeyindex(frame, key, true);
+ if (i == -2) {
+ return NULL;
+ }
+ if (i >= 0) {
+ PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i);
+ assert(value != NULL);
+ return Py_NewRef(value);
+ }
+
+ // Okay not in the fast locals, try extra locals
+
+ PyObject *extra = frame->f_extra_locals;
+ if (extra != NULL) {
+ PyObject *value = PyDict_GetItem(extra, key);
+ if (value != NULL) {
+ return Py_NewRef(value);
+ }
+ }
+
+ PyErr_Format(PyExc_KeyError, "local variable '%R' is not defined", key);
+ return NULL;
+}
+
+static int
+framelocalsproxy_setitem(PyObject *self, PyObject *key, PyObject *value)
+{
+ /* Merge locals into fast locals */
+ PyFrameObject* frame = ((PyFrameLocalsProxyObject*)self)->frame;
+ PyObject** fast = _PyFrame_GetLocalsArray(frame->f_frame);
+ PyCodeObject* co = _PyFrame_GetCode(frame->f_frame);
+
+ int i = framelocalsproxy_getkeyindex(frame, key, false);
+ if (i == -2) {
+ return -1;
+ }
+ if (i >= 0) {
+ if (value == NULL) {
+ PyErr_SetString(PyExc_ValueError, "cannot remove local variables from FrameLocalsProxy");
+ return -1;
+ }
+
+ _Py_Executors_InvalidateDependency(PyInterpreterState_Get(), co, 1);
+
+ _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
+ PyObject *oldvalue = fast[i];
+ PyObject *cell = NULL;
+ if (kind == CO_FAST_FREE) {
+ // The cell was set when the frame was created from
+ // the function's closure.
+ assert(oldvalue != NULL && PyCell_Check(oldvalue));
+ cell = oldvalue;
+ } else if (kind & CO_FAST_CELL && oldvalue != NULL) {
+ if (PyCell_Check(oldvalue)) {
+ cell = oldvalue;
+ }
+ }
+ if (cell != NULL) {
+ oldvalue = PyCell_GET(cell);
+ if (value != oldvalue) {
+ PyCell_SET(cell, Py_XNewRef(value));
+ Py_XDECREF(oldvalue);
+ }
+ } else if (value != oldvalue) {
+ Py_XSETREF(fast[i], Py_NewRef(value));
+ }
+ return 0;
+ }
+
+ // Okay not in the fast locals, try extra locals
+
+ PyObject *extra = frame->f_extra_locals;
+
+ if (extra == NULL) {
+ if (value == NULL) {
+ _PyErr_SetKeyError(key);
+ return -1;
+ }
+ extra = PyDict_New();
+ if (extra == NULL) {
+ return -1;
+ }
+ frame->f_extra_locals = extra;
+ }
+
+ assert(PyDict_Check(extra));
+
+ if (value == NULL) {
+ return PyDict_DelItem(extra, key);
+ } else {
+ return PyDict_SetItem(extra, key, value);
+ }
+}
+
+static int
+framelocalsproxy_merge(PyObject* self, PyObject* other)
+{
+ if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) {
+ return -1;
+ }
+
+ PyObject *keys = PyMapping_Keys(other);
+ if (keys == NULL) {
+ return -1;
+ }
+
+ PyObject *iter = PyObject_GetIter(keys);
+ Py_DECREF(keys);
+ if (iter == NULL) {
+ return -1;
+ }
+
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+
+ while ((key = PyIter_Next(iter)) != NULL) {
+ value = PyObject_GetItem(other, key);
+ if (value == NULL) {
+ Py_DECREF(key);
+ Py_DECREF(iter);
+ return -1;
+ }
+
+ if (framelocalsproxy_setitem(self, key, value) < 0) {
+ Py_DECREF(key);
+ Py_DECREF(value);
+ Py_DECREF(iter);
+ return -1;
+ }
+
+ Py_DECREF(key);
+ Py_DECREF(value);
+ }
+
+ Py_DECREF(iter);
+
+ if (PyErr_Occurred()) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static PyObject *
+framelocalsproxy_keys(PyObject *self, void *Py_UNUSED(ignored))
+{
+ PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
+ PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
+ PyObject *names = PyList_New(0);
+ if (names == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; i < co->co_nlocalsplus; i++) {
+ PyObject *val = framelocalsproxy_getval(frame->f_frame, co, i);
+ if (val) {
+ PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
+ if (PyList_Append(names, name) < 0) {
+ Py_DECREF(names);
+ return NULL;
+ }
+ }
+ }
+
+ // Iterate through the extra locals
+ if (frame->f_extra_locals) {
+ assert(PyDict_Check(frame->f_extra_locals));
+
+ Py_ssize_t i = 0;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+
+ while (PyDict_Next(frame->f_extra_locals, &i, &key, &value)) {
+ if (PyList_Append(names, key) < 0) {
+ Py_DECREF(names);
+ return NULL;
+ }
+ }
+ }
+
+ return names;
+}
+
+static void
+framelocalsproxy_dealloc(PyObject *self)
+{
+ PyObject_GC_UnTrack(self);
+ Py_CLEAR(((PyFrameLocalsProxyObject*)self)->frame);
+ Py_TYPE(self)->tp_free(self);
+}
+
+static PyObject *
+framelocalsproxy_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ if (PyTuple_GET_SIZE(args) != 1) {
+ PyErr_Format(PyExc_TypeError,
+ "FrameLocalsProxy expected 1 argument, got %zd",
+ PyTuple_GET_SIZE(args));
+ return NULL;
+ }
+ PyObject *item = PyTuple_GET_ITEM(args, 0);
+
+ if (!PyFrame_Check(item)) {
+ PyErr_Format(PyExc_TypeError, "expect frame, not %T", item);
+ return NULL;
+ }
+ PyFrameObject *frame = (PyFrameObject*)item;
+
+ if (kwds != NULL && PyDict_Size(kwds) != 0) {
+ PyErr_SetString(PyExc_TypeError,
+ "FrameLocalsProxy takes no keyword arguments");
+ return 0;
+ }
+
+ PyFrameLocalsProxyObject *self = (PyFrameLocalsProxyObject *)type->tp_alloc(type, 0);
+ if (self == NULL) {
+ return NULL;
+ }
+
+ ((PyFrameLocalsProxyObject*)self)->frame = (PyFrameObject*)Py_NewRef(frame);
+
+ return (PyObject *)self;
+}
+
+static int
+framelocalsproxy_tp_clear(PyObject *self)
+{
+ Py_CLEAR(((PyFrameLocalsProxyObject*)self)->frame);
+ return 0;
+}
+
+static int
+framelocalsproxy_visit(PyObject *self, visitproc visit, void *arg)
+{
+ Py_VISIT(((PyFrameLocalsProxyObject*)self)->frame);
+ return 0;
+}
+
+static PyObject *
+framelocalsproxy_iter(PyObject *self)
+{
+ PyObject* keys = framelocalsproxy_keys(self, NULL);
+ if (keys == NULL) {
+ return NULL;
+ }
+
+ PyObject* iter = PyObject_GetIter(keys);
+ Py_XDECREF(keys);
+
+ return iter;
+}
+
+static PyObject *
+framelocalsproxy_richcompare(PyObject *self, PyObject *other, int op)
+{
+ if (PyFrameLocalsProxy_Check(other)) {
+ bool result = ((PyFrameLocalsProxyObject*)self)->frame == ((PyFrameLocalsProxyObject*)other)->frame;
+ if (op == Py_EQ) {
+ return PyBool_FromLong(result);
+ } else if (op == Py_NE) {
+ return PyBool_FromLong(!result);
+ }
+ } else if (PyDict_Check(other)) {
+ PyObject *dct = PyDict_New();
+ if (dct == NULL) {
+ return NULL;
+ }
+
+ if (PyDict_Update(dct, self) < 0) {
+ Py_DECREF(dct);
+ return NULL;
+ }
+
+ PyObject *result = PyObject_RichCompare(dct, other, op);
+ Py_DECREF(dct);
+ return result;
+ }
+
+ Py_RETURN_NOTIMPLEMENTED;
+}
+
+static PyObject *
+framelocalsproxy_repr(PyObject *self)
+{
+ int i = Py_ReprEnter(self);
+ if (i != 0) {
+ return i > 0 ? PyUnicode_FromString("{...}") : NULL;
+ }
+
+ PyObject *dct = PyDict_New();
+ if (dct == NULL) {
+ Py_ReprLeave(self);
+ return NULL;
+ }
+
+ if (PyDict_Update(dct, self) < 0) {
+ Py_DECREF(dct);
+ Py_ReprLeave(self);
+ return NULL;
+ }
+
+ PyObject *repr = PyObject_Repr(dct);
+ Py_DECREF(dct);
+
+ Py_ReprLeave(self);
+
+ return repr;
+}
+
+static PyObject*
+framelocalsproxy_or(PyObject *self, PyObject *other)
+{
+ if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+
+ PyObject *result = PyDict_New();
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (PyDict_Update(result, self) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ if (PyDict_Update(result, other) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject*
+framelocalsproxy_inplace_or(PyObject *self, PyObject *other)
+{
+ if (!PyDict_Check(other) && !PyFrameLocalsProxy_Check(other)) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+
+ if (framelocalsproxy_merge(self, other) < 0) {
+ Py_RETURN_NOTIMPLEMENTED;
+ }
+
+ return Py_NewRef(self);
+}
+
+static PyObject*
+framelocalsproxy_values(PyObject *self, void *Py_UNUSED(ignored))
+{
+ PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
+ PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
+ PyObject *values = PyList_New(0);
+ if (values == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; i < co->co_nlocalsplus; i++) {
+ PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i);
+ if (value) {
+ if (PyList_Append(values, value) < 0) {
+ Py_DECREF(values);
+ return NULL;
+ }
+ }
+ }
+
+ // Iterate through the extra locals
+ if (frame->f_extra_locals) {
+ Py_ssize_t j = 0;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ while (PyDict_Next(frame->f_extra_locals, &j, &key, &value)) {
+ if (PyList_Append(values, value) < 0) {
+ Py_DECREF(values);
+ return NULL;
+ }
+ }
+ }
+
+ return values;
+}
+
+static PyObject *
+framelocalsproxy_items(PyObject *self, void *Py_UNUSED(ignored))
+{
+ PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
+ PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
+ PyObject *items = PyList_New(0);
+ if (items == NULL) {
+ return NULL;
+ }
+
+ for (int i = 0; i < co->co_nlocalsplus; i++) {
+ PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
+ PyObject *value = framelocalsproxy_getval(frame->f_frame, co, i);
+
+ if (value) {
+ PyObject *pair = PyTuple_Pack(2, name, value);
+ if (pair == NULL) {
+ Py_DECREF(items);
+ return NULL;
+ }
+
+ if (PyList_Append(items, pair) < 0) {
+ Py_DECREF(items);
+ Py_DECREF(pair);
+ return NULL;
+ }
+
+ Py_DECREF(pair);
+ }
+ }
+
+ // Iterate through the extra locals
+ if (frame->f_extra_locals) {
+ Py_ssize_t j = 0;
+ PyObject *key = NULL;
+ PyObject *value = NULL;
+ while (PyDict_Next(frame->f_extra_locals, &j, &key, &value)) {
+ PyObject *pair = PyTuple_Pack(2, key, value);
+ if (pair == NULL) {
+ Py_DECREF(items);
+ return NULL;
+ }
+
+ if (PyList_Append(items, pair) < 0) {
+ Py_DECREF(items);
+ Py_DECREF(pair);
+ return NULL;
+ }
+
+ Py_DECREF(pair);
+ }
+ }
+
+ return items;
+}
+
+static Py_ssize_t
+framelocalsproxy_length(PyObject *self)
+{
+ PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
+ PyCodeObject *co = _PyFrame_GetCode(frame->f_frame);
+ Py_ssize_t size = 0;
+
+ if (frame->f_extra_locals != NULL) {
+ assert(PyDict_Check(frame->f_extra_locals));
+ size += PyDict_Size(frame->f_extra_locals);
+ }
+
+ for (int i = 0; i < co->co_nlocalsplus; i++) {
+ if (framelocalsproxy_getval(frame->f_frame, co, i) != NULL) {
+ size++;
+ }
+ }
+ return size;
+}
+
+static int
+framelocalsproxy_contains(PyObject *self, PyObject *key)
+{
+ PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
+
+ int i = framelocalsproxy_getkeyindex(frame, key, true);
+ if (i == -2) {
+ return -1;
+ }
+ if (i >= 0) {
+ return 1;
+ }
+
+ PyObject *extra = ((PyFrameObject*)frame)->f_extra_locals;
+ if (extra != NULL) {
+ return PyDict_Contains(extra, key);
+ }
+
+ return 0;
+}
+
+static PyObject* framelocalsproxy___contains__(PyObject *self, PyObject *key)
+{
+ int result = framelocalsproxy_contains(self, key);
+ if (result < 0) {
+ return NULL;
+ }
+ return PyBool_FromLong(result);
+}
+
+static PyObject*
+framelocalsproxy_update(PyObject *self, PyObject *other)
+{
+ if (framelocalsproxy_merge(self, other) < 0) {
+ PyErr_SetString(PyExc_TypeError, "update() argument must be dict or another FrameLocalsProxy");
+ return NULL;
+ }
+
+ Py_RETURN_NONE;
+}
+
+static PyObject*
+framelocalsproxy_get(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
+{
+ if (nargs < 1 || nargs > 2) {
+ PyErr_SetString(PyExc_TypeError, "get expected 1 or 2 arguments");
+ return NULL;
+ }
+
+ PyObject *key = args[0];
+ PyObject *default_value = Py_None;
+
+ if (nargs == 2) {
+ default_value = args[1];
+ }
+
+ PyObject *result = framelocalsproxy_getitem(self, key);
+
+ if (result == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+ PyErr_Clear();
+ return Py_XNewRef(default_value);
+ }
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject*
+framelocalsproxy_setdefault(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
+{
+ if (nargs < 1 || nargs > 2) {
+ PyErr_SetString(PyExc_TypeError, "setdefault expected 1 or 2 arguments");
+ return NULL;
+ }
+
+ PyObject *key = args[0];
+ PyObject *default_value = Py_None;
+
+ if (nargs == 2) {
+ default_value = args[1];
+ }
+
+ PyObject *result = framelocalsproxy_getitem(self, key);
+
+ if (result == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_KeyError)) {
+ PyErr_Clear();
+ if (framelocalsproxy_setitem(self, key, default_value) < 0) {
+ return NULL;
+ }
+ return Py_XNewRef(default_value);
+ }
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject*
+framelocalsproxy_pop(PyObject* self, PyObject *const *args, Py_ssize_t nargs)
+{
+ if (!_PyArg_CheckPositional("pop", nargs, 1, 2)) {
+ return NULL;
+ }
+
+ PyObject *key = args[0];
+ PyObject *default_value = NULL;
+
+ if (nargs == 2) {
+ default_value = args[1];
+ }
+
+ PyFrameObject *frame = ((PyFrameLocalsProxyObject*)self)->frame;
+
+ int i = framelocalsproxy_getkeyindex(frame, key, false);
+ if (i == -2) {
+ return NULL;
+ }
+
+ if (i >= 0) {
+ PyErr_SetString(PyExc_ValueError, "cannot remove local variables from FrameLocalsProxy");
+ return NULL;
+ }
+
+ PyObject *result = NULL;
+
+ if (frame->f_extra_locals == NULL) {
+ if (default_value != NULL) {
+ return Py_XNewRef(default_value);
+ } else {
+ _PyErr_SetKeyError(key);
+ return NULL;
+ }
+ }
+
+ if (PyDict_Pop(frame->f_extra_locals, key, &result) < 0) {
+ return NULL;
+ }
+
+ if (result == NULL) {
+ if (default_value != NULL) {
+ return Py_XNewRef(default_value);
+ } else {
+ _PyErr_SetKeyError(key);
+ return NULL;
+ }
+ }
+
+ return result;
+}
+
+static PyObject*
+framelocalsproxy_copy(PyObject *self, PyObject *Py_UNUSED(ignored))
+{
+ PyObject* result = PyDict_New();
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (PyDict_Update(result, self) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static PyObject*
+framelocalsproxy_reversed(PyObject *self, void *Py_UNUSED(ignored))
+{
+ PyObject *result = framelocalsproxy_keys(self, NULL);
+
+ if (result == NULL) {
+ return NULL;
+ }
+
+ if (PyList_Reverse(result) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ return result;
+}
+
+static PyNumberMethods framelocalsproxy_as_number = {
+ .nb_or = framelocalsproxy_or,
+ .nb_inplace_or = framelocalsproxy_inplace_or,
+};
+
+static PySequenceMethods framelocalsproxy_as_sequence = {
+ .sq_contains = framelocalsproxy_contains,
+};
+
+static PyMappingMethods framelocalsproxy_as_mapping = {
+ framelocalsproxy_length, // mp_length
+ framelocalsproxy_getitem, // mp_subscript
+ framelocalsproxy_setitem, // mp_ass_subscript
+};
+
+static PyMethodDef framelocalsproxy_methods[] = {
+ {"__contains__", framelocalsproxy___contains__, METH_O | METH_COEXIST,
+ NULL},
+ {"__getitem__", framelocalsproxy_getitem, METH_O | METH_COEXIST,
+ NULL},
+ {"update", framelocalsproxy_update, METH_O,
+ NULL},
+ {"__reversed__", _PyCFunction_CAST(framelocalsproxy_reversed), METH_NOARGS,
+ NULL},
+ {"copy", _PyCFunction_CAST(framelocalsproxy_copy), METH_NOARGS,
+ NULL},
+ {"keys", _PyCFunction_CAST(framelocalsproxy_keys), METH_NOARGS,
+ NULL},
+ {"values", _PyCFunction_CAST(framelocalsproxy_values), METH_NOARGS,
+ NULL},
+ {"items", _PyCFunction_CAST(framelocalsproxy_items), METH_NOARGS,
+ NULL},
+ {"get", _PyCFunction_CAST(framelocalsproxy_get), METH_FASTCALL,
+ NULL},
+ {"pop", _PyCFunction_CAST(framelocalsproxy_pop), METH_FASTCALL,
+ NULL},
+ {"setdefault", _PyCFunction_CAST(framelocalsproxy_setdefault), METH_FASTCALL,
+ NULL},
+ {NULL, NULL} /* sentinel */
+};
+
+PyTypeObject PyFrameLocalsProxy_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ .tp_name = "FrameLocalsProxy",
+ .tp_basicsize = sizeof(PyFrameLocalsProxyObject),
+ .tp_dealloc = (destructor)framelocalsproxy_dealloc,
+ .tp_repr = &framelocalsproxy_repr,
+ .tp_as_number = &framelocalsproxy_as_number,
+ .tp_as_sequence = &framelocalsproxy_as_sequence,
+ .tp_as_mapping = &framelocalsproxy_as_mapping,
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_setattro = PyObject_GenericSetAttr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MAPPING,
+ .tp_traverse = framelocalsproxy_visit,
+ .tp_clear = framelocalsproxy_tp_clear,
+ .tp_richcompare = framelocalsproxy_richcompare,
+ .tp_iter = framelocalsproxy_iter,
+ .tp_methods = framelocalsproxy_methods,
+ .tp_alloc = PyType_GenericAlloc,
+ .tp_new = framelocalsproxy_new,
+ .tp_free = PyObject_GC_Del,
+};
+
+PyObject *
+_PyFrameLocalsProxy_New(PyFrameObject *frame)
+{
+ PyObject* args = PyTuple_Pack(1, frame);
+ if (args == NULL) {
+ return NULL;
+ }
+
+ PyObject* proxy = (PyObject*)framelocalsproxy_new(&PyFrameLocalsProxy_Type, args, NULL);
+ Py_DECREF(args);
+ return proxy;
+}
+
static PyMemberDef frame_memberlist[] = {
- {"f_trace_lines", T_BOOL, OFF(f_trace_lines), 0},
+ {"f_trace_lines", Py_T_BOOL, OFF(f_trace_lines), 0},
{NULL} /* Sentinel */
};
-
static PyObject *
frame_getlocals(PyFrameObject *f, void *closure)
{
@@ -29,23 +869,43 @@ frame_getlocals(PyFrameObject *f, void *closure)
return NULL;
}
assert(!_PyFrame_IsIncomplete(f->f_frame));
- PyObject *locals = _PyFrame_GetLocals(f->f_frame, 1);
- if (locals) {
- f->f_fast_as_locals = 1;
+
+ PyCodeObject *co = _PyFrame_GetCode(f->f_frame);
+
+ if (!(co->co_flags & CO_OPTIMIZED) && !_PyFrame_HasHiddenLocals(f->f_frame)) {
+ if (f->f_frame->f_locals == NULL) {
+ // We found cases when f_locals is NULL for non-optimized code.
+ // We fill the f_locals with an empty dict to avoid crash until
+ // we find the root cause.
+ f->f_frame->f_locals = PyDict_New();
+ if (f->f_frame->f_locals == NULL) {
+ return NULL;
+ }
+ }
+ return Py_NewRef(f->f_frame->f_locals);
}
- return locals;
+
+ return _PyFrameLocalsProxy_New(f);
}
int
PyFrame_GetLineNumber(PyFrameObject *f)
{
assert(f != NULL);
- if (f->f_lineno != 0) {
- return f->f_lineno;
+ if (f->f_lineno == -1) {
+ // We should calculate it once. If we can't get the line number,
+ // set f->f_lineno to 0.
+ f->f_lineno = PyUnstable_InterpreterFrame_GetLine(f->f_frame);
+ if (f->f_lineno < 0) {
+ f->f_lineno = 0;
+ return -1;
+ }
}
- else {
- return PyUnstable_InterpreterFrame_GetLine(f->f_frame);
+
+ if (f->f_lineno > 0) {
+ return f->f_lineno;
}
+ return PyUnstable_InterpreterFrame_GetLine(f->f_frame);
}
static PyObject *
@@ -126,10 +986,13 @@ frame_settrace_opcodes(PyFrameObject *f, PyObject* value, void *Py_UNUSED(ignore
}
if (value == Py_True) {
f->f_trace_opcodes = 1;
- _PyInterpreterState_GET()->f_opcode_trace_set = true;
+ if (f->f_trace) {
+ return _PyEval_SetOpcodeTrace(f, true);
+ }
}
else {
f->f_trace_opcodes = 0;
+ return _PyEval_SetOpcodeTrace(f, false);
}
return 0;
}
@@ -300,11 +1163,6 @@ mark_stacks(PyCodeObject *code_obj, int len)
stacks[i] = UNINITIALIZED;
}
stacks[0] = EMPTY_STACK;
- if (code_obj->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR))
- {
- // Generators get sent None while starting:
- stacks[0] = push_value(stacks[0], Object);
- }
int todo = 1;
while (todo) {
todo = 0;
@@ -356,7 +1214,7 @@ mark_stacks(PyCodeObject *code_obj, int len)
break;
case JUMP_BACKWARD:
case JUMP_BACKWARD_NO_INTERRUPT:
- j = i + 1 - oparg;
+ j = next_i - oparg;
assert(j >= 0);
assert(j < len);
if (stacks[j] == UNINITIALIZED && j < i) {
@@ -412,10 +1270,10 @@ mark_stacks(PyCodeObject *code_obj, int len)
case LOAD_GLOBAL:
{
int j = oparg;
+ next_stack = push_value(next_stack, Object);
if (j & 1) {
next_stack = push_value(next_stack, Null);
}
- next_stack = push_value(next_stack, Object);
stacks[next_i] = next_stack;
break;
}
@@ -425,22 +1283,12 @@ mark_stacks(PyCodeObject *code_obj, int len)
int j = oparg;
if (j & 1) {
next_stack = pop_value(next_stack);
- next_stack = push_value(next_stack, Null);
next_stack = push_value(next_stack, Object);
+ next_stack = push_value(next_stack, Null);
}
stacks[next_i] = next_stack;
break;
}
- case CALL:
- {
- int args = oparg;
- for (int j = 0; j < args+2; j++) {
- next_stack = pop_value(next_stack);
- }
- next_stack = push_value(next_stack, Object);
- stacks[next_i] = next_stack;
- break;
- }
case SWAP:
{
int n = oparg;
@@ -598,39 +1446,14 @@ first_line_not_before(int *lines, int len, int line)
return result;
}
-static PyFrameState
-_PyFrame_GetState(PyFrameObject *frame)
+static bool frame_is_suspended(PyFrameObject *frame)
{
assert(!_PyFrame_IsIncomplete(frame->f_frame));
- if (frame->f_frame->stacktop == 0) {
- return FRAME_CLEARED;
- }
- switch(frame->f_frame->owner) {
- case FRAME_OWNED_BY_GENERATOR:
- {
- PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame);
- return gen->gi_frame_state;
- }
- case FRAME_OWNED_BY_THREAD:
- {
- if (_PyInterpreterFrame_LASTI(frame->f_frame) < 0) {
- return FRAME_CREATED;
- }
- switch (frame->f_frame->prev_instr->op.code)
- {
- case COPY_FREE_VARS:
- case MAKE_CELL:
- case RETURN_GENERATOR:
- /* Frame not fully initialized */
- return FRAME_CREATED;
- default:
- return FRAME_EXECUTING;
- }
- }
- case FRAME_OWNED_BY_FRAME_OBJECT:
- return FRAME_COMPLETED;
+ if (frame->f_frame->owner == FRAME_OWNED_BY_GENERATOR) {
+ PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame);
+ return FRAME_STATE_SUSPENDED(gen->gi_frame_state);
}
- Py_UNREACHABLE();
+ return false;
}
/* Setter for f_lineno - you can set f_lineno from within a trace function in
@@ -650,6 +1473,7 @@ _PyFrame_GetState(PyFrameObject *frame)
static int
frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignored))
{
+ PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
if (p_new_lineno == NULL) {
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
return -1;
@@ -661,7 +1485,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
return -1;
}
- PyFrameState state = _PyFrame_GetState(f);
+ bool is_suspended = frame_is_suspended(f);
/*
* This code preserves the historical restrictions on
* setting the line number of a frame.
@@ -727,7 +1551,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
}
new_lineno = (int)l_new_lineno;
- if (new_lineno < f->f_frame->f_code->co_firstlineno) {
+ if (new_lineno < code->co_firstlineno) {
PyErr_Format(PyExc_ValueError,
"line %d comes before the current code block",
new_lineno);
@@ -736,8 +1560,8 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
/* PyCode_NewWithPosOnlyArgs limits co_code to be under INT_MAX so this
* should never overflow. */
- int len = (int)Py_SIZE(f->f_frame->f_code);
- int *lines = marklines(f->f_frame->f_code, len);
+ int len = (int)Py_SIZE(code);
+ int *lines = marklines(code, len);
if (lines == NULL) {
return -1;
}
@@ -751,7 +1575,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
return -1;
}
- int64_t *stacks = mark_stacks(f->f_frame->f_code, len);
+ int64_t *stacks = mark_stacks(code, len);
if (stacks == NULL) {
PyMem_Free(lines);
return -1;
@@ -796,7 +1620,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
// in the new location. Rather than crashing or changing co_code, just bind
// None instead:
int unbound = 0;
- for (int i = 0; i < f->f_frame->f_code->co_nlocalsplus; i++) {
+ for (int i = 0; i < code->co_nlocalsplus; i++) {
// Counting every unbound local is overly-cautious, but a full flow
// analysis (like we do in the compiler) is probably too expensive:
unbound += f->f_frame->localsplus[i] == NULL;
@@ -809,7 +1633,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
}
// Do this in a second pass to avoid writing a bunch of Nones when
// warnings are being treated as errors and the previous bit raises:
- for (int i = 0; i < f->f_frame->f_code->co_nlocalsplus; i++) {
+ for (int i = 0; i < code->co_nlocalsplus; i++) {
if (f->f_frame->localsplus[i] == NULL) {
f->f_frame->localsplus[i] = Py_NewRef(Py_None);
unbound--;
@@ -817,20 +1641,17 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
}
assert(unbound == 0);
}
- if (state == FRAME_SUSPENDED) {
+ if (is_suspended) {
/* Account for value popped by yield */
start_stack = pop_value(start_stack);
}
while (start_stack > best_stack) {
if (top_of_stack(start_stack) == Except) {
/* Pop exception stack as well as the evaluation stack */
- PyThreadState *tstate = _PyThreadState_GET();
- _PyErr_StackItem *exc_info = tstate->exc_info;
- PyObject *value = exc_info->exc_value;
PyObject *exc = _PyFrame_StackPop(f->f_frame);
assert(PyExceptionInstance_Check(exc) || exc == Py_None);
- exc_info->exc_value = exc;
- Py_XDECREF(value);
+ PyThreadState *tstate = _PyThreadState_GET();
+ Py_XSETREF(tstate->exc_info->exc_value, exc == Py_None ? NULL : exc);
}
else {
PyObject *v = _PyFrame_StackPop(f->f_frame);
@@ -840,7 +1661,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore
}
/* Finally set the new lasti and return OK. */
f->f_lineno = 0;
- f->f_frame->prev_instr = _PyCode_CODE(f->f_frame->f_code) + best_addr;
+ f->f_frame->instr_ptr = _PyCode_CODE(code) + best_addr;
return 0;
}
@@ -861,6 +1682,9 @@ frame_settrace(PyFrameObject *f, PyObject* v, void *closure)
}
if (v != f->f_trace) {
Py_XSETREF(f->f_trace, Py_XNewRef(v));
+ if (v != NULL && f->f_trace_opcodes) {
+ return _PyEval_SetOpcodeTrace(f, true);
+ }
}
return 0;
}
@@ -891,7 +1715,7 @@ frame_dealloc(PyFrameObject *f)
}
Py_TRASHCAN_BEGIN(f, frame_dealloc);
- PyCodeObject *co = NULL;
+ PyObject *co = NULL;
/* GH-106092: If f->f_frame was on the stack and we reached the maximum
* nesting depth for deallocations, the trashcan may have delayed this
@@ -902,8 +1726,8 @@ frame_dealloc(PyFrameObject *f)
/* Kill all local variables including specials, if we own them */
if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) {
/* Don't clear code object until the end */
- co = frame->f_code;
- frame->f_code = NULL;
+ co = frame->f_executable;
+ frame->f_executable = NULL;
Py_CLEAR(frame->f_funcobj);
Py_CLEAR(frame->f_locals);
PyObject **locals = _PyFrame_GetLocalsArray(frame);
@@ -913,6 +1737,8 @@ frame_dealloc(PyFrameObject *f)
}
Py_CLEAR(f->f_back);
Py_CLEAR(f->f_trace);
+ Py_CLEAR(f->f_extra_locals);
+ Py_CLEAR(f->f_locals_cache);
PyObject_GC_Del(f);
Py_XDECREF(co);
Py_TRASHCAN_END;
@@ -923,6 +1749,8 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg)
{
Py_VISIT(f->f_back);
Py_VISIT(f->f_trace);
+ Py_VISIT(f->f_extra_locals);
+ Py_VISIT(f->f_locals_cache);
if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) {
return 0;
}
@@ -934,6 +1762,8 @@ static int
frame_tp_clear(PyFrameObject *f)
{
Py_CLEAR(f->f_trace);
+ Py_CLEAR(f->f_extra_locals);
+ Py_CLEAR(f->f_locals_cache);
/* locals and stack */
PyObject **locals = _PyFrame_GetLocalsArray(f->f_frame);
@@ -942,6 +1772,7 @@ frame_tp_clear(PyFrameObject *f)
Py_CLEAR(locals[i]);
}
f->f_frame->stacktop = 0;
+ Py_CLEAR(f->f_frame->f_locals);
return 0;
}
@@ -953,6 +1784,9 @@ frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
if (gen->gi_frame_state == FRAME_EXECUTING) {
goto running;
}
+ if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) {
+ goto suspended;
+ }
_PyGen_Finalize((PyObject *)gen);
}
else if (f->f_frame->owner == FRAME_OWNED_BY_THREAD) {
@@ -967,6 +1801,10 @@ running:
PyErr_SetString(PyExc_RuntimeError,
"cannot clear an executing frame");
return NULL;
+suspended:
+ PyErr_SetString(PyExc_RuntimeError,
+ "cannot clear a suspended frame");
+ return NULL;
}
PyDoc_STRVAR(clear__doc__,
@@ -977,7 +1815,7 @@ frame_sizeof(PyFrameObject *f, PyObject *Py_UNUSED(ignored))
{
Py_ssize_t res;
res = offsetof(PyFrameObject, _f_frame_data) + offsetof(_PyInterpreterFrame, localsplus);
- PyCodeObject *code = f->f_frame->f_code;
+ PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
res += _PyFrame_NumSlotsForCodeObject(code) * sizeof(PyObject *);
return PyLong_FromSsize_t(res);
}
@@ -989,7 +1827,7 @@ static PyObject *
frame_repr(PyFrameObject *f)
{
int lineno = PyFrame_GetLineNumber(f);
- PyCodeObject *code = f->f_frame->f_code;
+ PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
return PyUnicode_FromFormat(
"<frame at %p, file %R, line %d, code %S>",
f, code->co_filename, lineno, code->co_name);
@@ -1061,8 +1899,9 @@ _PyFrame_New_NoTrack(PyCodeObject *code)
f->f_trace = NULL;
f->f_trace_lines = 1;
f->f_trace_opcodes = 0;
- f->f_fast_as_locals = 0;
f->f_lineno = 0;
+ f->f_extra_locals = NULL;
+ f->f_locals_cache = NULL;
return f;
}
@@ -1098,7 +1937,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code,
f->f_frame = (_PyInterpreterFrame *)f->_f_frame_data;
f->f_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT;
// This frame needs to be "complete", so pretend that the first RESUME ran:
- f->f_frame->prev_instr = _PyCode_CODE(code) + code->_co_firsttraceable;
+ f->f_frame->instr_ptr = _PyCode_CODE(code) + code->_co_firsttraceable + 1;
assert(!_PyFrame_IsIncomplete(f->f_frame));
Py_DECREF(func);
_PyObject_GC_TRACK(f);
@@ -1111,8 +1950,8 @@ _PyFrame_OpAlreadyRan(_PyInterpreterFrame *frame, int opcode, int oparg)
// This only works when opcode is a non-quickened form:
assert(_PyOpcode_Deopt[opcode] == opcode);
int check_oparg = 0;
- for (_Py_CODEUNIT *instruction = _PyCode_CODE(frame->f_code);
- instruction < frame->prev_instr; instruction++)
+ for (_Py_CODEUNIT *instruction = _PyCode_CODE(_PyFrame_GetCode(frame));
+ instruction < frame->instr_ptr; instruction++)
{
int check_opcode = _PyOpcode_Deopt[instruction->op.code];
check_oparg |= instruction->op.arg;
@@ -1137,7 +1976,7 @@ frame_init_get_vars(_PyInterpreterFrame *frame)
{
// COPY_FREE_VARS has no quickened forms, so no need to use _PyOpcode_Deopt
// here:
- PyCodeObject *co = frame->f_code;
+ PyCodeObject *co = _PyFrame_GetCode(frame);
int lasti = _PyInterpreterFrame_LASTI(frame);
if (!(lasti < 0 && _PyCode_CODE(co)->op.code == COPY_FREE_VARS
&& PyFunction_Check(frame->f_funcobj)))
@@ -1148,13 +1987,13 @@ frame_init_get_vars(_PyInterpreterFrame *frame)
/* Free vars have not been initialized -- Do that */
PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure;
- int offset = PyCode_GetFirstFree(co);
+ int offset = PyUnstable_Code_GetFirstFree(co);
for (int i = 0; i < co->co_nfreevars; ++i) {
PyObject *o = PyTuple_GET_ITEM(closure, i);
frame->localsplus[offset + i] = Py_NewRef(o);
}
// COPY_FREE_VARS doesn't have inline CACHEs, either:
- frame->prev_instr = _PyCode_CODE(frame->f_code);
+ frame->instr_ptr = _PyCode_CODE(_PyFrame_GetCode(frame));
}
@@ -1194,10 +2033,9 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
// (likely) MAKE_CELL must have executed already.
value = PyCell_GET(value);
}
- // (likely) Otherwise it it is an arg (kind & CO_FAST_LOCAL),
+ // (likely) Otherwise it is an arg (kind & CO_FAST_LOCAL),
// with the initial value set when the frame was created...
- // (unlikely) ...or it was set to some initial value by
- // an earlier call to PyFrame_LocalsToFast().
+ // (unlikely) ...or it was set via the f_locals proxy.
}
}
}
@@ -1209,103 +2047,57 @@ frame_get_var(_PyInterpreterFrame *frame, PyCodeObject *co, int i,
}
-PyObject *
-_PyFrame_GetLocals(_PyInterpreterFrame *frame, int include_hidden)
+bool
+_PyFrame_HasHiddenLocals(_PyInterpreterFrame *frame)
{
- /* Merge fast locals into f->f_locals */
- PyObject *locals = frame->f_locals;
- if (locals == NULL) {
- locals = frame->f_locals = PyDict_New();
- if (locals == NULL) {
- return NULL;
- }
- }
- PyObject *hidden = NULL;
-
- /* If include_hidden, "hidden" fast locals (from inlined comprehensions in
- module/class scopes) will be included in the returned dict, but not in
- frame->f_locals; the returned dict will be a modified copy. Non-hidden
- locals will still be updated in frame->f_locals. */
- if (include_hidden) {
- hidden = PyDict_New();
- if (hidden == NULL) {
- return NULL;
- }
- }
-
- frame_init_get_vars(frame);
+ /*
+ * This function returns if there are hidden locals introduced by PEP 709,
+ * which are the isolated fast locals for inline comprehensions
+ */
+ PyCodeObject* co = _PyFrame_GetCode(frame);
- PyCodeObject *co = frame->f_code;
for (int i = 0; i < co->co_nlocalsplus; i++) {
- PyObject *value; // borrowed reference
- if (!frame_get_var(frame, co, i, &value)) {
- continue;
- }
-
- PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
_PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
+
if (kind & CO_FAST_HIDDEN) {
- if (include_hidden && value != NULL) {
- if (PyObject_SetItem(hidden, name, value) != 0) {
- goto error;
- }
- }
- continue;
- }
- if (value == NULL) {
- if (PyObject_DelItem(locals, name) != 0) {
- if (PyErr_ExceptionMatches(PyExc_KeyError)) {
- PyErr_Clear();
- }
- else {
- goto error;
- }
- }
- }
- else {
- if (PyObject_SetItem(locals, name, value) != 0) {
- goto error;
- }
- }
- }
+ PyObject* value = framelocalsproxy_getval(frame, co, i);
- if (include_hidden && PyDict_Size(hidden)) {
- PyObject *innerlocals = PyDict_New();
- if (innerlocals == NULL) {
- goto error;
- }
- if (PyDict_Merge(innerlocals, locals, 1) != 0) {
- Py_DECREF(innerlocals);
- goto error;
- }
- if (PyDict_Merge(innerlocals, hidden, 1) != 0) {
- Py_DECREF(innerlocals);
- goto error;
+ if (value != NULL) {
+ return true;
+ }
}
- locals = innerlocals;
- }
- else {
- Py_INCREF(locals);
}
- Py_CLEAR(hidden);
- return locals;
-
- error:
- Py_XDECREF(hidden);
- return NULL;
+ return false;
}
-int
-_PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame)
+PyObject *
+_PyFrame_GetLocals(_PyInterpreterFrame *frame)
{
- PyObject *locals = _PyFrame_GetLocals(frame, 0);
- if (locals == NULL) {
- return -1;
+ // We should try to avoid creating the FrameObject if possible.
+ // So we check if the frame is a module or class level scope
+ PyCodeObject *co = _PyFrame_GetCode(frame);
+
+ if (!(co->co_flags & CO_OPTIMIZED) && !_PyFrame_HasHiddenLocals(frame)) {
+ if (frame->f_locals == NULL) {
+ // We found cases when f_locals is NULL for non-optimized code.
+ // We fill the f_locals with an empty dict to avoid crash until
+ // we find the root cause.
+ frame->f_locals = PyDict_New();
+ if (frame->f_locals == NULL) {
+ return NULL;
+ }
+ }
+ return Py_NewRef(frame->f_locals);
}
- Py_DECREF(locals);
- return 0;
+
+ PyFrameObject* f = _PyFrame_GetFrameObject(frame);
+ if (f == NULL) {
+ return NULL;
+ }
+
+ return _PyFrameLocalsProxy_New(f);
}
@@ -1321,7 +2113,7 @@ PyFrame_GetVar(PyFrameObject *frame_obj, PyObject *name)
_PyInterpreterFrame *frame = frame_obj->f_frame;
frame_init_get_vars(frame);
- PyCodeObject *co = frame->f_code;
+ PyCodeObject *co = _PyFrame_GetCode(frame);
for (int i = 0; i < co->co_nlocalsplus; i++) {
PyObject *var_name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
if (!_PyUnicode_Equal(var_name, name)) {
@@ -1359,112 +2151,25 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name)
int
PyFrame_FastToLocalsWithError(PyFrameObject *f)
{
- if (f == NULL) {
- PyErr_BadInternalCall();
- return -1;
- }
- assert(!_PyFrame_IsIncomplete(f->f_frame));
- int err = _PyFrame_FastToLocalsWithError(f->f_frame);
- if (err == 0) {
- f->f_fast_as_locals = 1;
- }
- return err;
+ // Nothing to do here, as f_locals is now a write-through proxy in
+ // optimized frames. Soft-deprecated, since there's no maintenance hassle.
+ return 0;
}
void
PyFrame_FastToLocals(PyFrameObject *f)
{
- int res;
- assert(!_PyFrame_IsIncomplete(f->f_frame));
- assert(!PyErr_Occurred());
-
- res = PyFrame_FastToLocalsWithError(f);
- if (res < 0)
- PyErr_Clear();
-}
-
-void
-_PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear)
-{
- /* Merge locals into fast locals */
- PyObject *locals;
- PyObject **fast;
- PyCodeObject *co;
- locals = frame->f_locals;
- if (locals == NULL) {
- return;
- }
- fast = _PyFrame_GetLocalsArray(frame);
- co = frame->f_code;
-
- PyObject *exc = PyErr_GetRaisedException();
- for (int i = 0; i < co->co_nlocalsplus; i++) {
- _PyLocals_Kind kind = _PyLocals_GetKind(co->co_localspluskinds, i);
-
- /* Same test as in PyFrame_FastToLocals() above. */
- if (kind & CO_FAST_FREE && !(co->co_flags & CO_OPTIMIZED)) {
- continue;
- }
- PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
- PyObject *value = PyObject_GetItem(locals, name);
- /* We only care about NULLs if clear is true. */
- if (value == NULL) {
- PyErr_Clear();
- if (!clear) {
- continue;
- }
- }
- PyObject *oldvalue = fast[i];
- PyObject *cell = NULL;
- if (kind == CO_FAST_FREE) {
- // The cell was set when the frame was created from
- // the function's closure.
- assert(oldvalue != NULL && PyCell_Check(oldvalue));
- cell = oldvalue;
- }
- else if (kind & CO_FAST_CELL && oldvalue != NULL) {
- /* Same test as in PyFrame_FastToLocals() above. */
- if (PyCell_Check(oldvalue) &&
- _PyFrame_OpAlreadyRan(frame, MAKE_CELL, i)) {
- // (likely) MAKE_CELL must have executed already.
- cell = oldvalue;
- }
- // (unlikely) Otherwise, it must have been set to some
- // initial value by an earlier call to PyFrame_LocalsToFast().
- }
- if (cell != NULL) {
- oldvalue = PyCell_GET(cell);
- if (value != oldvalue) {
- PyCell_SET(cell, Py_XNewRef(value));
- Py_XDECREF(oldvalue);
- }
- }
- else if (value != oldvalue) {
- if (value == NULL) {
- // Probably can't delete this, since the compiler's flow
- // analysis may have already "proven" that it exists here:
- const char *e = "assigning None to unbound local %R";
- if (PyErr_WarnFormat(PyExc_RuntimeWarning, 0, e, name)) {
- // It's okay if frame_obj is NULL, just try anyways:
- PyErr_WriteUnraisable((PyObject *)frame->frame_obj);
- }
- value = Py_NewRef(Py_None);
- }
- Py_XSETREF(fast[i], Py_NewRef(value));
- }
- Py_XDECREF(value);
- }
- PyErr_SetRaisedException(exc);
+ // Nothing to do here, as f_locals is now a write-through proxy in
+ // optimized frames. Soft-deprecated, since there's no maintenance hassle.
+ return;
}
void
PyFrame_LocalsToFast(PyFrameObject *f, int clear)
{
- assert(!_PyFrame_IsIncomplete(f->f_frame));
- if (f && f->f_fast_as_locals && _PyFrame_GetState(f) != FRAME_CLEARED) {
- _PyFrame_LocalsToFast(f->f_frame, clear);
- f->f_fast_as_locals = 0;
- }
+ // Nothing to do here, as f_locals is now a write-through proxy in
+ // optimized frames. Soft-deprecated, since there's no maintenance hassle.
+ return;
}
int
@@ -1481,7 +2186,7 @@ PyFrame_GetCode(PyFrameObject *frame)
{
assert(frame != NULL);
assert(!_PyFrame_IsIncomplete(frame->f_frame));
- PyCodeObject *code = frame->f_frame->f_code;
+ PyCodeObject *code = _PyFrame_GetCode(frame->f_frame);
assert(code != NULL);
return (PyCodeObject*)Py_NewRef(code);
}
diff --git a/contrib/tools/python3/Objects/funcobject.c b/contrib/tools/python3/Objects/funcobject.c
index f43e3a2787b..a39b5464f4d 100644
--- a/contrib/tools/python3/Objects/funcobject.c
+++ b/contrib/tools/python3/Objects/funcobject.c
@@ -2,13 +2,14 @@
/* Function object implementation */
#include "Python.h"
-#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
-#include "pycore_code.h" // _Py_next_func_version
-#include "pycore_object.h" // _PyObject_GC_UNTRACK()
-#include "pycore_pyerrors.h" // _PyErr_Occurred()
-#include "structmember.h" // PyMemberDef
+#include "pycore_ceval.h" // _PyEval_BuiltinsFromGlobals()
+#include "pycore_dict.h" // _Py_INCREF_DICT()
+#include "pycore_long.h" // _PyLong_GetOne()
+#include "pycore_modsupport.h" // _PyArg_NoKeywords()
+#include "pycore_object.h" // _PyObject_GC_UNTRACK()
+#include "pycore_pyerrors.h" // _PyErr_Occurred()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
-static PyObject* func_repr(PyFunctionObject *op);
static const char *
func_event_name(PyFunction_WatchEvent event) {
@@ -35,21 +36,9 @@ notify_func_watchers(PyInterpreterState *interp, PyFunction_WatchEvent event,
// callback must be non-null if the watcher bit is set
assert(cb != NULL);
if (cb(event, func, new_value) < 0) {
- // Don't risk resurrecting the func if an unraisablehook keeps a
- // reference; pass a string as context.
- PyObject *context = NULL;
- PyObject *repr = func_repr(func);
- if (repr != NULL) {
- context = PyUnicode_FromFormat(
- "%s watcher callback for %U",
- func_event_name(event), repr);
- Py_DECREF(repr);
- }
- if (context == NULL) {
- context = Py_NewRef(Py_None);
- }
- PyErr_WriteUnraisable(context);
- Py_DECREF(context);
+ PyErr_FormatUnraisable(
+ "Exception ignored in %s watcher callback for function %U at %p",
+ func_event_name(event), func->func_qualname, func);
}
}
i++;
@@ -67,6 +56,15 @@ handle_func_event(PyFunction_WatchEvent event, PyFunctionObject *func,
if (interp->active_func_watchers) {
notify_func_watchers(interp, event, func, new_value);
}
+ switch (event) {
+ case PyFunction_EVENT_MODIFY_CODE:
+ case PyFunction_EVENT_MODIFY_DEFAULTS:
+ case PyFunction_EVENT_MODIFY_KWDEFAULTS:
+ RARE_EVENT_INTERP_INC(interp, func_modification);
+ break;
+ default:
+ break;
+ }
}
int
@@ -106,8 +104,8 @@ PyFunction_ClearWatcher(int watcher_id)
PyFunctionObject *
_PyFunction_FromConstructor(PyFrameConstructor *constr)
{
- PyObject *module = Py_XNewRef(PyDict_GetItemWithError(constr->fc_globals, &_Py_ID(__name__)));
- if (!module && PyErr_Occurred()) {
+ PyObject *module;
+ if (PyDict_GetItemRef(constr->fc_globals, &_Py_ID(__name__), &module) < 0) {
return NULL;
}
@@ -132,6 +130,9 @@ _PyFunction_FromConstructor(PyFrameConstructor *constr)
op->func_typeparams = NULL;
op->vectorcall = _PyFunction_Vectorcall;
op->func_version = 0;
+ // NOTE: functions created via FrameConstructor do not use deferred
+ // reference counting because they are typically not part of cycles
+ // nor accessed by multiple threads.
_PyObject_GC_TRACK(op);
handle_func_event(PyFunction_EVENT_CREATE, op, NULL);
return op;
@@ -172,12 +173,11 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
Py_INCREF(doc);
// __module__: Use globals['__name__'] if it exists, or NULL.
- PyObject *module = PyDict_GetItemWithError(globals, &_Py_ID(__name__));
+ PyObject *module;
PyObject *builtins = NULL;
- if (module == NULL && _PyErr_Occurred(tstate)) {
+ if (PyDict_GetItemRef(globals, &_Py_ID(__name__), &module) < 0) {
goto error;
}
- Py_XINCREF(module);
builtins = _PyEval_BuiltinsFromGlobals(tstate, globals); // borrowed ref
if (builtins == NULL) {
@@ -208,6 +208,12 @@ PyFunction_NewWithQualName(PyObject *code, PyObject *globals, PyObject *qualname
op->func_typeparams = NULL;
op->vectorcall = _PyFunction_Vectorcall;
op->func_version = 0;
+ if ((code_obj->co_flags & CO_NESTED) == 0) {
+ // Use deferred reference counting for top-level functions, but not
+ // nested functions because they are more likely to capture variables,
+ // which makes prompt deallocation more important.
+ _PyObject_SetDeferredRefcount((PyObject *)op);
+ }
_PyObject_GC_TRACK(op);
handle_func_event(PyFunction_EVENT_CREATE, op, NULL);
return (PyObject *)op;
@@ -223,21 +229,142 @@ error:
return NULL;
}
-uint32_t _PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
+/*
+(This is purely internal documentation. There are no public APIs here.)
+
+Function (and code) versions
+----------------------------
+
+The Tier 1 specializer generates CALL variants that can be invalidated
+by changes to critical function attributes:
+
+- __code__
+- __defaults__
+- __kwdefaults__
+- __closure__
+
+For this purpose function objects have a 32-bit func_version member
+that the specializer writes to the specialized instruction's inline
+cache and which is checked by a guard on the specialized instructions.
+
+The MAKE_FUNCTION bytecode sets func_version from the code object's
+co_version field. The latter is initialized from a counter in the
+interpreter state (interp->func_state.next_version) and never changes.
+When this counter overflows, it remains zero and the specializer loses
+the ability to specialize calls to new functions.
+
+The func_version is reset to zero when any of the critical attributes
+is modified; after this point the specializer will no longer specialize
+calls to this function, and the guard will always fail.
+
+The function and code version cache
+-----------------------------------
+
+The Tier 2 optimizer now has a problem, since it needs to find the
+function and code objects given only the version number from the inline
+cache. Our solution is to maintain a cache mapping version numbers to
+function and code objects. To limit the cache size we could hash
+the version number, but for now we simply use it modulo the table size.
+
+There are some corner cases (e.g. generator expressions) where we will
+be unable to find the function object in the cache but we can still
+find the code object. For this reason the cache stores both the
+function object and the code object.
+
+The cache doesn't contain strong references; cache entries are
+invalidated whenever the function or code object is deallocated.
+
+Invariants
+----------
+
+These should hold at any time except when one of the cache-mutating
+functions is running.
+
+- For any slot s at index i:
+ - s->func == NULL or s->func->func_version % FUNC_VERSION_CACHE_SIZE == i
+ - s->code == NULL or s->code->co_version % FUNC_VERSION_CACHE_SIZE == i
+ if s->func != NULL, then s->func->func_code == s->code
+
+*/
+
+void
+_PyFunction_SetVersion(PyFunctionObject *func, uint32_t version)
{
+#ifndef Py_GIL_DISABLED
+ PyInterpreterState *interp = _PyInterpreterState_GET();
if (func->func_version != 0) {
- return func->func_version;
+ struct _func_version_cache_item *slot =
+ interp->func_state.func_version_cache
+ + (func->func_version % FUNC_VERSION_CACHE_SIZE);
+ if (slot->func == func) {
+ slot->func = NULL;
+ // Leave slot->code alone, there may be use for it.
+ }
}
- if (func->vectorcall != _PyFunction_Vectorcall) {
- return 0;
+#endif
+ func->func_version = version;
+#ifndef Py_GIL_DISABLED
+ if (version != 0) {
+ struct _func_version_cache_item *slot =
+ interp->func_state.func_version_cache
+ + (version % FUNC_VERSION_CACHE_SIZE);
+ slot->func = func;
+ slot->code = func->func_code;
}
+#endif
+}
+
+void
+_PyFunction_ClearCodeByVersion(uint32_t version)
+{
+#ifndef Py_GIL_DISABLED
PyInterpreterState *interp = _PyInterpreterState_GET();
- if (interp->func_state.next_version == 0) {
- return 0;
+ struct _func_version_cache_item *slot =
+ interp->func_state.func_version_cache
+ + (version % FUNC_VERSION_CACHE_SIZE);
+ if (slot->code) {
+ assert(PyCode_Check(slot->code));
+ PyCodeObject *code = (PyCodeObject *)slot->code;
+ if (code->co_version == version) {
+ slot->code = NULL;
+ slot->func = NULL;
+ }
}
- uint32_t v = interp->func_state.next_version++;
- func->func_version = v;
- return v;
+#endif
+}
+
+PyFunctionObject *
+_PyFunction_LookupByVersion(uint32_t version, PyObject **p_code)
+{
+#ifdef Py_GIL_DISABLED
+ return NULL;
+#else
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ struct _func_version_cache_item *slot =
+ interp->func_state.func_version_cache
+ + (version % FUNC_VERSION_CACHE_SIZE);
+ if (slot->code) {
+ assert(PyCode_Check(slot->code));
+ PyCodeObject *code = (PyCodeObject *)slot->code;
+ if (code->co_version == version) {
+ *p_code = slot->code;
+ }
+ }
+ else {
+ *p_code = NULL;
+ }
+ if (slot->func && slot->func->func_version == version) {
+ assert(slot->func->func_code == slot->code);
+ return slot->func;
+ }
+ return NULL;
+#endif
+}
+
+uint32_t
+_PyFunction_GetVersionForCurrentState(PyFunctionObject *func)
+{
+ return func->func_version;
}
PyObject *
@@ -304,7 +431,7 @@ PyFunction_SetDefaults(PyObject *op, PyObject *defaults)
}
handle_func_event(PyFunction_EVENT_MODIFY_DEFAULTS,
(PyFunctionObject *) op, defaults);
- ((PyFunctionObject *)op)->func_version = 0;
+ _PyFunction_SetVersion((PyFunctionObject *)op, 0);
Py_XSETREF(((PyFunctionObject *)op)->func_defaults, defaults);
return 0;
}
@@ -313,7 +440,7 @@ void
PyFunction_SetVectorcall(PyFunctionObject *func, vectorcallfunc vectorcall)
{
assert(func != NULL);
- func->func_version = 0;
+ _PyFunction_SetVersion(func, 0);
func->vectorcall = vectorcall;
}
@@ -346,7 +473,7 @@ PyFunction_SetKwDefaults(PyObject *op, PyObject *defaults)
}
handle_func_event(PyFunction_EVENT_MODIFY_KWDEFAULTS,
(PyFunctionObject *) op, defaults);
- ((PyFunctionObject *)op)->func_version = 0;
+ _PyFunction_SetVersion((PyFunctionObject *)op, 0);
Py_XSETREF(((PyFunctionObject *)op)->func_kwdefaults, defaults);
return 0;
}
@@ -379,7 +506,7 @@ PyFunction_SetClosure(PyObject *op, PyObject *closure)
Py_TYPE(closure)->tp_name);
return -1;
}
- ((PyFunctionObject *)op)->func_version = 0;
+ _PyFunction_SetVersion((PyFunctionObject *)op, 0);
Py_XSETREF(((PyFunctionObject *)op)->func_closure, closure);
return 0;
}
@@ -441,7 +568,6 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)
"non-dict annotations");
return -1;
}
- ((PyFunctionObject *)op)->func_version = 0;
Py_XSETREF(((PyFunctionObject *)op)->func_annotations, annotations);
return 0;
}
@@ -451,17 +577,25 @@ PyFunction_SetAnnotations(PyObject *op, PyObject *annotations)
#define OFF(x) offsetof(PyFunctionObject, x)
static PyMemberDef func_memberlist[] = {
- {"__closure__", T_OBJECT, OFF(func_closure), READONLY},
- {"__doc__", T_OBJECT, OFF(func_doc), 0},
- {"__globals__", T_OBJECT, OFF(func_globals), READONLY},
- {"__module__", T_OBJECT, OFF(func_module), 0},
- {"__builtins__", T_OBJECT, OFF(func_builtins), READONLY},
+ {"__closure__", _Py_T_OBJECT, OFF(func_closure), Py_READONLY},
+ {"__doc__", _Py_T_OBJECT, OFF(func_doc), 0},
+ {"__globals__", _Py_T_OBJECT, OFF(func_globals), Py_READONLY},
+ {"__module__", _Py_T_OBJECT, OFF(func_module), 0},
+ {"__builtins__", _Py_T_OBJECT, OFF(func_builtins), Py_READONLY},
{NULL} /* Sentinel */
};
+/*[clinic input]
+class function "PyFunctionObject *" "&PyFunction_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=70af9c90aa2e71b0]*/
+
+#include "clinic/funcobject.c.h"
+
static PyObject *
-func_get_code(PyFunctionObject *op, void *Py_UNUSED(ignored))
+func_get_code(PyObject *self, void *Py_UNUSED(ignored))
{
+ PyFunctionObject* op = _PyFunction_CAST(self);
if (PySys_Audit("object.__getattr__", "Os", op, "__code__") < 0) {
return NULL;
}
@@ -470,8 +604,9 @@ func_get_code(PyFunctionObject *op, void *Py_UNUSED(ignored))
}
static int
-func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
+func_set_code(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
{
+ PyFunctionObject* op = _PyFunction_CAST(self);
Py_ssize_t nclosure;
int nfree;
@@ -494,26 +629,42 @@ func_set_code(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
if (nclosure != nfree) {
PyErr_Format(PyExc_ValueError,
"%U() requires a code object with %zd free vars,"
- " not %zd",
+ " not %d",
op->func_name,
nclosure, nfree);
return -1;
}
+
+ PyObject *func_code = PyFunction_GET_CODE(op);
+ int old_flags = ((PyCodeObject *)func_code)->co_flags;
+ int new_flags = ((PyCodeObject *)value)->co_flags;
+ int mask = CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR;
+ if ((old_flags & mask) != (new_flags & mask)) {
+ if (PyErr_Warn(PyExc_DeprecationWarning,
+ "Assigning a code object of non-matching type is deprecated "
+ "(e.g., from a generator to a plain function)") < 0)
+ {
+ return -1;
+ }
+ }
+
handle_func_event(PyFunction_EVENT_MODIFY_CODE, op, value);
- op->func_version = 0;
+ _PyFunction_SetVersion(op, 0);
Py_XSETREF(op->func_code, Py_NewRef(value));
return 0;
}
static PyObject *
-func_get_name(PyFunctionObject *op, void *Py_UNUSED(ignored))
+func_get_name(PyObject *self, void *Py_UNUSED(ignored))
{
+ PyFunctionObject* op = _PyFunction_CAST(self);
return Py_NewRef(op->func_name);
}
static int
-func_set_name(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
+func_set_name(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
{
+ PyFunctionObject * op = _PyFunction_CAST(self);
/* Not legal to del f.func_name or to set it to anything
* other than a string object. */
if (value == NULL || !PyUnicode_Check(value)) {
@@ -526,14 +677,16 @@ func_set_name(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
}
static PyObject *
-func_get_qualname(PyFunctionObject *op, void *Py_UNUSED(ignored))
+func_get_qualname(PyObject *self, void *Py_UNUSED(ignored))
{
+ PyFunctionObject *op = _PyFunction_CAST(self);
return Py_NewRef(op->func_qualname);
}
static int
-func_set_qualname(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
+func_set_qualname(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
{
+ PyFunctionObject *op = _PyFunction_CAST(self);
/* Not legal to del f.__qualname__ or to set it to anything
* other than a string object. */
if (value == NULL || !PyUnicode_Check(value)) {
@@ -546,8 +699,9 @@ func_set_qualname(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored
}
static PyObject *
-func_get_defaults(PyFunctionObject *op, void *Py_UNUSED(ignored))
+func_get_defaults(PyObject *self, void *Py_UNUSED(ignored))
{
+ PyFunctionObject * op = _PyFunction_CAST(self);
if (PySys_Audit("object.__getattr__", "Os", op, "__defaults__") < 0) {
return NULL;
}
@@ -558,8 +712,9 @@ func_get_defaults(PyFunctionObject *op, void *Py_UNUSED(ignored))
}
static int
-func_set_defaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
+func_set_defaults(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
{
+ PyFunctionObject *op = _PyFunction_CAST(self);
/* Legal to del f.func_defaults.
* Can only set func_defaults to NULL or a tuple. */
if (value == Py_None)
@@ -580,14 +735,15 @@ func_set_defaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored
}
handle_func_event(PyFunction_EVENT_MODIFY_DEFAULTS, op, value);
- op->func_version = 0;
+ _PyFunction_SetVersion(op, 0);
Py_XSETREF(op->func_defaults, Py_XNewRef(value));
return 0;
}
static PyObject *
-func_get_kwdefaults(PyFunctionObject *op, void *Py_UNUSED(ignored))
+func_get_kwdefaults(PyObject *self, void *Py_UNUSED(ignored))
{
+ PyFunctionObject * op = _PyFunction_CAST(self);
if (PySys_Audit("object.__getattr__", "Os",
op, "__kwdefaults__") < 0) {
return NULL;
@@ -599,8 +755,9 @@ func_get_kwdefaults(PyFunctionObject *op, void *Py_UNUSED(ignored))
}
static int
-func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
+func_set_kwdefaults(PyObject *self, PyObject *value, void *Py_UNUSED(ignored))
{
+ PyFunctionObject* op = _PyFunction_CAST(self);
if (value == Py_None)
value = NULL;
/* Legal to del f.func_kwdefaults.
@@ -621,25 +778,43 @@ func_set_kwdefaults(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignor
}
handle_func_event(PyFunction_EVENT_MODIFY_KWDEFAULTS, op, value);
- op->func_version = 0;
+ _PyFunction_SetVersion(op, 0);
Py_XSETREF(op->func_kwdefaults, Py_XNewRef(value));
return 0;
}
+
+/*[clinic input]
+@critical_section
+@getter
+function.__annotations__
+
+Dict of annotations in a function object.
+[clinic start generated code]*/
+
static PyObject *
-func_get_annotations(PyFunctionObject *op, void *Py_UNUSED(ignored))
+function___annotations___get_impl(PyFunctionObject *self)
+/*[clinic end generated code: output=a4cf4c884c934cbb input=92643d7186c1ad0c]*/
{
- if (op->func_annotations == NULL) {
- op->func_annotations = PyDict_New();
- if (op->func_annotations == NULL)
+ PyObject *d = NULL;
+ if (self->func_annotations == NULL) {
+ self->func_annotations = PyDict_New();
+ if (self->func_annotations == NULL)
return NULL;
}
- PyObject *d = func_get_annotation_dict(op);
+ d = func_get_annotation_dict(self);
return Py_XNewRef(d);
}
+/*[clinic input]
+@critical_section
+@setter
+function.__annotations__
+[clinic start generated code]*/
+
static int
-func_set_annotations(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
+function___annotations___set_impl(PyFunctionObject *self, PyObject *value)
+/*[clinic end generated code: output=a61795d4a95eede4 input=5302641f686f0463]*/
{
if (value == Py_None)
value = NULL;
@@ -651,24 +826,39 @@ func_set_annotations(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(igno
"__annotations__ must be set to a dict object");
return -1;
}
- op->func_version = 0;
- Py_XSETREF(op->func_annotations, Py_XNewRef(value));
+ Py_XSETREF(self->func_annotations, Py_XNewRef(value));
return 0;
}
+/*[clinic input]
+@critical_section
+@getter
+function.__type_params__
+
+Get the declared type parameters for a function.
+[clinic start generated code]*/
+
static PyObject *
-func_get_type_params(PyFunctionObject *op, void *Py_UNUSED(ignored))
+function___type_params___get_impl(PyFunctionObject *self)
+/*[clinic end generated code: output=eb844d7ffca517a8 input=0864721484293724]*/
{
- if (op->func_typeparams == NULL) {
+ if (self->func_typeparams == NULL) {
return PyTuple_New(0);
}
- assert(PyTuple_Check(op->func_typeparams));
- return Py_NewRef(op->func_typeparams);
+ assert(PyTuple_Check(self->func_typeparams));
+ return Py_NewRef(self->func_typeparams);
}
+/*[clinic input]
+@critical_section
+@setter
+function.__type_params__
+[clinic start generated code]*/
+
static int
-func_set_type_params(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(ignored))
+function___type_params___set_impl(PyFunctionObject *self, PyObject *value)
+/*[clinic end generated code: output=038b4cda220e56fb input=3862fbd4db2b70e8]*/
{
/* Not legal to del f.__type_params__ or to set it to anything
* other than a tuple object. */
@@ -677,7 +867,7 @@ func_set_type_params(PyFunctionObject *op, PyObject *value, void *Py_UNUSED(igno
"__type_params__ must be set to a tuple");
return -1;
}
- Py_XSETREF(op->func_typeparams, Py_NewRef(value));
+ Py_XSETREF(self->func_typeparams, Py_NewRef(value));
return 0;
}
@@ -693,28 +883,17 @@ _Py_set_function_type_params(PyThreadState *Py_UNUSED(ignored), PyObject *func,
}
static PyGetSetDef func_getsetlist[] = {
- {"__code__", (getter)func_get_code, (setter)func_set_code},
- {"__defaults__", (getter)func_get_defaults,
- (setter)func_set_defaults},
- {"__kwdefaults__", (getter)func_get_kwdefaults,
- (setter)func_set_kwdefaults},
- {"__annotations__", (getter)func_get_annotations,
- (setter)func_set_annotations},
+ {"__code__", func_get_code, func_set_code},
+ {"__defaults__", func_get_defaults, func_set_defaults},
+ {"__kwdefaults__", func_get_kwdefaults, func_set_kwdefaults},
+ FUNCTION___ANNOTATIONS___GETSETDEF
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
- {"__name__", (getter)func_get_name, (setter)func_set_name},
- {"__qualname__", (getter)func_get_qualname, (setter)func_set_qualname},
- {"__type_params__", (getter)func_get_type_params,
- (setter)func_set_type_params},
+ {"__name__", func_get_name, func_set_name},
+ {"__qualname__", func_get_qualname, func_set_qualname},
+ FUNCTION___TYPE_PARAMS___GETSETDEF
{NULL} /* Sentinel */
};
-/*[clinic input]
-class function "PyFunctionObject *" "&PyFunction_Type"
-[clinic start generated code]*/
-/*[clinic end generated code: output=da39a3ee5e6b4b0d input=70af9c90aa2e71b0]*/
-
-#include "clinic/funcobject.c.h"
-
/* function.__new__() maintains the following invariants for closures.
The closure must correspond to the free variables of the code object.
@@ -738,14 +917,17 @@ function.__new__ as func_new
a tuple that specifies the default argument values
closure: object = None
a tuple that supplies the bindings for free variables
+ kwdefaults: object = None
+ a dictionary that specifies the default keyword argument values
Create a function object.
[clinic start generated code]*/
static PyObject *
func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
- PyObject *name, PyObject *defaults, PyObject *closure)
-/*[clinic end generated code: output=99c6d9da3a24e3be input=93611752fc2daf11]*/
+ PyObject *name, PyObject *defaults, PyObject *closure,
+ PyObject *kwdefaults)
+/*[clinic end generated code: output=de72f4c22ac57144 input=20c9c9f04ad2d3f2]*/
{
PyFunctionObject *newfunc;
Py_ssize_t nclosure;
@@ -772,12 +954,17 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
return NULL;
}
}
+ if (kwdefaults != Py_None && !PyDict_Check(kwdefaults)) {
+ PyErr_SetString(PyExc_TypeError,
+ "arg 6 (kwdefaults) must be None or dict");
+ return NULL;
+ }
/* check that the closure is well-formed */
nclosure = closure == Py_None ? 0 : PyTuple_GET_SIZE(closure);
if (code->co_nfreevars != nclosure)
return PyErr_Format(PyExc_ValueError,
- "%U requires closure of length %zd, not %zd",
+ "%U requires closure of length %d, not %zd",
code->co_name, code->co_nfreevars, nclosure);
if (nclosure) {
Py_ssize_t i;
@@ -808,6 +995,9 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
if (closure != Py_None) {
newfunc->func_closure = Py_NewRef(closure);
}
+ if (kwdefaults != Py_None) {
+ newfunc->func_kwdefaults = Py_NewRef(kwdefaults);
+ }
return (PyObject *)newfunc;
}
@@ -815,7 +1005,7 @@ func_new_impl(PyTypeObject *type, PyCodeObject *code, PyObject *globals,
static int
func_clear(PyFunctionObject *op)
{
- op->func_version = 0;
+ _PyFunction_SetVersion(op, 0);
Py_CLEAR(op->func_globals);
Py_CLEAR(op->func_builtins);
Py_CLEAR(op->func_module);
@@ -831,26 +1021,22 @@ func_clear(PyFunctionObject *op)
// However, name and qualname could be str subclasses, so they
// could have reference cycles. The solution is to replace them
// with a genuinely immutable string.
- Py_SETREF(op->func_name, Py_NewRef(&_Py_STR(empty)));
- Py_SETREF(op->func_qualname, Py_NewRef(&_Py_STR(empty)));
+ Py_SETREF(op->func_name, &_Py_STR(empty));
+ Py_SETREF(op->func_qualname, &_Py_STR(empty));
return 0;
}
static void
func_dealloc(PyFunctionObject *op)
{
- assert(Py_REFCNT(op) == 0);
- Py_SET_REFCNT(op, 1);
+ _PyObject_ResurrectStart((PyObject *)op);
handle_func_event(PyFunction_EVENT_DESTROY, op, NULL);
- if (Py_REFCNT(op) > 1) {
- Py_SET_REFCNT(op, Py_REFCNT(op) - 1);
+ if (_PyObject_ResurrectEnd((PyObject *)op)) {
return;
}
- Py_SET_REFCNT(op, 0);
_PyObject_GC_UNTRACK(op);
- if (op->func_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *) op);
- }
+ FT_CLEAR_WEAKREFS((PyObject *) op, op->func_weakreflist);
+ _PyFunction_SetVersion(op, 0);
(void)func_clear(op);
// These aren't cleared by func_clear().
Py_DECREF(op->func_code);
@@ -942,17 +1128,12 @@ PyTypeObject PyFunction_Type = {
static int
functools_copy_attr(PyObject *wrapper, PyObject *wrapped, PyObject *name)
{
- PyObject *value = PyObject_GetAttr(wrapped, name);
- if (value == NULL) {
- if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
- PyErr_Clear();
- return 0;
- }
- return -1;
+ PyObject *value;
+ int res = PyObject_GetOptionalAttr(wrapped, name, &value);
+ if (value != NULL) {
+ res = PyObject_SetAttr(wrapper, name, value);
+ Py_DECREF(value);
}
-
- int res = PyObject_SetAttr(wrapper, name, value);
- Py_DECREF(value);
return res;
}
@@ -1042,13 +1223,21 @@ cm_descr_get(PyObject *self, PyObject *obj, PyObject *type)
}
if (type == NULL)
type = (PyObject *)(Py_TYPE(obj));
- if (Py_TYPE(cm->cm_callable)->tp_descr_get != NULL) {
- return Py_TYPE(cm->cm_callable)->tp_descr_get(cm->cm_callable, type,
- type);
- }
return PyMethod_New(cm->cm_callable, type);
}
+static PyObject *
+cm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ classmethod *cm = (classmethod *)PyType_GenericAlloc(type, 0);
+ if (cm == NULL) {
+ return NULL;
+ }
+ cm->cm_callable = Py_None;
+ cm->cm_dict = NULL;
+ return (PyObject *)cm;
+}
+
static int
cm_init(PyObject *self, PyObject *args, PyObject *kwds)
{
@@ -1068,8 +1257,8 @@ cm_init(PyObject *self, PyObject *args, PyObject *kwds)
}
static PyMemberDef cm_memberlist[] = {
- {"__func__", T_OBJECT, offsetof(classmethod, cm_callable), READONLY},
- {"__wrapped__", T_OBJECT, offsetof(classmethod, cm_callable), READONLY},
+ {"__func__", _Py_T_OBJECT, offsetof(classmethod, cm_callable), Py_READONLY},
+ {"__wrapped__", _Py_T_OBJECT, offsetof(classmethod, cm_callable), Py_READONLY},
{NULL} /* Sentinel */
};
@@ -1100,7 +1289,8 @@ cm_repr(classmethod *cm)
}
PyDoc_STRVAR(classmethod_doc,
-"classmethod(function) -> method\n\
+"classmethod(function, /)\n\
+--\n\
\n\
Convert a function to be a class method.\n\
\n\
@@ -1159,7 +1349,7 @@ PyTypeObject PyClassMethod_Type = {
offsetof(classmethod, cm_dict), /* tp_dictoffset */
cm_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
- PyType_GenericNew, /* tp_new */
+ cm_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
};
@@ -1237,6 +1427,18 @@ sm_descr_get(PyObject *self, PyObject *obj, PyObject *type)
return Py_NewRef(sm->sm_callable);
}
+static PyObject *
+sm_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+{
+ staticmethod *sm = (staticmethod *)PyType_GenericAlloc(type, 0);
+ if (sm == NULL) {
+ return NULL;
+ }
+ sm->sm_callable = Py_None;
+ sm->sm_dict = NULL;
+ return (PyObject *)sm;
+}
+
static int
sm_init(PyObject *self, PyObject *args, PyObject *kwds)
{
@@ -1263,8 +1465,8 @@ sm_call(PyObject *callable, PyObject *args, PyObject *kwargs)
}
static PyMemberDef sm_memberlist[] = {
- {"__func__", T_OBJECT, offsetof(staticmethod, sm_callable), READONLY},
- {"__wrapped__", T_OBJECT, offsetof(staticmethod, sm_callable), READONLY},
+ {"__func__", _Py_T_OBJECT, offsetof(staticmethod, sm_callable), Py_READONLY},
+ {"__wrapped__", _Py_T_OBJECT, offsetof(staticmethod, sm_callable), Py_READONLY},
{NULL} /* Sentinel */
};
@@ -1295,7 +1497,8 @@ sm_repr(staticmethod *sm)
}
PyDoc_STRVAR(staticmethod_doc,
-"staticmethod(function) -> method\n\
+"staticmethod(function, /)\n\
+--\n\
\n\
Convert a function to be a static method.\n\
\n\
@@ -1352,7 +1555,7 @@ PyTypeObject PyStaticMethod_Type = {
offsetof(staticmethod, sm_dict), /* tp_dictoffset */
sm_init, /* tp_init */
PyType_GenericAlloc, /* tp_alloc */
- PyType_GenericNew, /* tp_new */
+ sm_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
};
diff --git a/contrib/tools/python3/Objects/genericaliasobject.c b/contrib/tools/python3/Objects/genericaliasobject.c
index 7f89e68340b..8e59b3884cf 100644
--- a/contrib/tools/python3/Objects/genericaliasobject.c
+++ b/contrib/tools/python3/Objects/genericaliasobject.c
@@ -1,9 +1,12 @@
// types.GenericAlias -- used to represent e.g. list[int].
#include "Python.h"
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
+#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_object.h"
#include "pycore_unionobject.h" // _Py_union_type_or, _PyGenericAlias_Check
-#include "structmember.h" // PyMemberDef
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
+
#include <stdbool.h>
@@ -29,9 +32,7 @@ ga_dealloc(PyObject *self)
gaobject *alias = (gaobject *)self;
_PyObject_GC_UNTRACK(self);
- if (alias->weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject *)alias);
- }
+ FT_CLEAR_WEAKREFS((PyObject *)alias, alias->weakreflist);
Py_XDECREF(alias->origin);
Py_XDECREF(alias->args);
Py_XDECREF(alias->parameters);
@@ -54,8 +55,7 @@ ga_repr_item(_PyUnicodeWriter *writer, PyObject *p)
PyObject *qualname = NULL;
PyObject *module = NULL;
PyObject *r = NULL;
- PyObject *tmp;
- int err;
+ int rc;
if (p == Py_Ellipsis) {
// The Ellipsis object
@@ -63,28 +63,23 @@ ga_repr_item(_PyUnicodeWriter *writer, PyObject *p)
goto done;
}
- if (_PyObject_LookupAttr(p, &_Py_ID(__origin__), &tmp) < 0) {
- goto done;
+ if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
+ (rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
+ {
+ // It looks like a GenericAlias
+ goto use_repr;
}
- if (tmp != NULL) {
- Py_DECREF(tmp);
- if (_PyObject_LookupAttr(p, &_Py_ID(__args__), &tmp) < 0) {
- goto done;
- }
- if (tmp != NULL) {
- Py_DECREF(tmp);
- // It looks like a GenericAlias
- goto use_repr;
- }
+ if (rc < 0) {
+ goto done;
}
- if (_PyObject_LookupAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
+ if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
goto done;
}
if (qualname == NULL) {
goto use_repr;
}
- if (_PyObject_LookupAttr(p, &_Py_ID(__module__), &module) < 0) {
+ if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
goto done;
}
if (module == NULL || module == Py_None) {
@@ -112,13 +107,13 @@ done:
Py_XDECREF(module);
if (r == NULL) {
// error if any of the above PyObject_Repr/PyUnicode_From* fail
- err = -1;
+ rc = -1;
}
else {
- err = _PyUnicodeWriter_WriteStr(writer, r);
+ rc = _PyUnicodeWriter_WriteStr(writer, r);
Py_DECREF(r);
}
- return err;
+ return rc;
}
static int
@@ -138,10 +133,15 @@ ga_repr_items_list(_PyUnicodeWriter *writer, PyObject *p)
return -1;
}
}
- PyObject *item = PyList_GET_ITEM(p, i);
+ PyObject *item = PyList_GetItemRef(p, i);
+ if (item == NULL) {
+ return -1; // list can be mutated in a callback
+ }
if (ga_repr_item(writer, item) < 0) {
+ Py_DECREF(item);
return -1;
}
+ Py_DECREF(item);
}
if (_PyUnicodeWriter_WriteASCIIString(writer, "]", 1) < 0) {
@@ -252,22 +252,21 @@ _Py_make_parameters(PyObject *args)
Py_ssize_t iparam = 0;
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
PyObject *t = PyTuple_GET_ITEM(args, iarg);
- PyObject *subst;
// We don't want __parameters__ descriptor of a bare Python class.
if (PyType_Check(t)) {
continue;
}
- if (_PyObject_LookupAttr(t, &_Py_ID(__typing_subst__), &subst) < 0) {
+ int rc = PyObject_HasAttrWithError(t, &_Py_ID(__typing_subst__));
+ if (rc < 0) {
Py_DECREF(parameters);
return NULL;
}
- if (subst) {
+ if (rc) {
iparam += tuple_add(parameters, iparam, t);
- Py_DECREF(subst);
}
else {
PyObject *subparams;
- if (_PyObject_LookupAttr(t, &_Py_ID(__parameters__),
+ if (PyObject_GetOptionalAttr(t, &_Py_ID(__parameters__),
&subparams) < 0) {
Py_DECREF(parameters);
return NULL;
@@ -310,7 +309,7 @@ subs_tvars(PyObject *obj, PyObject *params,
PyObject **argitems, Py_ssize_t nargs)
{
PyObject *subparams;
- if (_PyObject_LookupAttr(obj, &_Py_ID(__parameters__), &subparams) < 0) {
+ if (PyObject_GetOptionalAttr(obj, &_Py_ID(__parameters__), &subparams) < 0) {
return NULL;
}
if (subparams && PyTuple_Check(subparams) && PyTuple_GET_SIZE(subparams)) {
@@ -361,7 +360,7 @@ _is_unpacked_typevartuple(PyObject *arg)
if (PyType_Check(arg)) { // TODO: Add test
return 0;
}
- int res = _PyObject_LookupAttr(arg, &_Py_ID(__typing_is_unpacked_typevartuple__), &tmp);
+ int res = PyObject_GetOptionalAttr(arg, &_Py_ID(__typing_is_unpacked_typevartuple__), &tmp);
if (res > 0) {
res = PyObject_IsTrue(tmp);
Py_DECREF(tmp);
@@ -383,7 +382,7 @@ _unpacked_tuple_args(PyObject *arg)
return Py_NewRef(result);
}
- if (_PyObject_LookupAttr(arg, &_Py_ID(__typing_unpacked_tuple_args__), &result) > 0) {
+ if (PyObject_GetOptionalAttr(arg, &_Py_ID(__typing_unpacked_tuple_args__), &result) > 0) {
if (result == Py_None) {
Py_DECREF(result);
return NULL;
@@ -448,7 +447,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
for (Py_ssize_t i = 0; i < nparams; i++) {
PyObject *param = PyTuple_GET_ITEM(parameters, i);
PyObject *prepare, *tmp;
- if (_PyObject_LookupAttr(param, &_Py_ID(__typing_prepare_subst__), &prepare) < 0) {
+ if (PyObject_GetOptionalAttr(param, &_Py_ID(__typing_prepare_subst__), &prepare) < 0) {
Py_DECREF(item);
return NULL;
}
@@ -503,7 +502,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
return NULL;
}
PyObject *subst;
- if (_PyObject_LookupAttr(arg, &_Py_ID(__typing_subst__), &subst) < 0) {
+ if (PyObject_GetOptionalAttr(arg, &_Py_ID(__typing_subst__), &subst) < 0) {
Py_DECREF(newargs);
Py_DECREF(item);
return NULL;
@@ -523,11 +522,22 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
return NULL;
}
if (unpack) {
+ if (!PyTuple_Check(arg)) {
+ Py_DECREF(newargs);
+ Py_DECREF(item);
+ PyObject *original = PyTuple_GET_ITEM(args, iarg);
+ PyErr_Format(PyExc_TypeError,
+ "expected __typing_subst__ of %T objects to return a tuple, not %T",
+ original, arg);
+ Py_DECREF(arg);
+ return NULL;
+ }
jarg = tuple_extend(&newargs, jarg,
&PyTuple_GET_ITEM(arg, 0), PyTuple_GET_SIZE(arg));
Py_DECREF(arg);
if (jarg < 0) {
Py_DECREF(item);
+ assert(newargs == NULL);
return NULL;
}
}
@@ -542,6 +552,8 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
}
PyDoc_STRVAR(genericalias__doc__,
+"GenericAlias(origin, args, /)\n"
+"--\n\n"
"Represent a PEP 585 generic type\n"
"\n"
"E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,).");
@@ -638,6 +650,11 @@ static const char* const attr_exceptions[] = {
"__mro_entries__",
"__reduce_ex__", // needed so we don't look up object.__reduce_ex__
"__reduce__",
+ NULL,
+};
+
+static const char* const attr_blocked[] = {
+ "__bases__",
"__copy__",
"__deepcopy__",
NULL,
@@ -648,15 +665,29 @@ ga_getattro(PyObject *self, PyObject *name)
{
gaobject *alias = (gaobject *)self;
if (PyUnicode_Check(name)) {
+ // When we check blocked attrs, we don't allow to proxy them to `__origin__`.
+ // Otherwise, we can break existing code.
+ for (const char * const *p = attr_blocked; ; p++) {
+ if (*p == NULL) {
+ break;
+ }
+ if (_PyUnicode_EqualToASCIIString(name, *p)) {
+ goto generic_getattr;
+ }
+ }
+
+ // When we see own attrs, it has a priority over `__origin__`'s attr.
for (const char * const *p = attr_exceptions; ; p++) {
if (*p == NULL) {
return PyObject_GetAttr(alias->origin, name);
}
if (_PyUnicode_EqualToASCIIString(name, *p)) {
- break;
+ goto generic_getattr;
}
}
}
+
+generic_getattr:
return PyObject_GenericGetAttr(self, name);
}
@@ -786,9 +817,9 @@ static PyMethodDef ga_methods[] = {
};
static PyMemberDef ga_members[] = {
- {"__origin__", T_OBJECT, offsetof(gaobject, origin), READONLY},
- {"__args__", T_OBJECT, offsetof(gaobject, args), READONLY},
- {"__unpacked__", T_BOOL, offsetof(gaobject, starred), READONLY},
+ {"__origin__", _Py_T_OBJECT, offsetof(gaobject, origin), Py_READONLY},
+ {"__args__", _Py_T_OBJECT, offsetof(gaobject, args), Py_READONLY},
+ {"__unpacked__", Py_T_BOOL, offsetof(gaobject, starred), Py_READONLY},
{0}
};
@@ -816,7 +847,7 @@ ga_unpacked_tuple_args(PyObject *self, void *unused)
}
static PyGetSetDef ga_properties[] = {
- {"__parameters__", ga_parameters, (setter)NULL, "Type variables in the GenericAlias.", NULL},
+ {"__parameters__", ga_parameters, (setter)NULL, PyDoc_STR("Type variables in the GenericAlias."), NULL},
{"__typing_unpacked_tuple_args__", ga_unpacked_tuple_args, (setter)NULL, NULL},
{0}
};
diff --git a/contrib/tools/python3/Objects/genobject.c b/contrib/tools/python3/Objects/genobject.c
index 640a7d906c8..b19ff252ddb 100644
--- a/contrib/tools/python3/Objects/genobject.c
+++ b/contrib/tools/python3/Objects/genobject.c
@@ -6,13 +6,16 @@
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _PyEval_EvalFrame()
#include "pycore_frame.h" // _PyInterpreterFrame
-#include "pycore_genobject.h" // struct _Py_async_gen_state
+#include "pycore_gc.h" // _PyGC_CLEAR_FINALIZED()
+#include "pycore_genobject.h" // struct _Py_async_gen_freelist
+#include "pycore_modsupport.h" // _PyArg_CheckPositional()
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
+#include "pycore_opcode_utils.h" // RESUME_AFTER_YIELD_FROM
+#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_*
#include "pycore_pyerrors.h" // _PyErr_ClearExcState()
#include "pycore_pystate.h" // _PyThreadState_GET()
-#include "structmember.h" // PyMemberDef
-#include "opcode.h" // SEND
-#include "frameobject.h" // _PyInterpreterFrame_GetLine
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
+
#include "pystats.h"
static PyObject *gen_close(PyGenObject *, PyObject *);
@@ -29,7 +32,7 @@ static const char *ASYNC_GEN_IGNORED_EXIT_MSG =
static inline PyCodeObject *
_PyGen_GetCode(PyGenObject *gen) {
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe);
- return frame->f_code;
+ return _PyFrame_GetCode(frame);
}
PyCodeObject *
@@ -40,19 +43,12 @@ PyGen_GetCode(PyGenObject *gen) {
return res;
}
-static inline int
-exc_state_traverse(_PyErr_StackItem *exc_state, visitproc visit, void *arg)
-{
- Py_VISIT(exc_state->exc_value);
- return 0;
-}
-
static int
gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
{
Py_VISIT(gen->gi_name);
Py_VISIT(gen->gi_qualname);
- if (gen->gi_frame_state < FRAME_CLEARED) {
+ if (gen->gi_frame_state != FRAME_CLEARED) {
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe);
assert(frame->frame_obj == NULL ||
frame->frame_obj->f_frame->owner == FRAME_OWNED_BY_GENERATOR);
@@ -63,7 +59,8 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
}
/* No need to visit cr_origin, because it's just tuples/str/int, so can't
participate in a reference cycle. */
- return exc_state_traverse(&gen->gi_exc_state, visit, arg);
+ Py_VISIT(gen->gi_exc_state.exc_value);
+ return 0;
}
void
@@ -71,7 +68,7 @@ _PyGen_Finalize(PyObject *self)
{
PyGenObject *gen = (PyGenObject *)self;
- if (gen->gi_frame_state >= FRAME_COMPLETED) {
+ if (FRAME_STATE_FINISHED(gen->gi_frame_state)) {
/* Generator isn't paused, so no need to close */
return;
}
@@ -123,14 +120,26 @@ _PyGen_Finalize(PyObject *self)
}
static void
+gen_clear_frame(PyGenObject *gen)
+{
+ if (gen->gi_frame_state == FRAME_CLEARED)
+ return;
+
+ gen->gi_frame_state = FRAME_CLEARED;
+ _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
+ frame->previous = NULL;
+ _PyFrame_ClearExceptCode(frame);
+ _PyErr_ClearExcState(&gen->gi_exc_state);
+}
+
+static void
gen_dealloc(PyGenObject *gen)
{
PyObject *self = (PyObject *) gen;
_PyObject_GC_UNTRACK(gen);
- if (gen->gi_weakreflist != NULL)
- PyObject_ClearWeakRefs(self);
+ FT_CLEAR_WEAKREFS(self, gen->gi_weakreflist);
_PyObject_GC_TRACK(self);
@@ -144,19 +153,15 @@ gen_dealloc(PyGenObject *gen)
and GC_Del. */
Py_CLEAR(((PyAsyncGenObject*)gen)->ag_origin_or_finalizer);
}
- if (gen->gi_frame_state < FRAME_CLEARED) {
- _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
- gen->gi_frame_state = FRAME_CLEARED;
- frame->previous = NULL;
- _PyFrame_ClearExceptCode(frame);
- }
+ gen_clear_frame(gen);
+ assert(gen->gi_exc_state.exc_value == NULL);
if (_PyGen_GetCode(gen)->co_flags & CO_COROUTINE) {
Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer);
}
Py_DECREF(_PyGen_GetCode(gen));
Py_CLEAR(gen->gi_name);
Py_CLEAR(gen->gi_qualname);
- _PyErr_ClearExcState(&gen->gi_exc_state);
+
PyObject_GC_Del(gen);
}
@@ -166,7 +171,6 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
{
PyThreadState *tstate = _PyThreadState_GET();
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
- PyObject *result;
*presult = NULL;
if (gen->gi_frame_state == FRAME_CREATED && arg && arg != Py_None) {
@@ -193,7 +197,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
PyErr_SetString(PyExc_ValueError, msg);
return PYGEN_ERROR;
}
- if (gen->gi_frame_state >= FRAME_COMPLETED) {
+ if (FRAME_STATE_FINISHED(gen->gi_frame_state)) {
if (PyCoro_CheckExact(gen) && !closing) {
/* `gen` is an exhausted coroutine: raise an error,
except when called from gen_close(), which should
@@ -211,10 +215,12 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
return PYGEN_ERROR;
}
- assert(gen->gi_frame_state < FRAME_EXECUTING);
+ assert((gen->gi_frame_state == FRAME_CREATED) ||
+ FRAME_STATE_SUSPENDED(gen->gi_frame_state));
+
/* Push arg onto the frame's value stack */
- result = arg ? arg : Py_None;
- _PyFrame_StackPush(frame, Py_NewRef(result));
+ PyObject *arg_obj = arg ? arg : Py_None;
+ _PyFrame_StackPush(frame, Py_NewRef(arg_obj));
_PyErr_StackItem *prev_exc_info = tstate->exc_info;
gen->gi_exc_state.previous_item = prev_exc_info;
@@ -222,12 +228,12 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
if (exc) {
assert(_PyErr_Occurred(tstate));
- _PyErr_ChainStackItem(NULL);
+ _PyErr_ChainStackItem();
}
gen->gi_frame_state = FRAME_EXECUTING;
EVAL_CALL_STAT_INC(EVAL_CALL_GENERATOR);
- result = _PyEval_EvalFrame(tstate, frame, exc);
+ PyObject *result = _PyEval_EvalFrame(tstate, frame, exc);
assert(tstate->exc_info == prev_exc_info);
assert(gen->gi_exc_state.previous_item == NULL);
assert(gen->gi_frame_state != FRAME_EXECUTING);
@@ -236,7 +242,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
/* If the generator just returned (as opposed to yielding), signal
* that the generator is exhausted. */
if (result) {
- if (gen->gi_frame_state == FRAME_SUSPENDED) {
+ if (FRAME_STATE_SUSPENDED(gen->gi_frame_state)) {
*presult = result;
return PYGEN_NEXT;
}
@@ -252,10 +258,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
!PyErr_ExceptionMatches(PyExc_StopAsyncIteration));
}
- /* generator can't be rerun, so release the frame */
- /* first clean reference cycle through stored exception traceback */
- _PyErr_ClearExcState(&gen->gi_exc_state);
-
+ assert(gen->gi_exc_state.exc_value == NULL);
assert(gen->gi_frame_state == FRAME_CLEARED);
*presult = result;
return result ? PYGEN_RETURN : PYGEN_ERROR;
@@ -288,7 +291,7 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, int exc, int closing)
}
PyDoc_STRVAR(send_doc,
-"send(arg) -> send 'arg' into generator,\n\
+"send(value) -> send 'value' into generator,\n\
return next yielded value or raise StopIteration.");
static PyObject *
@@ -317,7 +320,7 @@ gen_close_iter(PyObject *yf)
}
else {
PyObject *meth;
- if (_PyObject_LookupAttr(yf, &_Py_ID(close), &meth) < 0) {
+ if (PyObject_GetOptionalAttr(yf, &_Py_ID(close), &meth) < 0) {
PyErr_WriteUnraisable(yf);
}
if (meth) {
@@ -334,56 +337,42 @@ gen_close_iter(PyObject *yf)
static inline bool
is_resume(_Py_CODEUNIT *instr)
{
- return instr->op.code == RESUME || instr->op.code == INSTRUMENTED_RESUME;
-}
-
-static inline bool
-is_yield(_Py_CODEUNIT *instr)
-{
- return instr->op.code == YIELD_VALUE || instr->op.code == INSTRUMENTED_YIELD_VALUE;
+ uint8_t code = FT_ATOMIC_LOAD_UINT8_RELAXED(instr->op.code);
+ return (
+ code == RESUME ||
+ code == RESUME_CHECK ||
+ code == INSTRUMENTED_RESUME
+ );
}
PyObject *
_PyGen_yf(PyGenObject *gen)
{
- PyObject *yf = NULL;
-
- if (gen->gi_frame_state < FRAME_CLEARED) {
+ if (gen->gi_frame_state == FRAME_SUSPENDED_YIELD_FROM) {
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
-
- if (gen->gi_frame_state == FRAME_CREATED) {
- /* Return immediately if the frame didn't start yet. SEND
- always come after LOAD_CONST: a code object should not start
- with SEND */
- assert(_PyCode_CODE(_PyGen_GetCode(gen))[0].op.code != SEND);
- return NULL;
- }
- _Py_CODEUNIT next = frame->prev_instr[1];
- if (!is_resume(&next) || next.op.arg < 2)
- {
- /* Not in a yield from */
- return NULL;
- }
- yf = Py_NewRef(_PyFrame_StackPeek(frame));
+ assert(is_resume(frame->instr_ptr));
+ assert((frame->instr_ptr->op.arg & RESUME_OPARG_LOCATION_MASK) >= RESUME_AFTER_YIELD_FROM);
+ return Py_NewRef(_PyFrame_StackPeek(frame));
}
-
- return yf;
+ return NULL;
}
static PyObject *
gen_close(PyGenObject *gen, PyObject *args)
{
PyObject *retval;
- PyObject *yf = _PyGen_yf(gen);
int err = 0;
+
if (gen->gi_frame_state == FRAME_CREATED) {
gen->gi_frame_state = FRAME_COMPLETED;
+ gen_clear_frame(gen);
Py_RETURN_NONE;
}
- if (gen->gi_frame_state >= FRAME_COMPLETED) {
+ if (FRAME_STATE_FINISHED(gen->gi_frame_state)) {
Py_RETURN_NONE;
}
+ PyObject *yf = _PyGen_yf(gen);
if (yf) {
PyFrameState state = gen->gi_frame_state;
gen->gi_frame_state = FRAME_EXECUTING;
@@ -392,16 +381,17 @@ gen_close(PyGenObject *gen, PyObject *args)
Py_DECREF(yf);
}
_PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe;
- /* It is possible for the previous instruction to not be a
- * YIELD_VALUE if the debugger has changed the lineno. */
- if (err == 0 && is_yield(frame->prev_instr)) {
- assert(is_resume(frame->prev_instr + 1));
- int exception_handler_depth = frame->prev_instr[0].op.code;
- assert(exception_handler_depth > 0);
+ if (is_resume(frame->instr_ptr)) {
+ bool no_unwind_tools = _PyEval_NoToolsForUnwind(_PyThreadState_GET());
/* We can safely ignore the outermost try block
- * as it automatically generated to handle
+ * as it is automatically generated to handle
* StopIteration. */
- if (exception_handler_depth == 1) {
+ int oparg = frame->instr_ptr->op.arg;
+ if (oparg & RESUME_OPARG_DEPTH1_MASK && no_unwind_tools) {
+ // RESUME after YIELD_VALUE and exception depth is 1
+ assert((oparg & RESUME_OPARG_LOCATION_MASK) != RESUME_AT_FUNC_START);
+ gen->gi_frame_state = FRAME_COMPLETED;
+ gen_clear_frame(gen);
Py_RETURN_NONE;
}
}
@@ -420,11 +410,16 @@ gen_close(PyGenObject *gen, PyObject *args)
PyErr_SetString(PyExc_RuntimeError, msg);
return NULL;
}
- if (PyErr_ExceptionMatches(PyExc_StopIteration)
- || PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
- PyErr_Clear(); /* ignore these errors */
+ assert(PyErr_Occurred());
+ if (PyErr_ExceptionMatches(PyExc_GeneratorExit)) {
+ PyErr_Clear(); /* ignore this error */
Py_RETURN_NONE;
}
+ /* if the generator returned a value while closing, StopIteration was
+ * raised in gen_send_ex() above; retrieve and return the value here */
+ if (_PyGen_FetchStopIterationValue(&retval) == 0) {
+ return retval;
+ }
return NULL;
}
@@ -472,9 +467,9 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
will be reported correctly to the user. */
/* XXX We should probably be updating the current frame
somewhere in ceval.c. */
- _PyInterpreterFrame *prev = tstate->cframe->current_frame;
+ _PyInterpreterFrame *prev = tstate->current_frame;
frame->previous = prev;
- tstate->cframe->current_frame = frame;
+ tstate->current_frame = frame;
/* Close the generator that we are currently iterating with
'yield from' or awaiting on with 'await'. */
PyFrameState state = gen->gi_frame_state;
@@ -482,12 +477,12 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
typ, val, tb);
gen->gi_frame_state = state;
- tstate->cframe->current_frame = prev;
+ tstate->current_frame = prev;
frame->previous = NULL;
} else {
/* `yf` is an iterator or a coroutine-like object. */
PyObject *meth;
- if (_PyObject_LookupAttr(yf, &_Py_ID(throw), &meth) < 0) {
+ if (PyObject_GetOptionalAttr(yf, &_Py_ID(throw), &meth) < 0) {
Py_DECREF(yf);
return NULL;
}
@@ -730,7 +725,7 @@ gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored))
static PyObject *
gen_getsuspended(PyGenObject *gen, void *Py_UNUSED(ignored))
{
- return PyBool_FromLong(gen->gi_frame_state == FRAME_SUSPENDED);
+ return PyBool_FromLong(FRAME_STATE_SUSPENDED(gen->gi_frame_state));
}
static PyObject *
@@ -802,6 +797,7 @@ static PyMethodDef gen_methods[] = {
{"throw",_PyCFunction_CAST(gen_throw), METH_FASTCALL, throw_doc},
{"close",(PyCFunction)gen_close, METH_NOARGS, close_doc},
{"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__},
+ {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* Sentinel */
};
@@ -923,7 +919,7 @@ _Py_MakeCoro(PyFunctionObject *func)
if (origin_depth == 0) {
((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL;
} else {
- _PyInterpreterFrame *frame = tstate->cframe->current_frame;
+ _PyInterpreterFrame *frame = tstate->current_frame;
assert(frame);
assert(_PyFrame_IsIncomplete(frame));
frame = _PyFrame_GetFirstComplete(frame->previous);
@@ -941,7 +937,7 @@ static PyObject *
gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
PyObject *name, PyObject *qualname)
{
- PyCodeObject *code = f->f_frame->f_code;
+ PyCodeObject *code = _PyFrame_GetCode(f->f_frame);
int size = code->co_nlocalsplus + code->co_stacksize;
PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, size);
if (gen == NULL) {
@@ -1085,7 +1081,7 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored))
static PyObject *
cr_getsuspended(PyCoroObject *coro, void *Py_UNUSED(ignored))
{
- if (coro->cr_frame_state == FRAME_SUSPENDED) {
+ if (FRAME_STATE_SUSPENDED(coro->cr_frame_state)) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
@@ -1128,7 +1124,7 @@ static PyGetSetDef coro_getsetlist[] = {
};
static PyMemberDef coro_memberlist[] = {
- {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin_or_finalizer), READONLY},
+ {"cr_origin", _Py_T_OBJECT, offsetof(PyCoroObject, cr_origin_or_finalizer), Py_READONLY},
{NULL} /* Sentinel */
};
@@ -1154,6 +1150,7 @@ static PyMethodDef coro_methods[] = {
{"throw",_PyCFunction_CAST(gen_throw), METH_FASTCALL, coro_throw_doc},
{"close",(PyCFunction)gen_close, METH_NOARGS, coro_close_doc},
{"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__},
+ {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* Sentinel */
};
@@ -1323,7 +1320,7 @@ compute_cr_origin(int origin_depth, _PyInterpreterFrame *current_frame)
}
frame = current_frame;
for (int i = 0; i < frame_count; ++i) {
- PyCodeObject *code = frame->f_code;
+ PyCodeObject *code = _PyFrame_GetCode(frame);
int line = PyUnstable_InterpreterFrame_GetLine(frame);
PyObject *frameinfo = Py_BuildValue("OiO", code->co_filename, line,
code->co_name);
@@ -1522,7 +1519,7 @@ ag_getcode(PyGenObject *gen, void *Py_UNUSED(ignored))
static PyObject *
ag_getsuspended(PyAsyncGenObject *ag, void *Py_UNUSED(ignored))
{
- if (ag->ag_frame_state == FRAME_SUSPENDED) {
+ if (FRAME_STATE_SUSPENDED(ag->ag_frame_state)) {
Py_RETURN_TRUE;
}
Py_RETURN_FALSE;
@@ -1542,8 +1539,8 @@ static PyGetSetDef async_gen_getsetlist[] = {
};
static PyMemberDef async_gen_memberlist[] = {
- {"ag_running", T_BOOL, offsetof(PyAsyncGenObject, ag_running_async),
- READONLY},
+ {"ag_running", Py_T_BOOL, offsetof(PyAsyncGenObject, ag_running_async),
+ Py_READONLY},
{NULL} /* Sentinel */
};
@@ -1634,12 +1631,19 @@ PyTypeObject PyAsyncGen_Type = {
};
-#if _PyAsyncGen_MAXFREELIST > 0
-static struct _Py_async_gen_state *
-get_async_gen_state(void)
+#ifdef WITH_FREELISTS
+static struct _Py_async_gen_freelist *
+get_async_gen_freelist(void)
{
- PyInterpreterState *interp = _PyInterpreterState_GET();
- return &interp->async_gen;
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
+ return &freelists->async_gens;
+}
+
+static struct _Py_async_gen_asend_freelist *
+get_async_gen_asend_freelist(void)
+{
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
+ return &freelists->async_gen_asends;
}
#endif
@@ -1662,39 +1666,34 @@ PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
void
-_PyAsyncGen_ClearFreeLists(PyInterpreterState *interp)
+_PyAsyncGen_ClearFreeLists(struct _Py_object_freelists *freelist_state, int is_finalization)
{
-#if _PyAsyncGen_MAXFREELIST > 0
- struct _Py_async_gen_state *state = &interp->async_gen;
+#ifdef WITH_FREELISTS
+ struct _Py_async_gen_freelist *freelist = &freelist_state->async_gens;
- while (state->value_numfree) {
+ while (freelist->numfree > 0) {
_PyAsyncGenWrappedValue *o;
- o = state->value_freelist[--state->value_numfree];
+ o = freelist->items[--freelist->numfree];
assert(_PyAsyncGenWrappedValue_CheckExact(o));
PyObject_GC_Del(o);
}
- while (state->asend_numfree) {
+ struct _Py_async_gen_asend_freelist *asend_freelist = &freelist_state->async_gen_asends;
+
+ while (asend_freelist->numfree > 0) {
PyAsyncGenASend *o;
- o = state->asend_freelist[--state->asend_numfree];
+ o = asend_freelist->items[--asend_freelist->numfree];
assert(Py_IS_TYPE(o, &_PyAsyncGenASend_Type));
PyObject_GC_Del(o);
}
-#endif
-}
-void
-_PyAsyncGen_Fini(PyInterpreterState *interp)
-{
- _PyAsyncGen_ClearFreeLists(interp);
-#if defined(Py_DEBUG) && _PyAsyncGen_MAXFREELIST > 0
- struct _Py_async_gen_state *state = &interp->async_gen;
- state->value_numfree = -1;
- state->asend_numfree = -1;
+ if (is_finalization) {
+ freelist->numfree = -1;
+ asend_freelist->numfree = -1;
+ }
#endif
}
-
static PyObject *
async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result)
{
@@ -1731,18 +1730,19 @@ async_gen_unwrap_value(PyAsyncGenObject *gen, PyObject *result)
static void
async_gen_asend_dealloc(PyAsyncGenASend *o)
{
+ if (PyObject_CallFinalizerFromDealloc((PyObject *)o)) {
+ return;
+ }
+
_PyObject_GC_UNTRACK((PyObject *)o);
Py_CLEAR(o->ags_gen);
Py_CLEAR(o->ags_sendval);
-#if _PyAsyncGen_MAXFREELIST > 0
- struct _Py_async_gen_state *state = get_async_gen_state();
-#ifdef Py_DEBUG
- // async_gen_asend_dealloc() must not be called after _PyAsyncGen_Fini()
- assert(state->asend_numfree != -1);
-#endif
- if (state->asend_numfree < _PyAsyncGen_MAXFREELIST) {
+#ifdef WITH_FREELISTS
+ struct _Py_async_gen_asend_freelist *freelist = get_async_gen_asend_freelist();
+ if (freelist->numfree >= 0 && freelist->numfree < _PyAsyncGen_MAXFREELIST) {
assert(PyAsyncGenASend_CheckExact(o));
- state->asend_freelist[state->asend_numfree++] = o;
+ _PyGC_CLEAR_FINALIZED((PyObject *)o);
+ freelist->items[freelist->numfree++] = o;
}
else
#endif
@@ -1846,10 +1846,34 @@ async_gen_asend_throw(PyAsyncGenASend *o, PyObject *const *args, Py_ssize_t narg
static PyObject *
async_gen_asend_close(PyAsyncGenASend *o, PyObject *args)
{
- o->ags_state = AWAITABLE_STATE_CLOSED;
- Py_RETURN_NONE;
+ PyObject *result;
+ if (o->ags_state == AWAITABLE_STATE_CLOSED) {
+ Py_RETURN_NONE;
+ }
+ result = async_gen_asend_throw(o, &PyExc_GeneratorExit, 1);
+ if (result == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_StopIteration) ||
+ PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
+ PyErr_ExceptionMatches(PyExc_GeneratorExit))
+ {
+ PyErr_Clear();
+ Py_RETURN_NONE;
+ }
+ return result;
+ } else {
+ Py_DECREF(result);
+ PyErr_SetString(PyExc_RuntimeError, "coroutine ignored GeneratorExit");
+ return NULL;
+ }
}
+static void
+async_gen_asend_finalize(PyAsyncGenASend *o)
+{
+ if (o->ags_state == AWAITABLE_STATE_INIT) {
+ _PyErr_WarnUnawaitedAgenMethod(o->ags_gen, &_Py_ID(asend));
+ }
+}
static PyMethodDef async_gen_asend_methods[] = {
{"send", (PyCFunction)async_gen_asend_send, METH_O, send_doc},
@@ -1907,6 +1931,7 @@ PyTypeObject _PyAsyncGenASend_Type = {
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
+ .tp_finalize = (destructor)async_gen_asend_finalize,
};
@@ -1914,15 +1939,11 @@ static PyObject *
async_gen_asend_new(PyAsyncGenObject *gen, PyObject *sendval)
{
PyAsyncGenASend *o;
-#if _PyAsyncGen_MAXFREELIST > 0
- struct _Py_async_gen_state *state = get_async_gen_state();
-#ifdef Py_DEBUG
- // async_gen_asend_new() must not be called after _PyAsyncGen_Fini()
- assert(state->asend_numfree != -1);
-#endif
- if (state->asend_numfree) {
- state->asend_numfree--;
- o = state->asend_freelist[state->asend_numfree];
+#ifdef WITH_FREELISTS
+ struct _Py_async_gen_asend_freelist *freelist = get_async_gen_asend_freelist();
+ if (freelist->numfree > 0) {
+ freelist->numfree--;
+ o = freelist->items[freelist->numfree];
_Py_NewReference((PyObject *)o);
}
else
@@ -1953,15 +1974,11 @@ async_gen_wrapped_val_dealloc(_PyAsyncGenWrappedValue *o)
{
_PyObject_GC_UNTRACK((PyObject *)o);
Py_CLEAR(o->agw_val);
-#if _PyAsyncGen_MAXFREELIST > 0
- struct _Py_async_gen_state *state = get_async_gen_state();
-#ifdef Py_DEBUG
- // async_gen_wrapped_val_dealloc() must not be called after _PyAsyncGen_Fini()
- assert(state->value_numfree != -1);
-#endif
- if (state->value_numfree < _PyAsyncGen_MAXFREELIST) {
+#ifdef WITH_FREELISTS
+ struct _Py_async_gen_freelist *freelist = get_async_gen_freelist();
+ if (freelist->numfree >= 0 && freelist->numfree < _PyAsyncGen_MAXFREELIST) {
assert(_PyAsyncGenWrappedValue_CheckExact(o));
- state->value_freelist[state->value_numfree++] = o;
+ freelist->items[freelist->numfree++] = o;
OBJECT_STAT_INC(to_freelist);
}
else
@@ -2030,15 +2047,11 @@ _PyAsyncGenValueWrapperNew(PyThreadState *tstate, PyObject *val)
_PyAsyncGenWrappedValue *o;
assert(val);
-#if _PyAsyncGen_MAXFREELIST > 0
- struct _Py_async_gen_state *state = &tstate->interp->async_gen;
-#ifdef Py_DEBUG
- // _PyAsyncGenValueWrapperNew() must not be called after _PyAsyncGen_Fini()
- assert(state->value_numfree != -1);
-#endif
- if (state->value_numfree) {
- state->value_numfree--;
- o = state->value_freelist[state->value_numfree];
+#ifdef WITH_FREELISTS
+ struct _Py_async_gen_freelist *freelist = get_async_gen_freelist();
+ if (freelist->numfree > 0) {
+ freelist->numfree--;
+ o = freelist->items[freelist->numfree];
OBJECT_STAT_INC(from_freelist);
assert(_PyAsyncGenWrappedValue_CheckExact(o));
_Py_NewReference((PyObject*)o);
@@ -2064,6 +2077,10 @@ _PyAsyncGenValueWrapperNew(PyThreadState *tstate, PyObject *val)
static void
async_gen_athrow_dealloc(PyAsyncGenAThrow *o)
{
+ if (PyObject_CallFinalizerFromDealloc((PyObject *)o)) {
+ return;
+ }
+
_PyObject_GC_UNTRACK((PyObject *)o);
Py_CLEAR(o->agt_gen);
Py_CLEAR(o->agt_args);
@@ -2093,7 +2110,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
return NULL;
}
- if (gen->gi_frame_state >= FRAME_COMPLETED) {
+ if (FRAME_STATE_FINISHED(gen->gi_frame_state)) {
o->agt_state = AWAITABLE_STATE_CLOSED;
PyErr_SetNone(PyExc_StopIteration);
return NULL;
@@ -2291,11 +2308,37 @@ async_gen_athrow_iternext(PyAsyncGenAThrow *o)
static PyObject *
async_gen_athrow_close(PyAsyncGenAThrow *o, PyObject *args)
{
- o->agt_state = AWAITABLE_STATE_CLOSED;
- Py_RETURN_NONE;
+ PyObject *result;
+ if (o->agt_state == AWAITABLE_STATE_CLOSED) {
+ Py_RETURN_NONE;
+ }
+ result = async_gen_athrow_throw(o, &PyExc_GeneratorExit, 1);
+ if (result == NULL) {
+ if (PyErr_ExceptionMatches(PyExc_StopIteration) ||
+ PyErr_ExceptionMatches(PyExc_StopAsyncIteration) ||
+ PyErr_ExceptionMatches(PyExc_GeneratorExit))
+ {
+ PyErr_Clear();
+ Py_RETURN_NONE;
+ }
+ return result;
+ } else {
+ Py_DECREF(result);
+ PyErr_SetString(PyExc_RuntimeError, "coroutine ignored GeneratorExit");
+ return NULL;
+ }
}
+static void
+async_gen_athrow_finalize(PyAsyncGenAThrow *o)
+{
+ if (o->agt_state == AWAITABLE_STATE_INIT) {
+ PyObject *method = o->agt_args ? &_Py_ID(athrow) : &_Py_ID(aclose);
+ _PyErr_WarnUnawaitedAgenMethod(o->agt_gen, method);
+ }
+}
+
static PyMethodDef async_gen_athrow_methods[] = {
{"send", (PyCFunction)async_gen_athrow_send, METH_O, send_doc},
{"throw", _PyCFunction_CAST(async_gen_athrow_throw),
@@ -2353,6 +2396,7 @@ PyTypeObject _PyAsyncGenAThrow_Type = {
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
+ .tp_finalize = (destructor)async_gen_athrow_finalize,
};
diff --git a/contrib/tools/python3/Objects/interpreteridobject.c b/contrib/tools/python3/Objects/interpreteridobject.c
deleted file mode 100644
index 46239100dcb..00000000000
--- a/contrib/tools/python3/Objects/interpreteridobject.c
+++ /dev/null
@@ -1,294 +0,0 @@
-/* InterpreterID object */
-
-#include "Python.h"
-#include "pycore_abstract.h" // _PyIndex_Check()
-#include "pycore_interp.h" // _PyInterpreterState_LookUpID()
-#include "interpreteridobject.h"
-
-
-typedef struct interpid {
- PyObject_HEAD
- int64_t id;
-} interpid;
-
-static interpid *
-newinterpid(PyTypeObject *cls, int64_t id, int force)
-{
- PyInterpreterState *interp = _PyInterpreterState_LookUpID(id);
- if (interp == NULL) {
- if (force) {
- PyErr_Clear();
- }
- else {
- return NULL;
- }
- }
-
- if (interp != NULL) {
- if (_PyInterpreterState_IDIncref(interp) < 0) {
- return NULL;
- }
- }
-
- interpid *self = PyObject_New(interpid, cls);
- if (self == NULL) {
- if (interp != NULL) {
- _PyInterpreterState_IDDecref(interp);
- }
- return NULL;
- }
- self->id = id;
-
- return self;
-}
-
-static int
-interp_id_converter(PyObject *arg, void *ptr)
-{
- int64_t id;
- if (PyObject_TypeCheck(arg, &_PyInterpreterID_Type)) {
- id = ((interpid *)arg)->id;
- }
- else if (_PyIndex_Check(arg)) {
- id = PyLong_AsLongLong(arg);
- if (id == -1 && PyErr_Occurred()) {
- return 0;
- }
- if (id < 0) {
- PyErr_Format(PyExc_ValueError,
- "interpreter ID must be a non-negative int, got %R", arg);
- return 0;
- }
- }
- else {
- PyErr_Format(PyExc_TypeError,
- "interpreter ID must be an int, got %.100s",
- Py_TYPE(arg)->tp_name);
- return 0;
- }
- *(int64_t *)ptr = id;
- return 1;
-}
-
-static PyObject *
-interpid_new(PyTypeObject *cls, PyObject *args, PyObject *kwds)
-{
- static char *kwlist[] = {"id", "force", NULL};
- int64_t id;
- int force = 0;
- if (!PyArg_ParseTupleAndKeywords(args, kwds,
- "O&|$p:InterpreterID.__init__", kwlist,
- interp_id_converter, &id, &force)) {
- return NULL;
- }
-
- return (PyObject *)newinterpid(cls, id, force);
-}
-
-static void
-interpid_dealloc(PyObject *v)
-{
- int64_t id = ((interpid *)v)->id;
- PyInterpreterState *interp = _PyInterpreterState_LookUpID(id);
- if (interp != NULL) {
- _PyInterpreterState_IDDecref(interp);
- }
- else {
- // already deleted
- PyErr_Clear();
- }
- Py_TYPE(v)->tp_free(v);
-}
-
-static PyObject *
-interpid_repr(PyObject *self)
-{
- PyTypeObject *type = Py_TYPE(self);
- const char *name = _PyType_Name(type);
- interpid *id = (interpid *)self;
- return PyUnicode_FromFormat("%s(%" PRId64 ")", name, id->id);
-}
-
-static PyObject *
-interpid_str(PyObject *self)
-{
- interpid *id = (interpid *)self;
- return PyUnicode_FromFormat("%" PRId64 "", id->id);
-}
-
-static PyObject *
-interpid_int(PyObject *self)
-{
- interpid *id = (interpid *)self;
- return PyLong_FromLongLong(id->id);
-}
-
-static PyNumberMethods interpid_as_number = {
- 0, /* nb_add */
- 0, /* nb_subtract */
- 0, /* nb_multiply */
- 0, /* nb_remainder */
- 0, /* nb_divmod */
- 0, /* nb_power */
- 0, /* nb_negative */
- 0, /* nb_positive */
- 0, /* nb_absolute */
- 0, /* nb_bool */
- 0, /* nb_invert */
- 0, /* nb_lshift */
- 0, /* nb_rshift */
- 0, /* nb_and */
- 0, /* nb_xor */
- 0, /* nb_or */
- (unaryfunc)interpid_int, /* nb_int */
- 0, /* nb_reserved */
- 0, /* nb_float */
-
- 0, /* nb_inplace_add */
- 0, /* nb_inplace_subtract */
- 0, /* nb_inplace_multiply */
- 0, /* nb_inplace_remainder */
- 0, /* nb_inplace_power */
- 0, /* nb_inplace_lshift */
- 0, /* nb_inplace_rshift */
- 0, /* nb_inplace_and */
- 0, /* nb_inplace_xor */
- 0, /* nb_inplace_or */
-
- 0, /* nb_floor_divide */
- 0, /* nb_true_divide */
- 0, /* nb_inplace_floor_divide */
- 0, /* nb_inplace_true_divide */
-
- (unaryfunc)interpid_int, /* nb_index */
-};
-
-static Py_hash_t
-interpid_hash(PyObject *self)
-{
- interpid *id = (interpid *)self;
- PyObject *obj = PyLong_FromLongLong(id->id);
- if (obj == NULL) {
- return -1;
- }
- Py_hash_t hash = PyObject_Hash(obj);
- Py_DECREF(obj);
- return hash;
-}
-
-static PyObject *
-interpid_richcompare(PyObject *self, PyObject *other, int op)
-{
- if (op != Py_EQ && op != Py_NE) {
- Py_RETURN_NOTIMPLEMENTED;
- }
-
- if (!PyObject_TypeCheck(self, &_PyInterpreterID_Type)) {
- Py_RETURN_NOTIMPLEMENTED;
- }
-
- interpid *id = (interpid *)self;
- int equal;
- if (PyObject_TypeCheck(other, &_PyInterpreterID_Type)) {
- interpid *otherid = (interpid *)other;
- equal = (id->id == otherid->id);
- }
- else if (PyLong_CheckExact(other)) {
- /* Fast path */
- int overflow;
- long long otherid = PyLong_AsLongLongAndOverflow(other, &overflow);
- if (otherid == -1 && PyErr_Occurred()) {
- return NULL;
- }
- equal = !overflow && (otherid >= 0) && (id->id == otherid);
- }
- else if (PyNumber_Check(other)) {
- PyObject *pyid = PyLong_FromLongLong(id->id);
- if (pyid == NULL) {
- return NULL;
- }
- PyObject *res = PyObject_RichCompare(pyid, other, op);
- Py_DECREF(pyid);
- return res;
- }
- else {
- Py_RETURN_NOTIMPLEMENTED;
- }
-
- if ((op == Py_EQ && equal) || (op == Py_NE && !equal)) {
- Py_RETURN_TRUE;
- }
- Py_RETURN_FALSE;
-}
-
-PyDoc_STRVAR(interpid_doc,
-"A interpreter ID identifies a interpreter and may be used as an int.");
-
-PyTypeObject _PyInterpreterID_Type = {
- PyVarObject_HEAD_INIT(&PyType_Type, 0)
- "InterpreterID", /* tp_name */
- sizeof(interpid), /* tp_basicsize */
- 0, /* tp_itemsize */
- (destructor)interpid_dealloc, /* tp_dealloc */
- 0, /* tp_vectorcall_offset */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_as_async */
- (reprfunc)interpid_repr, /* tp_repr */
- &interpid_as_number, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- interpid_hash, /* tp_hash */
- 0, /* tp_call */
- (reprfunc)interpid_str, /* tp_str */
- 0, /* tp_getattro */
- 0, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
- interpid_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- interpid_richcompare, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- 0, /* tp_members */
- 0, /* tp_getset */
- 0, /* tp_base */
- 0, /* tp_dict */
- 0, /* tp_descr_get */
- 0, /* tp_descr_set */
- 0, /* tp_dictoffset */
- 0, /* tp_init */
- 0, /* tp_alloc */
- interpid_new, /* tp_new */
-};
-
-PyObject *_PyInterpreterID_New(int64_t id)
-{
- return (PyObject *)newinterpid(&_PyInterpreterID_Type, id, 0);
-}
-
-PyObject *
-_PyInterpreterState_GetIDObject(PyInterpreterState *interp)
-{
- if (_PyInterpreterState_IDInitref(interp) != 0) {
- return NULL;
- };
- int64_t id = PyInterpreterState_GetID(interp);
- if (id < 0) {
- return NULL;
- }
- return (PyObject *)newinterpid(&_PyInterpreterID_Type, id, 0);
-}
-
-PyInterpreterState *
-_PyInterpreterID_LookUp(PyObject *requested_id)
-{
- int64_t id;
- if (!interp_id_converter(requested_id, &id)) {
- return NULL;
- }
- return _PyInterpreterState_LookUpID(id);
-}
diff --git a/contrib/tools/python3/Objects/iterobject.c b/contrib/tools/python3/Objects/iterobject.c
index 66e4490766a..ebb342ff109 100644
--- a/contrib/tools/python3/Objects/iterobject.c
+++ b/contrib/tools/python3/Objects/iterobject.c
@@ -1,7 +1,9 @@
/* Iterator objects */
#include "Python.h"
+#include "pycore_abstract.h" // _PyObject_HasLen()
#include "pycore_call.h" // _PyObject_CallNoArgs()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_object.h" // _PyObject_GC_TRACK()
typedef struct {
diff --git a/contrib/tools/python3/Objects/listobject.c b/contrib/tools/python3/Objects/listobject.c
index d017f34b94f..f5acc5c1bef 100644
--- a/contrib/tools/python3/Objects/listobject.c
+++ b/contrib/tools/python3/Objects/listobject.c
@@ -2,11 +2,17 @@
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
+#include "pycore_critical_section.h" // _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED()
+#include "pycore_dict.h" // _PyDictViewObject
+#include "pycore_pyatomic_ft_wrappers.h"
#include "pycore_interp.h" // PyInterpreterState.list
-#include "pycore_list.h" // struct _Py_list_state, _PyListIterObject
+#include "pycore_list.h" // struct _Py_list_freelist, _PyListIterObject
#include "pycore_long.h" // _PyLong_DigitCount
-#include "pycore_object.h" // _PyObject_GC_TRACK()
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
+#include "pycore_object.h" // _PyObject_GC_TRACK(), _PyDebugAllocatorStats()
#include "pycore_tuple.h" // _PyTuple_FromArray()
+#include "pycore_setobject.h" // _PySet_NextEntry()
#include <stddef.h>
/*[clinic input]
@@ -18,15 +24,79 @@ class list "PyListObject *" "&PyList_Type"
_Py_DECLARE_STR(list_err, "list index out of range");
-#if PyList_MAXFREELIST > 0
-static struct _Py_list_state *
-get_list_state(void)
+#ifdef WITH_FREELISTS
+static struct _Py_list_freelist *
+get_list_freelist(void)
{
- PyInterpreterState *interp = _PyInterpreterState_GET();
- return &interp->list;
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
+ assert(freelists != NULL);
+ return &freelists->lists;
}
#endif
+#ifdef Py_GIL_DISABLED
+typedef struct {
+ Py_ssize_t allocated;
+ PyObject *ob_item[];
+} _PyListArray;
+
+static _PyListArray *
+list_allocate_array(size_t capacity)
+{
+ if (capacity > PY_SSIZE_T_MAX/sizeof(PyObject*) - 1) {
+ return NULL;
+ }
+ _PyListArray *array = PyMem_Malloc(sizeof(_PyListArray) + capacity * sizeof(PyObject *));
+ if (array == NULL) {
+ return NULL;
+ }
+ array->allocated = capacity;
+ return array;
+}
+
+static Py_ssize_t
+list_capacity(PyObject **items)
+{
+ _PyListArray *array = _Py_CONTAINER_OF(items, _PyListArray, ob_item);
+ return array->allocated;
+}
+#endif
+
+static void
+free_list_items(PyObject** items, bool use_qsbr)
+{
+#ifdef Py_GIL_DISABLED
+ _PyListArray *array = _Py_CONTAINER_OF(items, _PyListArray, ob_item);
+ if (use_qsbr) {
+ size_t size = sizeof(_PyListArray) + array->allocated * sizeof(PyObject *);
+ _PyMem_FreeDelayed(array, size);
+ }
+ else {
+ PyMem_Free(array);
+ }
+#else
+ PyMem_Free(items);
+#endif
+}
+
+static void
+ensure_shared_on_resize(PyListObject *self)
+{
+#ifdef Py_GIL_DISABLED
+ // We can't use _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED here because
+ // the `CALL_LIST_APPEND` bytecode handler may lock the list without
+ // a critical section.
+ assert(Py_REFCNT(self) == 1 || PyMutex_IsLocked(&_PyObject_CAST(self)->ob_mutex));
+
+ // Ensure that the list array is freed using QSBR if we are not the
+ // owning thread.
+ if (!_Py_IsOwnedByCurrentThread((PyObject *)self) &&
+ !_PyObject_GC_IS_SHARED(self))
+ {
+ _PyObject_GC_SET_SHARED(self);
+ }
+#endif
+}
/* Ensure ob_item has room for at least newsize elements, and set
* ob_size to newsize. If newsize > ob_size on entry, the content
@@ -44,8 +114,7 @@ get_list_state(void)
static int
list_resize(PyListObject *self, Py_ssize_t newsize)
{
- PyObject **items;
- size_t new_allocated, num_allocated_bytes;
+ size_t new_allocated, target_bytes;
Py_ssize_t allocated = self->allocated;
/* Bypass realloc() when a previous overallocation is large enough
@@ -77,9 +146,39 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
if (newsize == 0)
new_allocated = 0;
+
+ ensure_shared_on_resize(self);
+
+#ifdef Py_GIL_DISABLED
+ _PyListArray *array = list_allocate_array(new_allocated);
+ if (array == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ PyObject **old_items = self->ob_item;
+ if (self->ob_item) {
+ if (new_allocated < (size_t)allocated) {
+ target_bytes = new_allocated * sizeof(PyObject*);
+ }
+ else {
+ target_bytes = allocated * sizeof(PyObject*);
+ }
+ memcpy(array->ob_item, self->ob_item, target_bytes);
+ }
+ if (new_allocated > (size_t)allocated) {
+ memset(array->ob_item + allocated, 0, sizeof(PyObject *) * (new_allocated - allocated));
+ }
+ _Py_atomic_store_ptr_release(&self->ob_item, &array->ob_item);
+ self->allocated = new_allocated;
+ Py_SET_SIZE(self, newsize);
+ if (old_items != NULL) {
+ free_list_items(old_items, _PyObject_GC_IS_SHARED(self));
+ }
+#else
+ PyObject **items;
if (new_allocated <= (size_t)PY_SSIZE_T_MAX / sizeof(PyObject *)) {
- num_allocated_bytes = new_allocated * sizeof(PyObject *);
- items = (PyObject **)PyMem_Realloc(self->ob_item, num_allocated_bytes);
+ target_bytes = new_allocated * sizeof(PyObject *);
+ items = (PyObject **)PyMem_Realloc(self->ob_item, target_bytes);
}
else {
// integer overflow
@@ -92,12 +191,14 @@ list_resize(PyListObject *self, Py_ssize_t newsize)
self->ob_item = items;
Py_SET_SIZE(self, newsize);
self->allocated = new_allocated;
+#endif
return 0;
}
static int
list_preallocate_exact(PyListObject *self, Py_ssize_t size)
{
+ PyObject **items;
assert(self->ob_item == NULL);
assert(size > 0);
@@ -107,36 +208,39 @@ list_preallocate_exact(PyListObject *self, Py_ssize_t size)
* allocated size up to the nearest even number.
*/
size = (size + 1) & ~(size_t)1;
- PyObject **items = PyMem_New(PyObject*, size);
+#ifdef Py_GIL_DISABLED
+ _PyListArray *array = list_allocate_array(size);
+ if (array == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ items = array->ob_item;
+ memset(items, 0, size * sizeof(PyObject *));
+#else
+ items = PyMem_New(PyObject*, size);
if (items == NULL) {
PyErr_NoMemory();
return -1;
}
- self->ob_item = items;
+#endif
+ FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item, items);
self->allocated = size;
return 0;
}
void
-_PyList_ClearFreeList(PyInterpreterState *interp)
+_PyList_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization)
{
-#if PyList_MAXFREELIST > 0
- struct _Py_list_state *state = &interp->list;
- while (state->numfree) {
- PyListObject *op = state->free_list[--state->numfree];
+#ifdef WITH_FREELISTS
+ struct _Py_list_freelist *state = &freelists->lists;
+ while (state->numfree > 0) {
+ PyListObject *op = state->items[--state->numfree];
assert(PyList_CheckExact(op));
PyObject_GC_Del(op);
}
-#endif
-}
-
-void
-_PyList_Fini(PyInterpreterState *interp)
-{
- _PyList_ClearFreeList(interp);
-#if defined(Py_DEBUG) && PyList_MAXFREELIST > 0
- struct _Py_list_state *state = &interp->list;
- state->numfree = -1;
+ if (is_finalization) {
+ state->numfree = -1;
+ }
#endif
}
@@ -144,11 +248,11 @@ _PyList_Fini(PyInterpreterState *interp)
void
_PyList_DebugMallocStats(FILE *out)
{
-#if PyList_MAXFREELIST > 0
- struct _Py_list_state *state = get_list_state();
+#ifdef WITH_FREELISTS
+ struct _Py_list_freelist *list_freelist = get_list_freelist();
_PyDebugAllocatorStats(out,
"free PyListObject",
- state->numfree, sizeof(PyListObject));
+ list_freelist->numfree, sizeof(PyListObject));
#endif
}
@@ -162,15 +266,11 @@ PyList_New(Py_ssize_t size)
return NULL;
}
-#if PyList_MAXFREELIST > 0
- struct _Py_list_state *state = get_list_state();
-#ifdef Py_DEBUG
- // PyList_New() must not be called after _PyList_Fini()
- assert(state->numfree != -1);
-#endif
- if (PyList_MAXFREELIST && state->numfree) {
- state->numfree--;
- op = state->free_list[state->numfree];
+#ifdef WITH_FREELISTS
+ struct _Py_list_freelist *list_freelist = get_list_freelist();
+ if (PyList_MAXFREELIST && list_freelist->numfree > 0) {
+ list_freelist->numfree--;
+ op = list_freelist->items[list_freelist->numfree];
OBJECT_STAT_INC(from_freelist);
_Py_NewReference((PyObject *)op);
}
@@ -186,7 +286,17 @@ PyList_New(Py_ssize_t size)
op->ob_item = NULL;
}
else {
+#ifdef Py_GIL_DISABLED
+ _PyListArray *array = list_allocate_array(size);
+ if (array == NULL) {
+ Py_DECREF(op);
+ return PyErr_NoMemory();
+ }
+ memset(&array->ob_item, 0, size * sizeof(PyObject *));
+ op->ob_item = array->ob_item;
+#else
op->ob_item = (PyObject **) PyMem_Calloc(size, sizeof(PyObject *));
+#endif
if (op->ob_item == NULL) {
Py_DECREF(op);
return PyErr_NoMemory();
@@ -207,11 +317,20 @@ list_new_prealloc(Py_ssize_t size)
return NULL;
}
assert(op->ob_item == NULL);
+#ifdef Py_GIL_DISABLED
+ _PyListArray *array = list_allocate_array(size);
+ if (array == NULL) {
+ Py_DECREF(op);
+ return PyErr_NoMemory();
+ }
+ op->ob_item = array->ob_item;
+#else
op->ob_item = PyMem_New(PyObject *, size);
if (op->ob_item == NULL) {
Py_DECREF(op);
return PyErr_NoMemory();
}
+#endif
op->allocated = size;
return (PyObject *) op;
}
@@ -223,8 +342,9 @@ PyList_Size(PyObject *op)
PyErr_BadInternalCall();
return -1;
}
- else
- return Py_SIZE(op);
+ else {
+ return PyList_GET_SIZE(op);
+ }
}
static inline int
@@ -240,6 +360,63 @@ valid_index(Py_ssize_t i, Py_ssize_t limit)
return (size_t) i < (size_t) limit;
}
+#ifdef Py_GIL_DISABLED
+
+static PyObject *
+list_item_impl(PyListObject *self, Py_ssize_t idx)
+{
+ PyObject *item = NULL;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ if (!_PyObject_GC_IS_SHARED(self)) {
+ _PyObject_GC_SET_SHARED(self);
+ }
+ Py_ssize_t size = Py_SIZE(self);
+ if (!valid_index(idx, size)) {
+ goto exit;
+ }
+ item = _Py_NewRefWithLock(self->ob_item[idx]);
+exit:
+ Py_END_CRITICAL_SECTION();
+ return item;
+}
+
+static inline PyObject*
+list_get_item_ref(PyListObject *op, Py_ssize_t i)
+{
+ if (!_Py_IsOwnedByCurrentThread((PyObject *)op) && !_PyObject_GC_IS_SHARED(op)) {
+ return list_item_impl(op, i);
+ }
+ // Need atomic operation for the getting size.
+ Py_ssize_t size = PyList_GET_SIZE(op);
+ if (!valid_index(i, size)) {
+ return NULL;
+ }
+ PyObject **ob_item = _Py_atomic_load_ptr(&op->ob_item);
+ if (ob_item == NULL) {
+ return NULL;
+ }
+ Py_ssize_t cap = list_capacity(ob_item);
+ assert(cap != -1 && cap >= size);
+ if (!valid_index(i, cap)) {
+ return NULL;
+ }
+ PyObject *item = _Py_TryXGetRef(&ob_item[i]);
+ if (item == NULL) {
+ return list_item_impl(op, i);
+ }
+ return item;
+}
+#else
+static inline PyObject*
+list_get_item_ref(PyListObject *op, Py_ssize_t i)
+{
+ if (!valid_index(i, Py_SIZE(op))) {
+ return NULL;
+ }
+ return Py_NewRef(PyList_GET_ITEM(op, i));
+}
+#endif
+
PyObject *
PyList_GetItem(PyObject *op, Py_ssize_t i)
{
@@ -255,25 +432,48 @@ PyList_GetItem(PyObject *op, Py_ssize_t i)
return ((PyListObject *)op) -> ob_item[i];
}
+PyObject *
+PyList_GetItemRef(PyObject *op, Py_ssize_t i)
+{
+ if (!PyList_Check(op)) {
+ PyErr_SetString(PyExc_TypeError, "expected a list");
+ return NULL;
+ }
+ PyObject *item = list_get_item_ref((PyListObject *)op, i);
+ if (item == NULL) {
+ _Py_DECLARE_STR(list_err, "list index out of range");
+ PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err));
+ return NULL;
+ }
+ return item;
+}
+
int
PyList_SetItem(PyObject *op, Py_ssize_t i,
PyObject *newitem)
{
- PyObject **p;
if (!PyList_Check(op)) {
Py_XDECREF(newitem);
PyErr_BadInternalCall();
return -1;
}
- if (!valid_index(i, Py_SIZE(op))) {
+ int ret;
+ PyListObject *self = ((PyListObject *)op);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ if (!valid_index(i, Py_SIZE(self))) {
Py_XDECREF(newitem);
PyErr_SetString(PyExc_IndexError,
"list assignment index out of range");
- return -1;
+ ret = -1;
+ goto end;
}
- p = ((PyListObject *)op) -> ob_item + i;
- Py_XSETREF(*p, newitem);
- return 0;
+ PyObject *tmp = self->ob_item[i];
+ FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[i], newitem);
+ Py_XDECREF(tmp);
+ ret = 0;
+end:;
+ Py_END_CRITICAL_SECTION();
+ return ret;
}
static int
@@ -299,8 +499,8 @@ ins1(PyListObject *self, Py_ssize_t where, PyObject *v)
where = n;
items = self->ob_item;
for (i = n; --i >= where; )
- items[i+1] = items[i];
- items[where] = Py_NewRef(v);
+ FT_ATOMIC_STORE_PTR_RELAXED(items[i+1], items[i]);
+ FT_ATOMIC_STORE_PTR_RELEASE(items[where], Py_NewRef(v));
return 0;
}
@@ -311,20 +511,25 @@ PyList_Insert(PyObject *op, Py_ssize_t where, PyObject *newitem)
PyErr_BadInternalCall();
return -1;
}
- return ins1((PyListObject *)op, where, newitem);
+ PyListObject *self = (PyListObject *)op;
+ int err;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ err = ins1(self, where, newitem);
+ Py_END_CRITICAL_SECTION();
+ return err;
}
/* internal, used by _PyList_AppendTakeRef */
int
_PyList_AppendTakeRefListResize(PyListObject *self, PyObject *newitem)
{
- Py_ssize_t len = PyList_GET_SIZE(self);
+ Py_ssize_t len = Py_SIZE(self);
assert(self->allocated == -1 || self->allocated == len);
if (list_resize(self, len + 1) < 0) {
Py_DECREF(newitem);
return -1;
}
- PyList_SET_ITEM(self, len, newitem);
+ FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[len], newitem);
return 0;
}
@@ -332,7 +537,11 @@ int
PyList_Append(PyObject *op, PyObject *newitem)
{
if (PyList_Check(op) && (newitem != NULL)) {
- return _PyList_AppendTakeRef((PyListObject *)op, Py_NewRef(newitem));
+ int ret;
+ Py_BEGIN_CRITICAL_SECTION(op);
+ ret = _PyList_AppendTakeRef((PyListObject *)op, Py_NewRef(newitem));
+ Py_END_CRITICAL_SECTION();
+ return ret;
}
PyErr_BadInternalCall();
return -1;
@@ -341,8 +550,9 @@ PyList_Append(PyObject *op, PyObject *newitem)
/* Methods */
static void
-list_dealloc(PyListObject *op)
+list_dealloc(PyObject *self)
{
+ PyListObject *op = (PyListObject *)self;
Py_ssize_t i;
PyObject_GC_UnTrack(op);
Py_TRASHCAN_BEGIN(op, list_dealloc)
@@ -355,16 +565,12 @@ list_dealloc(PyListObject *op)
while (--i >= 0) {
Py_XDECREF(op->ob_item[i]);
}
- PyMem_Free(op->ob_item);
+ free_list_items(op->ob_item, false);
}
-#if PyList_MAXFREELIST > 0
- struct _Py_list_state *state = get_list_state();
-#ifdef Py_DEBUG
- // list_dealloc() must not be called after _PyList_Fini()
- assert(state->numfree != -1);
-#endif
- if (state->numfree < PyList_MAXFREELIST && PyList_CheckExact(op)) {
- state->free_list[state->numfree++] = op;
+#ifdef WITH_FREELISTS
+ struct _Py_list_freelist *list_freelist = get_list_freelist();
+ if (list_freelist->numfree < PyList_MAXFREELIST && list_freelist->numfree >= 0 && PyList_CheckExact(op)) {
+ list_freelist->items[list_freelist->numfree++] = op;
OBJECT_STAT_INC(to_freelist);
}
else
@@ -376,17 +582,12 @@ list_dealloc(PyListObject *op)
}
static PyObject *
-list_repr(PyListObject *v)
+list_repr_impl(PyListObject *v)
{
- Py_ssize_t i;
PyObject *s;
_PyUnicodeWriter writer;
-
- if (Py_SIZE(v) == 0) {
- return PyUnicode_FromString("[]");
- }
-
- i = Py_ReprEnter((PyObject*)v);
+ PyObject *item = NULL;
+ Py_ssize_t i = Py_ReprEnter((PyObject*)v);
if (i != 0) {
return i > 0 ? PyUnicode_FromString("[...]") : NULL;
}
@@ -402,12 +603,15 @@ list_repr(PyListObject *v)
/* Do repr() on each element. Note that this may mutate the list,
so must refetch the list size on each iteration. */
for (i = 0; i < Py_SIZE(v); ++i) {
+ /* Hold a strong reference since repr(item) can mutate the list */
+ item = Py_XNewRef(v->ob_item[i]);
+
if (i > 0) {
if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0)
goto error;
}
- s = PyObject_Repr(v->ob_item[i]);
+ s = PyObject_Repr(item);
if (s == NULL)
goto error;
@@ -416,6 +620,7 @@ list_repr(PyListObject *v)
goto error;
}
Py_DECREF(s);
+ Py_CLEAR(item);
}
writer.overallocate = 0;
@@ -426,45 +631,74 @@ list_repr(PyListObject *v)
return _PyUnicodeWriter_Finish(&writer);
error:
+ Py_XDECREF(item);
_PyUnicodeWriter_Dealloc(&writer);
Py_ReprLeave((PyObject *)v);
return NULL;
}
+static PyObject *
+list_repr(PyObject *self)
+{
+ if (PyList_GET_SIZE(self) == 0) {
+ return PyUnicode_FromString("[]");
+ }
+ PyListObject *v = (PyListObject *)self;
+ PyObject *ret = NULL;
+ Py_BEGIN_CRITICAL_SECTION(v);
+ ret = list_repr_impl(v);
+ Py_END_CRITICAL_SECTION();
+ return ret;
+}
+
static Py_ssize_t
-list_length(PyListObject *a)
+list_length(PyObject *a)
{
- return Py_SIZE(a);
+ return PyList_GET_SIZE(a);
}
static int
-list_contains(PyListObject *a, PyObject *el)
+list_contains(PyObject *aa, PyObject *el)
{
- PyObject *item;
- Py_ssize_t i;
- int cmp;
- for (i = 0, cmp = 0 ; cmp == 0 && i < Py_SIZE(a); ++i) {
- item = PyList_GET_ITEM(a, i);
- Py_INCREF(item);
- cmp = PyObject_RichCompareBool(item, el, Py_EQ);
+ for (Py_ssize_t i = 0; ; i++) {
+ PyObject *item = list_get_item_ref((PyListObject *)aa, i);
+ if (item == NULL) {
+ // out-of-bounds
+ return 0;
+ }
+ int cmp = PyObject_RichCompareBool(item, el, Py_EQ);
Py_DECREF(item);
+ if (cmp != 0) {
+ return cmp;
+ }
}
- return cmp;
+ return 0;
}
static PyObject *
-list_item(PyListObject *a, Py_ssize_t i)
+list_item(PyObject *aa, Py_ssize_t i)
{
- if (!valid_index(i, Py_SIZE(a))) {
+ PyListObject *a = (PyListObject *)aa;
+ if (!valid_index(i, PyList_GET_SIZE(a))) {
+ PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err));
+ return NULL;
+ }
+ PyObject *item;
+#ifdef Py_GIL_DISABLED
+ item = list_get_item_ref(a, i);
+ if (item == NULL) {
PyErr_SetObject(PyExc_IndexError, &_Py_STR(list_err));
return NULL;
}
- return Py_NewRef(a->ob_item[i]);
+#else
+ item = Py_NewRef(a->ob_item[i]);
+#endif
+ return item;
}
static PyObject *
-list_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
+list_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
{
PyListObject *np;
PyObject **src, **dest;
@@ -494,6 +728,8 @@ PyList_GetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
PyErr_BadInternalCall();
return NULL;
}
+ PyObject *ret;
+ Py_BEGIN_CRITICAL_SECTION(a);
if (ilow < 0) {
ilow = 0;
}
@@ -506,23 +742,18 @@ PyList_GetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh)
else if (ihigh > Py_SIZE(a)) {
ihigh = Py_SIZE(a);
}
- return list_slice((PyListObject *)a, ilow, ihigh);
+ ret = list_slice_lock_held((PyListObject *)a, ilow, ihigh);
+ Py_END_CRITICAL_SECTION();
+ return ret;
}
static PyObject *
-list_concat(PyListObject *a, PyObject *bb)
+list_concat_lock_held(PyListObject *a, PyListObject *b)
{
Py_ssize_t size;
Py_ssize_t i;
PyObject **src, **dest;
PyListObject *np;
- if (!PyList_Check(bb)) {
- PyErr_Format(PyExc_TypeError,
- "can only concatenate list (not \"%.200s\") to list",
- Py_TYPE(bb)->tp_name);
- return NULL;
- }
-#define b ((PyListObject *)bb)
assert((size_t)Py_SIZE(a) + (size_t)Py_SIZE(b) < PY_SSIZE_T_MAX);
size = Py_SIZE(a) + Py_SIZE(b);
if (size == 0) {
@@ -546,11 +777,28 @@ list_concat(PyListObject *a, PyObject *bb)
}
Py_SET_SIZE(np, size);
return (PyObject *)np;
-#undef b
}
static PyObject *
-list_repeat(PyListObject *a, Py_ssize_t n)
+list_concat(PyObject *aa, PyObject *bb)
+{
+ if (!PyList_Check(bb)) {
+ PyErr_Format(PyExc_TypeError,
+ "can only concatenate list (not \"%.200s\") to list",
+ Py_TYPE(bb)->tp_name);
+ return NULL;
+ }
+ PyListObject *a = (PyListObject *)aa;
+ PyListObject *b = (PyListObject *)bb;
+ PyObject *ret;
+ Py_BEGIN_CRITICAL_SECTION2(a, b);
+ ret = list_concat_lock_held(a, b);
+ Py_END_CRITICAL_SECTION2();
+ return ret;
+}
+
+static PyObject *
+list_repeat_lock_held(PyListObject *a, Py_ssize_t n)
{
const Py_ssize_t input_size = Py_SIZE(a);
if (input_size == 0 || n <= 0)
@@ -590,26 +838,57 @@ list_repeat(PyListObject *a, Py_ssize_t n)
return (PyObject *) np;
}
-static int
-_list_clear(PyListObject *a)
+static PyObject *
+list_repeat(PyObject *aa, Py_ssize_t n)
{
- Py_ssize_t i;
- PyObject **item = a->ob_item;
- if (item != NULL) {
- /* Because XDECREF can recursively invoke operations on
- this list, we make it empty first. */
- i = Py_SIZE(a);
- Py_SET_SIZE(a, 0);
- a->ob_item = NULL;
- a->allocated = 0;
- while (--i >= 0) {
- Py_XDECREF(item[i]);
- }
- PyMem_Free(item);
+ PyObject *ret;
+ PyListObject *a = (PyListObject *)aa;
+ Py_BEGIN_CRITICAL_SECTION(a);
+ ret = list_repeat_lock_held(a, n);
+ Py_END_CRITICAL_SECTION();
+ return ret;
+}
+
+static void
+list_clear_impl(PyListObject *a, bool is_resize)
+{
+ PyObject **items = a->ob_item;
+ if (items == NULL) {
+ return;
+ }
+
+ /* Because XDECREF can recursively invoke operations on
+ this list, we make it empty first. */
+ Py_ssize_t i = Py_SIZE(a);
+ Py_SET_SIZE(a, 0);
+ FT_ATOMIC_STORE_PTR_RELEASE(a->ob_item, NULL);
+ a->allocated = 0;
+ while (--i >= 0) {
+ Py_XDECREF(items[i]);
+ }
+#ifdef Py_GIL_DISABLED
+ if (is_resize) {
+ ensure_shared_on_resize(a);
}
- /* Never fails; the return value can be ignored.
- Note that there is no guarantee that the list is actually empty
- at this point, because XDECREF may have populated it again! */
+ bool use_qsbr = is_resize && _PyObject_GC_IS_SHARED(a);
+#else
+ bool use_qsbr = false;
+#endif
+ free_list_items(items, use_qsbr);
+ // Note that there is no guarantee that the list is actually empty
+ // at this point, because XDECREF may have populated it indirectly again!
+}
+
+static void
+list_clear(PyListObject *a)
+{
+ list_clear_impl(a, true);
+}
+
+static int
+list_clear_slot(PyObject *self)
+{
+ list_clear_impl((PyListObject *)self, false);
return 0;
}
@@ -620,7 +899,7 @@ _list_clear(PyListObject *a)
* guaranteed the call cannot fail.
*/
static int
-list_ass_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
+list_ass_slice_lock_held(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
{
/* Because [X]DECREF can recursively invoke list operations on
this list, we must postpone all [X]DECREF activity until
@@ -643,15 +922,6 @@ list_ass_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
if (v == NULL)
n = 0;
else {
- if (a == b) {
- /* Special case "a[i:j] = a" -- copy b first */
- v = list_slice(b, 0, Py_SIZE(b));
- if (v == NULL)
- return result;
- result = list_ass_slice(a, ilow, ihigh, v);
- Py_DECREF(v);
- return result;
- }
v_as_SF = PySequence_Fast(v, "can only assign an iterable");
if(v_as_SF == NULL)
goto Error;
@@ -673,7 +943,8 @@ list_ass_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
d = n - norig;
if (Py_SIZE(a) + d == 0) {
Py_XDECREF(v_as_SF);
- return _list_clear(a);
+ list_clear(a);
+ return 0;
}
item = a->ob_item;
/* recycle the items that we are about to remove */
@@ -724,6 +995,36 @@ list_ass_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
#undef b
}
+static int
+list_ass_slice(PyListObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
+{
+ int ret;
+ if (a == (PyListObject *)v) {
+ Py_BEGIN_CRITICAL_SECTION(a);
+ Py_ssize_t n = PyList_GET_SIZE(a);
+ PyObject *copy = list_slice_lock_held(a, 0, n);
+ if (copy == NULL) {
+ ret = -1;
+ }
+ else {
+ ret = list_ass_slice_lock_held(a, ilow, ihigh, copy);
+ Py_DECREF(copy);
+ }
+ Py_END_CRITICAL_SECTION();
+ }
+ else if (v != NULL && PyList_CheckExact(v)) {
+ Py_BEGIN_CRITICAL_SECTION2(a, v);
+ ret = list_ass_slice_lock_held(a, ilow, ihigh, v);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else {
+ Py_BEGIN_CRITICAL_SECTION(a);
+ ret = list_ass_slice_lock_held(a, ilow, ihigh, v);
+ Py_END_CRITICAL_SECTION();
+ }
+ return ret;
+}
+
int
PyList_SetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
{
@@ -734,26 +1035,28 @@ PyList_SetSlice(PyObject *a, Py_ssize_t ilow, Py_ssize_t ihigh, PyObject *v)
return list_ass_slice((PyListObject *)a, ilow, ihigh, v);
}
-static PyObject *
-list_inplace_repeat(PyListObject *self, Py_ssize_t n)
+static int
+list_inplace_repeat_lock_held(PyListObject *self, Py_ssize_t n)
{
Py_ssize_t input_size = PyList_GET_SIZE(self);
if (input_size == 0 || n == 1) {
- return Py_NewRef(self);
+ return 0;
}
if (n < 1) {
- (void)_list_clear(self);
- return Py_NewRef(self);
+ list_clear(self);
+ return 0;
}
if (input_size > PY_SSIZE_T_MAX / n) {
- return PyErr_NoMemory();
+ PyErr_NoMemory();
+ return -1;
}
Py_ssize_t output_size = input_size * n;
- if (list_resize(self, output_size) < 0)
- return NULL;
+ if (list_resize(self, output_size) < 0) {
+ return -1;
+ }
PyObject **items = self->ob_item;
for (Py_ssize_t j = 0; j < input_size; j++) {
@@ -761,25 +1064,61 @@ list_inplace_repeat(PyListObject *self, Py_ssize_t n)
}
_Py_memory_repeat((char *)items, sizeof(PyObject *)*output_size,
sizeof(PyObject *)*input_size);
+ return 0;
+}
- return Py_NewRef(self);
+static PyObject *
+list_inplace_repeat(PyObject *_self, Py_ssize_t n)
+{
+ PyObject *ret;
+ PyListObject *self = (PyListObject *) _self;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ if (list_inplace_repeat_lock_held(self, n) < 0) {
+ ret = NULL;
+ }
+ else {
+ ret = Py_NewRef(self);
+ }
+ Py_END_CRITICAL_SECTION();
+ return ret;
}
static int
-list_ass_item(PyListObject *a, Py_ssize_t i, PyObject *v)
+list_ass_item_lock_held(PyListObject *a, Py_ssize_t i, PyObject *v)
{
if (!valid_index(i, Py_SIZE(a))) {
PyErr_SetString(PyExc_IndexError,
"list assignment index out of range");
return -1;
}
- if (v == NULL)
- return list_ass_slice(a, i, i+1, v);
- Py_SETREF(a->ob_item[i], Py_NewRef(v));
+ PyObject *tmp = a->ob_item[i];
+ if (v == NULL) {
+ Py_ssize_t size = Py_SIZE(a);
+ for (Py_ssize_t idx = i; idx < size - 1; idx++) {
+ FT_ATOMIC_STORE_PTR_RELAXED(a->ob_item[idx], a->ob_item[idx + 1]);
+ }
+ Py_SET_SIZE(a, size - 1);
+ }
+ else {
+ FT_ATOMIC_STORE_PTR_RELEASE(a->ob_item[i], Py_NewRef(v));
+ }
+ Py_DECREF(tmp);
return 0;
}
+static int
+list_ass_item(PyObject *aa, Py_ssize_t i, PyObject *v)
+{
+ int ret;
+ PyListObject *a = (PyListObject *)aa;
+ Py_BEGIN_CRITICAL_SECTION(a);
+ ret = list_ass_item_lock_held(a, i, v);
+ Py_END_CRITICAL_SECTION();
+ return ret;
+}
+
/*[clinic input]
+@critical_section
list.insert
index: Py_ssize_t
@@ -791,28 +1130,31 @@ Insert object before index.
static PyObject *
list_insert_impl(PyListObject *self, Py_ssize_t index, PyObject *object)
-/*[clinic end generated code: output=7f35e32f60c8cb78 input=858514cf894c7eab]*/
+/*[clinic end generated code: output=7f35e32f60c8cb78 input=b1987ca998a4ae2d]*/
{
- if (ins1(self, index, object) == 0)
+ if (ins1(self, index, object) == 0) {
Py_RETURN_NONE;
+ }
return NULL;
}
/*[clinic input]
-list.clear
+@critical_section
+list.clear as py_list_clear
Remove all items from list.
[clinic start generated code]*/
static PyObject *
-list_clear_impl(PyListObject *self)
-/*[clinic end generated code: output=67a1896c01f74362 input=ca3c1646856742f6]*/
+py_list_clear_impl(PyListObject *self)
+/*[clinic end generated code: output=83726743807e3518 input=e285b7f09051a9ba]*/
{
- _list_clear(self);
+ list_clear(self);
Py_RETURN_NONE;
}
/*[clinic input]
+@critical_section
list.copy
Return a shallow copy of the list.
@@ -820,12 +1162,13 @@ Return a shallow copy of the list.
static PyObject *
list_copy_impl(PyListObject *self)
-/*[clinic end generated code: output=ec6b72d6209d418e input=6453ab159e84771f]*/
+/*[clinic end generated code: output=ec6b72d6209d418e input=81c54b0c7bb4f73d]*/
{
- return list_slice(self, 0, Py_SIZE(self));
+ return list_slice_lock_held(self, 0, Py_SIZE(self));
}
/*[clinic input]
+@critical_section
list.append
object: object
@@ -835,8 +1178,8 @@ Append object to the end of the list.
[clinic start generated code]*/
static PyObject *
-list_append(PyListObject *self, PyObject *object)
-/*[clinic end generated code: output=7c096003a29c0eae input=43a3fe48a7066e91]*/
+list_append_impl(PyListObject *self, PyObject *object)
+/*[clinic end generated code: output=78423561d92ed405 input=122b0853de54004f]*/
{
if (_PyList_AppendTakeRef(self, Py_NewRef(object)) < 0) {
return NULL;
@@ -844,83 +1187,61 @@ list_append(PyListObject *self, PyObject *object)
Py_RETURN_NONE;
}
-/*[clinic input]
-list.extend
-
- iterable: object
- /
-
-Extend list by appending elements from the iterable.
-[clinic start generated code]*/
-
-static PyObject *
-list_extend(PyListObject *self, PyObject *iterable)
-/*[clinic end generated code: output=630fb3bca0c8e789 input=9ec5ba3a81be3a4d]*/
+static int
+list_extend_fast(PyListObject *self, PyObject *iterable)
{
- PyObject *it; /* iter(v) */
- Py_ssize_t m; /* size of self */
- Py_ssize_t n; /* guess for size of iterable */
- Py_ssize_t i;
- PyObject *(*iternext)(PyObject *);
+ Py_ssize_t n = PySequence_Fast_GET_SIZE(iterable);
+ if (n == 0) {
+ /* short circuit when iterable is empty */
+ return 0;
+ }
- /* Special cases:
- 1) lists and tuples which can use PySequence_Fast ops
- 2) extending self to self requires making a copy first
- */
- if (PyList_CheckExact(iterable) || PyTuple_CheckExact(iterable) ||
- (PyObject *)self == iterable) {
- PyObject **src, **dest;
- iterable = PySequence_Fast(iterable, "argument must be iterable");
- if (!iterable)
- return NULL;
- n = PySequence_Fast_GET_SIZE(iterable);
- if (n == 0) {
- /* short circuit when iterable is empty */
- Py_DECREF(iterable);
- Py_RETURN_NONE;
- }
- m = Py_SIZE(self);
- /* It should not be possible to allocate a list large enough to cause
- an overflow on any relevant platform */
- assert(m < PY_SSIZE_T_MAX - n);
- if (self->ob_item == NULL) {
- if (list_preallocate_exact(self, n) < 0) {
- return NULL;
- }
- Py_SET_SIZE(self, n);
- }
- else if (list_resize(self, m + n) < 0) {
- Py_DECREF(iterable);
- return NULL;
- }
- /* note that we may still have self == iterable here for the
- * situation a.extend(a), but the following code works
- * in that case too. Just make sure to resize self
- * before calling PySequence_Fast_ITEMS.
- */
- /* populate the end of self with iterable's items */
- src = PySequence_Fast_ITEMS(iterable);
- dest = self->ob_item + m;
- for (i = 0; i < n; i++) {
- PyObject *o = src[i];
- dest[i] = Py_NewRef(o);
+ Py_ssize_t m = Py_SIZE(self);
+ // It should not be possible to allocate a list large enough to cause
+ // an overflow on any relevant platform.
+ assert(m < PY_SSIZE_T_MAX - n);
+ if (self->ob_item == NULL) {
+ if (list_preallocate_exact(self, n) < 0) {
+ return -1;
}
- Py_DECREF(iterable);
- Py_RETURN_NONE;
+ Py_SET_SIZE(self, n);
+ }
+ else if (list_resize(self, m + n) < 0) {
+ return -1;
}
- it = PyObject_GetIter(iterable);
- if (it == NULL)
- return NULL;
- iternext = *Py_TYPE(it)->tp_iternext;
+ // note that we may still have self == iterable here for the
+ // situation a.extend(a), but the following code works
+ // in that case too. Just make sure to resize self
+ // before calling PySequence_Fast_ITEMS.
+ //
+ // populate the end of self with iterable's items.
+ PyObject **src = PySequence_Fast_ITEMS(iterable);
+ PyObject **dest = self->ob_item + m;
+ for (Py_ssize_t i = 0; i < n; i++) {
+ PyObject *o = src[i];
+ FT_ATOMIC_STORE_PTR_RELEASE(dest[i], Py_NewRef(o));
+ }
+ return 0;
+}
+
+static int
+list_extend_iter_lock_held(PyListObject *self, PyObject *iterable)
+{
+ PyObject *it = PyObject_GetIter(iterable);
+ if (it == NULL) {
+ return -1;
+ }
+ PyObject *(*iternext)(PyObject *) = *Py_TYPE(it)->tp_iternext;
/* Guess a result list size. */
- n = PyObject_LengthHint(iterable, 8);
+ Py_ssize_t n = PyObject_LengthHint(iterable, 8);
if (n < 0) {
Py_DECREF(it);
- return NULL;
+ return -1;
}
- m = Py_SIZE(self);
+
+ Py_ssize_t m = Py_SIZE(self);
if (m > PY_SSIZE_T_MAX - n) {
/* m + n overflowed; on the chance that n lied, and there really
* is enough room, ignore it. If n was telling the truth, we'll
@@ -933,8 +1254,10 @@ list_extend(PyListObject *self, PyObject *iterable)
}
else {
/* Make room. */
- if (list_resize(self, m + n) < 0)
+ if (list_resize(self, m + n) < 0) {
goto error;
+ }
+
/* Make the list sane again. */
Py_SET_SIZE(self, m);
}
@@ -951,10 +1274,11 @@ list_extend(PyListObject *self, PyObject *iterable)
}
break;
}
+
if (Py_SIZE(self) < self->allocated) {
- /* steals ref */
- PyList_SET_ITEM(self, Py_SIZE(self), item);
- Py_SET_SIZE(self, Py_SIZE(self) + 1);
+ Py_ssize_t len = Py_SIZE(self);
+ FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item[len], item); // steals item ref
+ Py_SET_SIZE(self, len + 1);
}
else {
if (_PyList_AppendTakeRef(self, item) < 0)
@@ -969,11 +1293,173 @@ list_extend(PyListObject *self, PyObject *iterable)
}
Py_DECREF(it);
- Py_RETURN_NONE;
+ return 0;
error:
Py_DECREF(it);
- return NULL;
+ return -1;
+}
+
+static int
+list_extend_lock_held(PyListObject *self, PyObject *iterable)
+{
+ PyObject *seq = PySequence_Fast(iterable, "argument must be iterable");
+ if (!seq) {
+ return -1;
+ }
+
+ int res = list_extend_fast(self, seq);
+ Py_DECREF(seq);
+ return res;
+}
+
+static int
+list_extend_set(PyListObject *self, PySetObject *other)
+{
+ Py_ssize_t m = Py_SIZE(self);
+ Py_ssize_t n = PySet_GET_SIZE(other);
+ if (list_resize(self, m + n) < 0) {
+ return -1;
+ }
+ /* populate the end of self with iterable's items */
+ Py_ssize_t setpos = 0;
+ Py_hash_t hash;
+ PyObject *key;
+ PyObject **dest = self->ob_item + m;
+ while (_PySet_NextEntryRef((PyObject *)other, &setpos, &key, &hash)) {
+ FT_ATOMIC_STORE_PTR_RELEASE(*dest, key);
+ dest++;
+ }
+ Py_SET_SIZE(self, m + n);
+ return 0;
+}
+
+static int
+list_extend_dict(PyListObject *self, PyDictObject *dict, int which_item)
+{
+ // which_item: 0 for keys and 1 for values
+ Py_ssize_t m = Py_SIZE(self);
+ Py_ssize_t n = PyDict_GET_SIZE(dict);
+ if (list_resize(self, m + n) < 0) {
+ return -1;
+ }
+
+ PyObject **dest = self->ob_item + m;
+ Py_ssize_t pos = 0;
+ PyObject *keyvalue[2];
+ while (_PyDict_Next((PyObject *)dict, &pos, &keyvalue[0], &keyvalue[1], NULL)) {
+ PyObject *obj = keyvalue[which_item];
+ Py_INCREF(obj);
+ FT_ATOMIC_STORE_PTR_RELEASE(*dest, obj);
+ dest++;
+ }
+
+ Py_SET_SIZE(self, m + n);
+ return 0;
+}
+
+static int
+list_extend_dictitems(PyListObject *self, PyDictObject *dict)
+{
+ Py_ssize_t m = Py_SIZE(self);
+ Py_ssize_t n = PyDict_GET_SIZE(dict);
+ if (list_resize(self, m + n) < 0) {
+ return -1;
+ }
+
+ PyObject **dest = self->ob_item + m;
+ Py_ssize_t pos = 0;
+ Py_ssize_t i = 0;
+ PyObject *key, *value;
+ while (_PyDict_Next((PyObject *)dict, &pos, &key, &value, NULL)) {
+ PyObject *item = PyTuple_Pack(2, key, value);
+ if (item == NULL) {
+ Py_SET_SIZE(self, m + i);
+ return -1;
+ }
+ FT_ATOMIC_STORE_PTR_RELEASE(*dest, item);
+ dest++;
+ i++;
+ }
+
+ Py_SET_SIZE(self, m + n);
+ return 0;
+}
+
+static int
+_list_extend(PyListObject *self, PyObject *iterable)
+{
+ // Special case:
+ // lists and tuples which can use PySequence_Fast ops
+ int res = -1;
+ if ((PyObject *)self == iterable) {
+ Py_BEGIN_CRITICAL_SECTION(self);
+ res = list_inplace_repeat_lock_held(self, 2);
+ Py_END_CRITICAL_SECTION();
+ }
+ else if (PyList_CheckExact(iterable)) {
+ Py_BEGIN_CRITICAL_SECTION2(self, iterable);
+ res = list_extend_lock_held(self, iterable);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else if (PyTuple_CheckExact(iterable)) {
+ Py_BEGIN_CRITICAL_SECTION(self);
+ res = list_extend_lock_held(self, iterable);
+ Py_END_CRITICAL_SECTION();
+ }
+ else if (PyAnySet_CheckExact(iterable)) {
+ Py_BEGIN_CRITICAL_SECTION2(self, iterable);
+ res = list_extend_set(self, (PySetObject *)iterable);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else if (PyDict_CheckExact(iterable)) {
+ Py_BEGIN_CRITICAL_SECTION2(self, iterable);
+ res = list_extend_dict(self, (PyDictObject *)iterable, 0 /*keys*/);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else if (Py_IS_TYPE(iterable, &PyDictKeys_Type)) {
+ PyDictObject *dict = ((_PyDictViewObject *)iterable)->dv_dict;
+ Py_BEGIN_CRITICAL_SECTION2(self, dict);
+ res = list_extend_dict(self, dict, 0 /*keys*/);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else if (Py_IS_TYPE(iterable, &PyDictValues_Type)) {
+ PyDictObject *dict = ((_PyDictViewObject *)iterable)->dv_dict;
+ Py_BEGIN_CRITICAL_SECTION2(self, dict);
+ res = list_extend_dict(self, dict, 1 /*values*/);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else if (Py_IS_TYPE(iterable, &PyDictItems_Type)) {
+ PyDictObject *dict = ((_PyDictViewObject *)iterable)->dv_dict;
+ Py_BEGIN_CRITICAL_SECTION2(self, dict);
+ res = list_extend_dictitems(self, dict);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else {
+ Py_BEGIN_CRITICAL_SECTION(self);
+ res = list_extend_iter_lock_held(self, iterable);
+ Py_END_CRITICAL_SECTION();
+ }
+ return res;
+}
+
+/*[clinic input]
+list.extend as list_extend
+
+ iterable: object
+ /
+
+Extend list by appending elements from the iterable.
+[clinic start generated code]*/
+
+static PyObject *
+list_extend(PyListObject *self, PyObject *iterable)
+/*[clinic end generated code: output=630fb3bca0c8e789 input=979da7597a515791]*/
+{
+ if (_list_extend(self, iterable) < 0) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
}
PyObject *
@@ -982,19 +1468,43 @@ _PyList_Extend(PyListObject *self, PyObject *iterable)
return list_extend(self, iterable);
}
-static PyObject *
-list_inplace_concat(PyListObject *self, PyObject *other)
+int
+PyList_Extend(PyObject *self, PyObject *iterable)
{
- PyObject *result;
+ if (!PyList_Check(self)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ return _list_extend((PyListObject*)self, iterable);
+}
+
- result = list_extend(self, other);
- if (result == NULL)
- return result;
- Py_DECREF(result);
+int
+PyList_Clear(PyObject *self)
+{
+ if (!PyList_Check(self)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ Py_BEGIN_CRITICAL_SECTION(self);
+ list_clear((PyListObject*)self);
+ Py_END_CRITICAL_SECTION();
+ return 0;
+}
+
+
+static PyObject *
+list_inplace_concat(PyObject *_self, PyObject *other)
+{
+ PyListObject *self = (PyListObject *)_self;
+ if (_list_extend(self, other) < 0) {
+ return NULL;
+ }
return Py_NewRef(self);
}
/*[clinic input]
+@critical_section
list.pop
index: Py_ssize_t = -1
@@ -1007,7 +1517,7 @@ Raises IndexError if list is empty or index is out of range.
static PyObject *
list_pop_impl(PyListObject *self, Py_ssize_t index)
-/*[clinic end generated code: output=6bd69dcb3f17eca8 input=b83675976f329e6f]*/
+/*[clinic end generated code: output=6bd69dcb3f17eca8 input=c269141068ae4b8f]*/
{
PyObject *v;
int status;
@@ -1029,7 +1539,8 @@ list_pop_impl(PyListObject *self, Py_ssize_t index)
const Py_ssize_t size_after_pop = Py_SIZE(self) - 1;
if (size_after_pop == 0) {
Py_INCREF(v);
- status = _list_clear(self);
+ list_clear(self);
+ status = 0;
}
else {
if ((size_after_pop - index) > 0) {
@@ -1162,6 +1673,15 @@ sortslice_advance(sortslice *slice, Py_ssize_t n)
/* Avoid malloc for small temp arrays. */
#define MERGESTATE_TEMP_SIZE 256
+/* The largest value of minrun. This must be a power of 2, and >= 1, so that
+ * the compute_minrun() algorithm guarantees to return a result no larger than
+ * this,
+ */
+#define MAX_MINRUN 64
+#if ((MAX_MINRUN) < 1) || ((MAX_MINRUN) & ((MAX_MINRUN) - 1))
+#error "MAX_MINRUN must be a power of 2, and >= 1"
+#endif
+
/* One MergeState exists on the stack per invocation of mergesort. It's just
* a convenient way to pass state around among the helper functions.
*/
@@ -1219,123 +1739,248 @@ struct s_MergeState {
int (*tuple_elem_compare)(PyObject *, PyObject *, MergeState *);
};
-/* binarysort is the best method for sorting small arrays: it does
- few compares, but can do data movement quadratic in the number of
- elements.
- [lo, hi) is a contiguous slice of a list, and is sorted via
- binary insertion. This sort is stable.
- On entry, must have lo <= start <= hi, and that [lo, start) is already
- sorted (pass start == lo if you don't know!).
- If islt() complains return -1, else 0.
+/* binarysort is the best method for sorting small arrays: it does few
+ compares, but can do data movement quadratic in the number of elements.
+ ss->keys is viewed as an array of n kays, a[:n]. a[:ok] is already sorted.
+ Pass ok = 0 (or 1) if you don't know.
+ It's sorted in-place, by a stable binary insertion sort. If ss->values
+ isn't NULL, it's permuted in lockstap with ss->keys.
+ On entry, must have n >= 1, and 0 <= ok <= n <= MAX_MINRUN.
+ Return -1 if comparison raises an exception, else 0.
Even in case of error, the output slice will be some permutation of
the input (nothing is lost or duplicated).
*/
static int
-binarysort(MergeState *ms, sortslice lo, PyObject **hi, PyObject **start)
+binarysort(MergeState *ms, const sortslice *ss, Py_ssize_t n, Py_ssize_t ok)
{
- Py_ssize_t k;
- PyObject **l, **p, **r;
+ Py_ssize_t k; /* for IFLT macro expansion */
+ PyObject ** const a = ss->keys;
+ PyObject ** const v = ss->values;
+ const bool has_values = v != NULL;
PyObject *pivot;
+ Py_ssize_t M;
+
+ assert(0 <= ok && ok <= n && 1 <= n && n <= MAX_MINRUN);
+ /* assert a[:ok] is sorted */
+ if (! ok)
+ ++ok;
+ /* Regular insertion sort has average- and worst-case O(n**2) cost
+ for both # of comparisons and number of bytes moved. But its branches
+ are highly predictable, and it loves sorted input (n-1 compares and no
+ data movement). This is significant in cases like sortperf.py's %sort,
+ where an out-of-order element near the start of a run is moved into
+ place slowly but then the remaining elements up to length minrun are
+ generally at worst one slot away from their correct position (so only
+ need 1 or 2 commpares to resolve). If comparisons are very fast (such
+ as for a list of Python floats), the simple inner loop leaves it
+ very competitive with binary insertion, despite that it does
+ significantly more compares overall on random data.
- assert(lo.keys <= start && start <= hi);
- /* assert [lo, start) is sorted */
- if (lo.keys == start)
- ++start;
- for (; start < hi; ++start) {
- /* set l to where *start belongs */
- l = lo.keys;
- r = start;
- pivot = *r;
- /* Invariants:
- * pivot >= all in [lo, l).
- * pivot < all in [r, start).
- * The second is vacuously true at the start.
+ Binary insertion sort has worst, average, and best case O(n log n)
+ cost for # of comparisons, but worst and average case O(n**2) cost
+ for data movement. The more expensive comparisons, the more important
+ the comparison advantage. But its branches are less predictable the
+ more "randomish" the data, and that's so significant its worst case
+ in real life is random input rather than reverse-ordered (which does
+ about twice the data movement than random input does).
+
+ Note that the number of bytes moved doesn't seem to matter. MAX_MINRUN
+ of 64 is so small that the key and value pointers all fit in a corner
+ of L1 cache, and moving things around in that is very fast. */
+#if 0 // ordinary insertion sort.
+ PyObject * vpivot = NULL;
+ for (; ok < n; ++ok) {
+ pivot = a[ok];
+ if (has_values)
+ vpivot = v[ok];
+ for (M = ok - 1; M >= 0; --M) {
+ k = ISLT(pivot, a[M]);
+ if (k < 0) {
+ a[M + 1] = pivot;
+ if (has_values)
+ v[M + 1] = vpivot;
+ goto fail;
+ }
+ else if (k) {
+ a[M + 1] = a[M];
+ if (has_values)
+ v[M + 1] = v[M];
+ }
+ else
+ break;
+ }
+ a[M + 1] = pivot;
+ if (has_values)
+ v[M + 1] = vpivot;
+ }
+#else // binary insertion sort
+ Py_ssize_t L, R;
+ for (; ok < n; ++ok) {
+ /* set L to where a[ok] belongs */
+ L = 0;
+ R = ok;
+ pivot = a[ok];
+ /* Slice invariants. vacuously true at the start:
+ * all a[0:L] <= pivot
+ * all a[L:R] unknown
+ * all a[R:ok] > pivot
*/
- assert(l < r);
+ assert(L < R);
do {
- p = l + ((r - l) >> 1);
- IFLT(pivot, *p)
- r = p;
+ /* don't do silly ;-) things to prevent overflow when finding
+ the midpoint; L and R are very far from filling a Py_ssize_t */
+ M = (L + R) >> 1;
+#if 1 // straightforward, but highly unpredictable branch on random data
+ IFLT(pivot, a[M])
+ R = M;
else
- l = p+1;
- } while (l < r);
- assert(l == r);
- /* The invariants still hold, so pivot >= all in [lo, l) and
- pivot < all in [l, start), so pivot belongs at l. Note
- that if there are elements equal to pivot, l points to the
- first slot after them -- that's why this sort is stable.
- Slide over to make room.
- Caution: using memmove is much slower under MSVC 5;
- we're not usually moving many slots. */
- for (p = start; p > l; --p)
- *p = *(p-1);
- *l = pivot;
- if (lo.values != NULL) {
- Py_ssize_t offset = lo.values - lo.keys;
- p = start + offset;
- pivot = *p;
- l += offset;
- for (p = start + offset; p > l; --p)
- *p = *(p-1);
- *l = pivot;
+ L = M + 1;
+#else
+ /* Try to get compiler to generate conditional move instructions
+ instead. Works fine, but leaving it disabled for now because
+ it's not yielding consistently faster sorts. Needs more
+ investigation. More computation in the inner loop adds its own
+ costs, which can be significant when compares are fast. */
+ k = ISLT(pivot, a[M]);
+ if (k < 0)
+ goto fail;
+ Py_ssize_t Mp1 = M + 1;
+ R = k ? M : R;
+ L = k ? L : Mp1;
+#endif
+ } while (L < R);
+ assert(L == R);
+ /* a[:L] holds all elements from a[:ok] <= pivot now, so pivot belongs
+ at index L. Slide a[L:ok] to the right a slot to make room for it.
+ Caution: using memmove is much slower under MSVC 5; we're not
+ usually moving many slots. Years later: under Visual Studio 2022,
+ memmove seems just slightly slower than doing it "by hand". */
+ for (M = ok; M > L; --M)
+ a[M] = a[M - 1];
+ a[L] = pivot;
+ if (has_values) {
+ pivot = v[ok];
+ for (M = ok; M > L; --M)
+ v[M] = v[M - 1];
+ v[L] = pivot;
}
}
+#endif // pick binary or regular insertion sort
return 0;
fail:
return -1;
}
-/*
-Return the length of the run beginning at lo, in the slice [lo, hi). lo < hi
-is required on entry. "A run" is the longest ascending sequence, with
-
- lo[0] <= lo[1] <= lo[2] <= ...
-
-or the longest descending sequence, with
-
- lo[0] > lo[1] > lo[2] > ...
+static void
+sortslice_reverse(sortslice *s, Py_ssize_t n)
+{
+ reverse_slice(s->keys, &s->keys[n]);
+ if (s->values != NULL)
+ reverse_slice(s->values, &s->values[n]);
+}
-Boolean *descending is set to 0 in the former case, or to 1 in the latter.
-For its intended use in a stable mergesort, the strictness of the defn of
-"descending" is needed so that the caller can safely reverse a descending
-sequence without violating stability (strict > ensures there are no equal
-elements to get out of order).
+/*
+Return the length of the run beginning at slo->keys, spanning no more than
+nremaining elements. The run beginning there may be ascending or descending,
+but the function permutes it in place, if needed, so that it's always ascending
+upon return.
Returns -1 in case of error.
*/
static Py_ssize_t
-count_run(MergeState *ms, PyObject **lo, PyObject **hi, int *descending)
+count_run(MergeState *ms, sortslice *slo, Py_ssize_t nremaining)
{
- Py_ssize_t k;
+ Py_ssize_t k; /* used by IFLT macro expansion */
Py_ssize_t n;
+ PyObject ** const lo = slo->keys;
- assert(lo < hi);
- *descending = 0;
- ++lo;
- if (lo == hi)
- return 1;
+ /* In general, as things go on we've established that the slice starts
+ with a monotone run of n elements, starting at lo. */
- n = 2;
- IFLT(*lo, *(lo-1)) {
- *descending = 1;
- for (lo = lo+1; lo < hi; ++lo, ++n) {
- IFLT(*lo, *(lo-1))
- ;
- else
- break;
- }
+ /* We're n elements into the slice, and the most recent neq+1 elments are
+ * all equal. This reverses them in-place, and resets neq for reuse.
+ */
+#define REVERSE_LAST_NEQ \
+ if (neq) { \
+ sortslice slice = *slo; \
+ ++neq; \
+ sortslice_advance(&slice, n - neq); \
+ sortslice_reverse(&slice, neq); \
+ neq = 0; \
}
- else {
- for (lo = lo+1; lo < hi; ++lo, ++n) {
- IFLT(*lo, *(lo-1))
+
+ /* Sticking to only __lt__ compares is confusing and error-prone. But in
+ * this routine, almost all uses of IFLT can be captured by tiny macros
+ * giving mnemonic names to the intent. Note that inline functions don't
+ * work for this (IFLT expands to code including `goto fail`).
+ */
+#define IF_NEXT_LARGER IFLT(lo[n-1], lo[n])
+#define IF_NEXT_SMALLER IFLT(lo[n], lo[n-1])
+
+ assert(nremaining);
+ /* try ascending run first */
+ for (n = 1; n < nremaining; ++n) {
+ IF_NEXT_SMALLER
+ break;
+ }
+ if (n == nremaining)
+ return n;
+ /* lo[n] is strictly less */
+ /* If n is 1 now, then the first compare established it's a descending
+ * run, so fall through to the descending case. But if n > 1, there are
+ * n elements in an ascending run terminated by the strictly less lo[n].
+ * If the first key < lo[n-1], *somewhere* along the way the sequence
+ * increased, so we're done (there is no descending run).
+ * Else first key >= lo[n-1], which implies that the entire ascending run
+ * consists of equal elements. In that case, this is a descending run,
+ * and we reverse the all-equal prefix in-place.
+ */
+ if (n > 1) {
+ IFLT(lo[0], lo[n-1])
+ return n;
+ sortslice_reverse(slo, n);
+ }
+ ++n; /* in all cases it's been established that lo[n] has been resolved */
+
+ /* Finish descending run. All-squal subruns are reversed in-place on the
+ * fly. Their original order will be restored at the end by the whole-slice
+ * reversal.
+ */
+ Py_ssize_t neq = 0;
+ for ( ; n < nremaining; ++n) {
+ IF_NEXT_SMALLER {
+ /* This ends the most recent run of equal elments, but still in
+ * the "descending" direction.
+ */
+ REVERSE_LAST_NEQ
+ }
+ else {
+ IF_NEXT_LARGER /* descending run is over */
break;
+ else /* not x < y and not y < x implies x == y */
+ ++neq;
}
}
+ REVERSE_LAST_NEQ
+ sortslice_reverse(slo, n); /* transform to ascending run */
+
+ /* And after reversing, it's possible this can be extended by a
+ * naturally increasing suffix; e.g., [3, 2, 3, 4, 1] makes an
+ * ascending run from the first 4 elements.
+ */
+ for ( ; n < nremaining; ++n) {
+ IF_NEXT_SMALLER
+ break;
+ }
return n;
fail:
return -1;
+
+#undef REVERSE_LAST_NEQ
+#undef IF_NEXT_SMALLER
+#undef IF_NEXT_LARGER
}
/*
@@ -2033,10 +2678,10 @@ merge_force_collapse(MergeState *ms)
/* Compute a good value for the minimum run length; natural runs shorter
* than this are boosted artificially via binary insertion.
*
- * If n < 64, return n (it's too small to bother with fancy stuff).
- * Else if n is an exact power of 2, return 32.
- * Else return an int k, 32 <= k <= 64, such that n/k is close to, but
- * strictly less than, an exact power of 2.
+ * If n < MAX_MINRUN return n (it's too small to bother with fancy stuff).
+ * Else if n is an exact power of 2, return MAX_MINRUN / 2.
+ * Else return an int k, MAX_MINRUN / 2 <= k <= MAX_MINRUN, such that n/k is
+ * close to, but strictly less than, an exact power of 2.
*
* See listsort.txt for more info.
*/
@@ -2046,21 +2691,13 @@ merge_compute_minrun(Py_ssize_t n)
Py_ssize_t r = 0; /* becomes 1 if any 1 bits are shifted off */
assert(n >= 0);
- while (n >= 64) {
+ while (n >= MAX_MINRUN) {
r |= n & 1;
n >>= 1;
}
return n + r;
}
-static void
-reverse_sortslice(sortslice *s, Py_ssize_t n)
-{
- reverse_slice(s->keys, &s->keys[n]);
- if (s->values != NULL)
- reverse_slice(s->values, &s->values[n]);
-}
-
/* Here we define custom comparison functions to optimize for the cases one commonly
* encounters in practice: homogeneous lists, often of one of the basic types. */
@@ -2229,6 +2866,7 @@ unsafe_tuple_compare(PyObject *v, PyObject *w, MergeState *ms)
* duplicated).
*/
/*[clinic input]
+@critical_section
list.sort
*
@@ -2248,7 +2886,7 @@ The reverse flag can be set to sort in descending order.
static PyObject *
list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
-/*[clinic end generated code: output=57b9f9c5e23fbe42 input=a74c4cd3ec6b5c08]*/
+/*[clinic end generated code: output=57b9f9c5e23fbe42 input=667bf25d0e3a3676]*/
{
MergeState ms;
Py_ssize_t nremaining;
@@ -2275,7 +2913,7 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
saved_ob_item = self->ob_item;
saved_allocated = self->allocated;
Py_SET_SIZE(self, 0);
- self->ob_item = NULL;
+ FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item, NULL);
self->allocated = -1; /* any operation will reset it to >= 0 */
if (keyfunc == NULL) {
@@ -2427,20 +3065,17 @@ list_sort_impl(PyListObject *self, PyObject *keyfunc, int reverse)
*/
minrun = merge_compute_minrun(nremaining);
do {
- int descending;
Py_ssize_t n;
/* Identify next run. */
- n = count_run(&ms, lo.keys, lo.keys + nremaining, &descending);
+ n = count_run(&ms, &lo, nremaining);
if (n < 0)
goto fail;
- if (descending)
- reverse_sortslice(&lo, n);
/* If short, extend to min(minrun, nremaining). */
if (n < minrun) {
const Py_ssize_t force = nremaining <= minrun ?
nremaining : minrun;
- if (binarysort(&ms, lo, lo.keys + force, lo.keys + n) < 0)
+ if (binarysort(&ms, &lo, force, n) < 0)
goto fail;
n = force;
}
@@ -2495,15 +3130,21 @@ keyfunc_fail:
final_ob_item = self->ob_item;
i = Py_SIZE(self);
Py_SET_SIZE(self, saved_ob_size);
- self->ob_item = saved_ob_item;
- self->allocated = saved_allocated;
+ FT_ATOMIC_STORE_PTR_RELEASE(self->ob_item, saved_ob_item);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(self->allocated, saved_allocated);
if (final_ob_item != NULL) {
- /* we cannot use _list_clear() for this because it does not
+ /* we cannot use list_clear() for this because it does not
guarantee that the list is really empty when it returns */
while (--i >= 0) {
Py_XDECREF(final_ob_item[i]);
}
- PyMem_Free(final_ob_item);
+#ifdef Py_GIL_DISABLED
+ ensure_shared_on_resize(self);
+ bool use_qsbr = _PyObject_GC_IS_SHARED(self);
+#else
+ bool use_qsbr = false;
+#endif
+ free_list_items(final_ob_item, use_qsbr);
}
return Py_XNewRef(result);
}
@@ -2517,7 +3158,9 @@ PyList_Sort(PyObject *v)
PyErr_BadInternalCall();
return -1;
}
+ Py_BEGIN_CRITICAL_SECTION(v);
v = list_sort_impl((PyListObject *)v, NULL, 0);
+ Py_END_CRITICAL_SECTION();
if (v == NULL)
return -1;
Py_DECREF(v);
@@ -2525,6 +3168,7 @@ PyList_Sort(PyObject *v)
}
/*[clinic input]
+@critical_section
list.reverse
Reverse *IN PLACE*.
@@ -2532,7 +3176,7 @@ Reverse *IN PLACE*.
static PyObject *
list_reverse_impl(PyListObject *self)
-/*[clinic end generated code: output=482544fc451abea9 input=eefd4c3ae1bc9887]*/
+/*[clinic end generated code: output=482544fc451abea9 input=04ac8e0c6a66e4d9]*/
{
if (Py_SIZE(self) > 1)
reverse_slice(self->ob_item, self->ob_item + Py_SIZE(self));
@@ -2548,8 +3192,11 @@ PyList_Reverse(PyObject *v)
PyErr_BadInternalCall();
return -1;
}
- if (Py_SIZE(self) > 1)
+ Py_BEGIN_CRITICAL_SECTION(self);
+ if (Py_SIZE(self) > 1) {
reverse_slice(self->ob_item, self->ob_item + Py_SIZE(self));
+ }
+ Py_END_CRITICAL_SECTION()
return 0;
}
@@ -2560,7 +3207,12 @@ PyList_AsTuple(PyObject *v)
PyErr_BadInternalCall();
return NULL;
}
- return _PyTuple_FromArray(((PyListObject *)v)->ob_item, Py_SIZE(v));
+ PyObject *ret;
+ PyListObject *self = (PyListObject *)v;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ ret = _PyTuple_FromArray(self->ob_item, Py_SIZE(v));
+ Py_END_CRITICAL_SECTION();
+ return ret;
}
PyObject *
@@ -2602,8 +3254,6 @@ list_index_impl(PyListObject *self, PyObject *value, Py_ssize_t start,
Py_ssize_t stop)
/*[clinic end generated code: output=ec51b88787e4e481 input=40ec5826303a0eb1]*/
{
- Py_ssize_t i;
-
if (start < 0) {
start += Py_SIZE(self);
if (start < 0)
@@ -2614,9 +3264,12 @@ list_index_impl(PyListObject *self, PyObject *value, Py_ssize_t start,
if (stop < 0)
stop = 0;
}
- for (i = start; i < stop && i < Py_SIZE(self); i++) {
- PyObject *obj = self->ob_item[i];
- Py_INCREF(obj);
+ for (Py_ssize_t i = start; i < stop; i++) {
+ PyObject *obj = list_get_item_ref(self, i);
+ if (obj == NULL) {
+ // out-of-bounds
+ break;
+ }
int cmp = PyObject_RichCompareBool(obj, value, Py_EQ);
Py_DECREF(obj);
if (cmp > 0)
@@ -2642,15 +3295,17 @@ list_count(PyListObject *self, PyObject *value)
/*[clinic end generated code: output=b1f5d284205ae714 input=3bdc3a5e6f749565]*/
{
Py_ssize_t count = 0;
- Py_ssize_t i;
-
- for (i = 0; i < Py_SIZE(self); i++) {
- PyObject *obj = self->ob_item[i];
+ for (Py_ssize_t i = 0; ; i++) {
+ PyObject *obj = list_get_item_ref(self, i);
+ if (obj == NULL) {
+ // out-of-bounds
+ break;
+ }
if (obj == value) {
count++;
+ Py_DECREF(obj);
continue;
}
- Py_INCREF(obj);
int cmp = PyObject_RichCompareBool(obj, value, Py_EQ);
Py_DECREF(obj);
if (cmp > 0)
@@ -2662,6 +3317,7 @@ list_count(PyListObject *self, PyObject *value)
}
/*[clinic input]
+@critical_section
list.remove
value: object
@@ -2673,8 +3329,8 @@ Raises ValueError if the value is not present.
[clinic start generated code]*/
static PyObject *
-list_remove(PyListObject *self, PyObject *value)
-/*[clinic end generated code: output=f087e1951a5e30d1 input=2dc2ba5bb2fb1f82]*/
+list_remove_impl(PyListObject *self, PyObject *value)
+/*[clinic end generated code: output=b9b76a6633b18778 input=26c813dbb95aa93b]*/
{
Py_ssize_t i;
@@ -2684,8 +3340,7 @@ list_remove(PyListObject *self, PyObject *value)
int cmp = PyObject_RichCompareBool(obj, value, Py_EQ);
Py_DECREF(obj);
if (cmp > 0) {
- if (list_ass_slice(self, i, i+1,
- (PyObject *)NULL) == 0)
+ if (list_ass_slice_lock_held(self, i, i+1, NULL) == 0)
Py_RETURN_NONE;
return NULL;
}
@@ -2697,8 +3352,9 @@ list_remove(PyListObject *self, PyObject *value)
}
static int
-list_traverse(PyListObject *o, visitproc visit, void *arg)
+list_traverse(PyObject *self, visitproc visit, void *arg)
{
+ PyListObject *o = (PyListObject *)self;
Py_ssize_t i;
for (i = Py_SIZE(o); --i >= 0; )
@@ -2707,7 +3363,7 @@ list_traverse(PyListObject *o, visitproc visit, void *arg)
}
static PyObject *
-list_richcompare(PyObject *v, PyObject *w, int op)
+list_richcompare_impl(PyObject *v, PyObject *w, int op)
{
PyListObject *vl, *wl;
Py_ssize_t i;
@@ -2769,6 +3425,16 @@ list_richcompare(PyObject *v, PyObject *w, int op)
return result;
}
+static PyObject *
+list_richcompare(PyObject *v, PyObject *w, int op)
+{
+ PyObject *ret;
+ Py_BEGIN_CRITICAL_SECTION2(v, w);
+ ret = list_richcompare_impl(v, w, op);
+ Py_END_CRITICAL_SECTION2()
+ return ret;
+}
+
/*[clinic input]
list.__init__
@@ -2793,13 +3459,14 @@ list___init___impl(PyListObject *self, PyObject *iterable)
/* Empty previous contents */
if (self->ob_item != NULL) {
- (void)_list_clear(self);
+ Py_BEGIN_CRITICAL_SECTION(self);
+ list_clear(self);
+ Py_END_CRITICAL_SECTION();
}
if (iterable != NULL) {
- PyObject *rv = list_extend(self, iterable);
- if (rv == NULL)
+ if (_list_extend(self, iterable) < 0) {
return -1;
- Py_DECREF(rv);
+ }
}
return 0;
}
@@ -2841,19 +3508,20 @@ list___sizeof___impl(PyListObject *self)
/*[clinic end generated code: output=3417541f95f9a53e input=b8030a5d5ce8a187]*/
{
size_t res = _PyObject_SIZE(Py_TYPE(self));
- res += (size_t)self->allocated * sizeof(void*);
+ Py_ssize_t allocated = FT_ATOMIC_LOAD_SSIZE_RELAXED(self->allocated);
+ res += (size_t)allocated * sizeof(void*);
return PyLong_FromSize_t(res);
}
static PyObject *list_iter(PyObject *seq);
-static PyObject *list_subscript(PyListObject*, PyObject*);
+static PyObject *list_subscript(PyObject*, PyObject*);
static PyMethodDef list_methods[] = {
- {"__getitem__", (PyCFunction)list_subscript, METH_O|METH_COEXIST,
+ {"__getitem__", list_subscript, METH_O|METH_COEXIST,
PyDoc_STR("__getitem__($self, index, /)\n--\n\nReturn self[index].")},
LIST___REVERSED___METHODDEF
LIST___SIZEOF___METHODDEF
- LIST_CLEAR_METHODDEF
+ PY_LIST_CLEAR_METHODDEF
LIST_COPY_METHODDEF
LIST_APPEND_METHODDEF
LIST_INSERT_METHODDEF
@@ -2869,21 +3537,61 @@ static PyMethodDef list_methods[] = {
};
static PySequenceMethods list_as_sequence = {
- (lenfunc)list_length, /* sq_length */
- (binaryfunc)list_concat, /* sq_concat */
- (ssizeargfunc)list_repeat, /* sq_repeat */
- (ssizeargfunc)list_item, /* sq_item */
+ list_length, /* sq_length */
+ list_concat, /* sq_concat */
+ list_repeat, /* sq_repeat */
+ list_item, /* sq_item */
0, /* sq_slice */
- (ssizeobjargproc)list_ass_item, /* sq_ass_item */
+ list_ass_item, /* sq_ass_item */
0, /* sq_ass_slice */
- (objobjproc)list_contains, /* sq_contains */
- (binaryfunc)list_inplace_concat, /* sq_inplace_concat */
- (ssizeargfunc)list_inplace_repeat, /* sq_inplace_repeat */
+ list_contains, /* sq_contains */
+ list_inplace_concat, /* sq_inplace_concat */
+ list_inplace_repeat, /* sq_inplace_repeat */
};
+static inline PyObject *
+list_slice_step_lock_held(PyListObject *a, Py_ssize_t start, Py_ssize_t step, Py_ssize_t len)
+{
+ PyListObject *np = (PyListObject *)list_new_prealloc(len);
+ if (np == NULL) {
+ return NULL;
+ }
+ size_t cur;
+ Py_ssize_t i;
+ PyObject **src = a->ob_item;
+ PyObject **dest = np->ob_item;
+ for (cur = start, i = 0; i < len;
+ cur += (size_t)step, i++) {
+ PyObject *v = src[cur];
+ dest[i] = Py_NewRef(v);
+ }
+ Py_SET_SIZE(np, len);
+ return (PyObject *)np;
+}
+
static PyObject *
-list_subscript(PyListObject* self, PyObject* item)
+list_slice_wrap(PyListObject *aa, Py_ssize_t start, Py_ssize_t stop, Py_ssize_t step)
{
+ PyObject *res = NULL;
+ Py_BEGIN_CRITICAL_SECTION(aa);
+ Py_ssize_t len = PySlice_AdjustIndices(Py_SIZE(aa), &start, &stop, step);
+ if (len <= 0) {
+ res = PyList_New(0);
+ }
+ else if (step == 1) {
+ res = list_slice_lock_held(aa, start, stop);
+ }
+ else {
+ res = list_slice_step_lock_held(aa, start, step, len);
+ }
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
+static PyObject *
+list_subscript(PyObject* _self, PyObject* item)
+{
+ PyListObject* self = (PyListObject*)_self;
if (_PyIndex_Check(item)) {
Py_ssize_t i;
i = PyNumber_AsSsize_t(item, PyExc_IndexError);
@@ -2891,41 +3599,14 @@ list_subscript(PyListObject* self, PyObject* item)
return NULL;
if (i < 0)
i += PyList_GET_SIZE(self);
- return list_item(self, i);
+ return list_item((PyObject *)self, i);
}
else if (PySlice_Check(item)) {
- Py_ssize_t start, stop, step, slicelength, i;
- size_t cur;
- PyObject* result;
- PyObject* it;
- PyObject **src, **dest;
-
+ Py_ssize_t start, stop, step;
if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
return NULL;
}
- slicelength = PySlice_AdjustIndices(Py_SIZE(self), &start, &stop,
- step);
-
- if (slicelength <= 0) {
- return PyList_New(0);
- }
- else if (step == 1) {
- return list_slice(self, start, stop);
- }
- else {
- result = list_new_prealloc(slicelength);
- if (!result) return NULL;
-
- src = self->ob_item;
- dest = ((PyListObject *)result)->ob_item;
- for (cur = start, i = 0; i < slicelength;
- cur += (size_t)step, i++) {
- it = Py_NewRef(src[cur]);
- dest[i] = it;
- }
- Py_SET_SIZE(result, slicelength);
- return result;
- }
+ return list_slice_wrap(self, start, stop, step);
}
else {
PyErr_Format(PyExc_TypeError,
@@ -2953,15 +3634,18 @@ adjust_slice_indexes(PyListObject *lst,
}
static int
-list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
+list_ass_subscript_lock_held(PyObject *_self, PyObject *item, PyObject *value)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(_self);
+
+ PyListObject *self = (PyListObject *)_self;
if (_PyIndex_Check(item)) {
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
if (i == -1 && PyErr_Occurred())
return -1;
if (i < 0)
i += PyList_GET_SIZE(self);
- return list_ass_item(self, i, value);
+ return list_ass_item_lock_held(self, i, value);
}
else if (PySlice_Check(item)) {
Py_ssize_t start, stop, step;
@@ -2981,7 +3665,7 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
step);
if (step == 1)
- return list_ass_slice(self, start, stop, value);
+ return list_ass_slice_lock_held(self, start, stop, value);
if (slicelength <= 0)
return 0;
@@ -3047,8 +3731,8 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
/* protect against a[::-1] = a */
if (self == (PyListObject*)value) {
- seq = list_slice((PyListObject*)value, 0,
- PyList_GET_SIZE(value));
+ seq = list_slice_lock_held((PyListObject *)value, 0,
+ Py_SIZE(value));
}
else {
seq = PySequence_Fast(value,
@@ -3062,7 +3746,7 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
step);
if (step == 1) {
- int res = list_ass_slice(self, start, stop, seq);
+ int res = list_ass_slice_lock_held(self, start, stop, seq);
Py_DECREF(seq);
return res;
}
@@ -3118,10 +3802,28 @@ list_ass_subscript(PyListObject* self, PyObject* item, PyObject* value)
}
}
+static int
+list_ass_subscript(PyObject *self, PyObject *item, PyObject *value)
+{
+ int res;
+#ifdef Py_GIL_DISABLED
+ if (PySlice_Check(item) && value != NULL && PyList_CheckExact(value)) {
+ Py_BEGIN_CRITICAL_SECTION2(self, value);
+ res = list_ass_subscript_lock_held(self, item, value);
+ Py_END_CRITICAL_SECTION2();
+ return res;
+ }
+#endif
+ Py_BEGIN_CRITICAL_SECTION(self);
+ res = list_ass_subscript_lock_held(self, item, value);
+ Py_END_CRITICAL_SECTION();
+ return res;
+}
+
static PyMappingMethods list_as_mapping = {
- (lenfunc)list_length,
- (binaryfunc)list_subscript,
- (objobjargproc)list_ass_subscript
+ list_length,
+ list_subscript,
+ list_ass_subscript
};
PyTypeObject PyList_Type = {
@@ -3129,12 +3831,12 @@ PyTypeObject PyList_Type = {
"list",
sizeof(PyListObject),
0,
- (destructor)list_dealloc, /* tp_dealloc */
+ list_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)list_repr, /* tp_repr */
+ list_repr, /* tp_repr */
0, /* tp_as_number */
&list_as_sequence, /* tp_as_sequence */
&list_as_mapping, /* tp_as_mapping */
@@ -3148,8 +3850,8 @@ PyTypeObject PyList_Type = {
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_LIST_SUBCLASS |
_Py_TPFLAGS_MATCH_SELF | Py_TPFLAGS_SEQUENCE, /* tp_flags */
list___init____doc__, /* tp_doc */
- (traverseproc)list_traverse, /* tp_traverse */
- (inquiry)_list_clear, /* tp_clear */
+ list_traverse, /* tp_traverse */
+ list_clear_slot, /* tp_clear */
list_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
list_iter, /* tp_iter */
@@ -3171,22 +3873,22 @@ PyTypeObject PyList_Type = {
/*********************** List Iterator **************************/
-static void listiter_dealloc(_PyListIterObject *);
-static int listiter_traverse(_PyListIterObject *, visitproc, void *);
-static PyObject *listiter_next(_PyListIterObject *);
-static PyObject *listiter_len(_PyListIterObject *, PyObject *);
+static void listiter_dealloc(PyObject *);
+static int listiter_traverse(PyObject *, visitproc, void *);
+static PyObject *listiter_next(PyObject *);
+static PyObject *listiter_len(PyObject *, PyObject *);
static PyObject *listiter_reduce_general(void *_it, int forward);
-static PyObject *listiter_reduce(_PyListIterObject *, PyObject *);
-static PyObject *listiter_setstate(_PyListIterObject *, PyObject *state);
+static PyObject *listiter_reduce(PyObject *, PyObject *);
+static PyObject *listiter_setstate(PyObject *, PyObject *state);
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
static PyMethodDef listiter_methods[] = {
- {"__length_hint__", (PyCFunction)listiter_len, METH_NOARGS, length_hint_doc},
- {"__reduce__", (PyCFunction)listiter_reduce, METH_NOARGS, reduce_doc},
- {"__setstate__", (PyCFunction)listiter_setstate, METH_O, setstate_doc},
+ {"__length_hint__", listiter_len, METH_NOARGS, length_hint_doc},
+ {"__reduce__", listiter_reduce, METH_NOARGS, reduce_doc},
+ {"__setstate__", listiter_setstate, METH_O, setstate_doc},
{NULL, NULL} /* sentinel */
};
@@ -3196,7 +3898,7 @@ PyTypeObject PyListIter_Type = {
sizeof(_PyListIterObject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)listiter_dealloc, /* tp_dealloc */
+ listiter_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
@@ -3213,12 +3915,12 @@ PyTypeObject PyListIter_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
- (traverseproc)listiter_traverse, /* tp_traverse */
+ listiter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
- (iternextfunc)listiter_next, /* tp_iternext */
+ listiter_next, /* tp_iternext */
listiter_methods, /* tp_methods */
0, /* tp_members */
};
@@ -3243,49 +3945,53 @@ list_iter(PyObject *seq)
}
static void
-listiter_dealloc(_PyListIterObject *it)
+listiter_dealloc(PyObject *self)
{
+ _PyListIterObject *it = (_PyListIterObject *)self;
_PyObject_GC_UNTRACK(it);
Py_XDECREF(it->it_seq);
PyObject_GC_Del(it);
}
static int
-listiter_traverse(_PyListIterObject *it, visitproc visit, void *arg)
+listiter_traverse(PyObject *it, visitproc visit, void *arg)
{
- Py_VISIT(it->it_seq);
+ Py_VISIT(((_PyListIterObject *)it)->it_seq);
return 0;
}
static PyObject *
-listiter_next(_PyListIterObject *it)
+listiter_next(PyObject *self)
{
- PyListObject *seq;
- PyObject *item;
-
- assert(it != NULL);
- seq = it->it_seq;
- if (seq == NULL)
+ _PyListIterObject *it = (_PyListIterObject *)self;
+ Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index);
+ if (index < 0) {
return NULL;
- assert(PyList_Check(seq));
-
- if (it->it_index < PyList_GET_SIZE(seq)) {
- item = PyList_GET_ITEM(seq, it->it_index);
- ++it->it_index;
- return Py_NewRef(item);
}
- it->it_seq = NULL;
- Py_DECREF(seq);
- return NULL;
+ PyObject *item = list_get_item_ref(it->it_seq, index);
+ if (item == NULL) {
+ // out-of-bounds
+ FT_ATOMIC_STORE_SSIZE_RELAXED(it->it_index, -1);
+#ifndef Py_GIL_DISABLED
+ PyListObject *seq = it->it_seq;
+ it->it_seq = NULL;
+ Py_DECREF(seq);
+#endif
+ return NULL;
+ }
+ FT_ATOMIC_STORE_SSIZE_RELAXED(it->it_index, index + 1);
+ return item;
}
static PyObject *
-listiter_len(_PyListIterObject *it, PyObject *Py_UNUSED(ignored))
+listiter_len(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- Py_ssize_t len;
- if (it->it_seq) {
- len = PyList_GET_SIZE(it->it_seq) - it->it_index;
+ assert(self != NULL);
+ _PyListIterObject *it = (_PyListIterObject *)self;
+ Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index);
+ if (index >= 0) {
+ Py_ssize_t len = PyList_GET_SIZE(it->it_seq) - index;
if (len >= 0)
return PyLong_FromSsize_t(len);
}
@@ -3293,20 +3999,21 @@ listiter_len(_PyListIterObject *it, PyObject *Py_UNUSED(ignored))
}
static PyObject *
-listiter_reduce(_PyListIterObject *it, PyObject *Py_UNUSED(ignored))
+listiter_reduce(PyObject *it, PyObject *Py_UNUSED(ignored))
{
return listiter_reduce_general(it, 1);
}
static PyObject *
-listiter_setstate(_PyListIterObject *it, PyObject *state)
+listiter_setstate(PyObject *self, PyObject *state)
{
+ _PyListIterObject *it = (_PyListIterObject *)self;
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
if (it->it_seq != NULL) {
- if (index < 0)
- index = 0;
+ if (index < -1)
+ index = -1;
else if (index > PyList_GET_SIZE(it->it_seq))
index = PyList_GET_SIZE(it->it_seq); /* iterator exhausted */
it->it_index = index;
@@ -3322,17 +4029,17 @@ typedef struct {
PyListObject *it_seq; /* Set to NULL when iterator is exhausted */
} listreviterobject;
-static void listreviter_dealloc(listreviterobject *);
-static int listreviter_traverse(listreviterobject *, visitproc, void *);
-static PyObject *listreviter_next(listreviterobject *);
-static PyObject *listreviter_len(listreviterobject *, PyObject *);
-static PyObject *listreviter_reduce(listreviterobject *, PyObject *);
-static PyObject *listreviter_setstate(listreviterobject *, PyObject *);
+static void listreviter_dealloc(PyObject *);
+static int listreviter_traverse(PyObject *, visitproc, void *);
+static PyObject *listreviter_next(PyObject *);
+static PyObject *listreviter_len(PyObject *, PyObject *);
+static PyObject *listreviter_reduce(PyObject *, PyObject *);
+static PyObject *listreviter_setstate(PyObject *, PyObject *);
static PyMethodDef listreviter_methods[] = {
- {"__length_hint__", (PyCFunction)listreviter_len, METH_NOARGS, length_hint_doc},
- {"__reduce__", (PyCFunction)listreviter_reduce, METH_NOARGS, reduce_doc},
- {"__setstate__", (PyCFunction)listreviter_setstate, METH_O, setstate_doc},
+ {"__length_hint__", listreviter_len, METH_NOARGS, length_hint_doc},
+ {"__reduce__", listreviter_reduce, METH_NOARGS, reduce_doc},
+ {"__setstate__", listreviter_setstate, METH_O, setstate_doc},
{NULL, NULL} /* sentinel */
};
@@ -3342,7 +4049,7 @@ PyTypeObject PyListRevIter_Type = {
sizeof(listreviterobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
- (destructor)listreviter_dealloc, /* tp_dealloc */
+ listreviter_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
@@ -3359,12 +4066,12 @@ PyTypeObject PyListRevIter_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
0, /* tp_doc */
- (traverseproc)listreviter_traverse, /* tp_traverse */
+ listreviter_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
- (iternextfunc)listreviter_next, /* tp_iternext */
+ listreviter_next, /* tp_iternext */
listreviter_methods, /* tp_methods */
0,
};
@@ -3392,64 +4099,67 @@ list___reversed___impl(PyListObject *self)
}
static void
-listreviter_dealloc(listreviterobject *it)
+listreviter_dealloc(PyObject *self)
{
+ listreviterobject *it = (listreviterobject *)self;
PyObject_GC_UnTrack(it);
Py_XDECREF(it->it_seq);
PyObject_GC_Del(it);
}
static int
-listreviter_traverse(listreviterobject *it, visitproc visit, void *arg)
+listreviter_traverse(PyObject *it, visitproc visit, void *arg)
{
- Py_VISIT(it->it_seq);
+ Py_VISIT(((listreviterobject *)it)->it_seq);
return 0;
}
static PyObject *
-listreviter_next(listreviterobject *it)
+listreviter_next(PyObject *self)
{
- PyObject *item;
- Py_ssize_t index;
- PyListObject *seq;
-
+ listreviterobject *it = (listreviterobject *)self;
assert(it != NULL);
- seq = it->it_seq;
- if (seq == NULL) {
+ Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index);
+ if (index < 0) {
return NULL;
}
- assert(PyList_Check(seq));
- index = it->it_index;
- if (index>=0 && index < PyList_GET_SIZE(seq)) {
- item = PyList_GET_ITEM(seq, index);
- it->it_index--;
- return Py_NewRef(item);
+ PyListObject *seq = it->it_seq;
+ assert(PyList_Check(seq));
+ PyObject *item = list_get_item_ref(seq, index);
+ if (item != NULL) {
+ FT_ATOMIC_STORE_SSIZE_RELAXED(it->it_index, index - 1);
+ return item;
}
- it->it_index = -1;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(it->it_index, -1);
+#ifndef Py_GIL_DISABLED
it->it_seq = NULL;
Py_DECREF(seq);
+#endif
return NULL;
}
static PyObject *
-listreviter_len(listreviterobject *it, PyObject *Py_UNUSED(ignored))
+listreviter_len(PyObject *self, PyObject *Py_UNUSED(ignored))
{
- Py_ssize_t len = it->it_index + 1;
+ listreviterobject *it = (listreviterobject *)self;
+ Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index);
+ Py_ssize_t len = index + 1;
if (it->it_seq == NULL || PyList_GET_SIZE(it->it_seq) < len)
len = 0;
return PyLong_FromSsize_t(len);
}
static PyObject *
-listreviter_reduce(listreviterobject *it, PyObject *Py_UNUSED(ignored))
+listreviter_reduce(PyObject *it, PyObject *Py_UNUSED(ignored))
{
return listiter_reduce_general(it, 0);
}
static PyObject *
-listreviter_setstate(listreviterobject *it, PyObject *state)
+listreviter_setstate(PyObject *self, PyObject *state)
{
+ listreviterobject *it = (listreviterobject *)self;
Py_ssize_t index = PyLong_AsSsize_t(state);
if (index == -1 && PyErr_Occurred())
return NULL;
@@ -3478,20 +4188,14 @@ listiter_reduce_general(void *_it, int forward)
/* the objects are not the same, index is of different types! */
if (forward) {
iter = _PyEval_GetBuiltin(&_Py_ID(iter));
- if (!iter) {
- return NULL;
- }
_PyListIterObject *it = (_PyListIterObject *)_it;
- if (it->it_seq) {
+ if (it->it_index >= 0) {
return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index);
}
} else {
iter = _PyEval_GetBuiltin(&_Py_ID(reversed));
- if (!iter) {
- return NULL;
- }
listreviterobject *it = (listreviterobject *)_it;
- if (it->it_seq) {
+ if (it->it_index >= 0) {
return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index);
}
}
diff --git a/contrib/tools/python3/Objects/longobject.c b/contrib/tools/python3/Objects/longobject.c
index c366034fe4b..4942c57f1ac 100644
--- a/contrib/tools/python3/Objects/longobject.c
+++ b/contrib/tools/python3/Objects/longobject.c
@@ -5,15 +5,14 @@
#include "Python.h"
#include "pycore_bitutils.h" // _Py_popcount32()
#include "pycore_initconfig.h" // _PyStatus_OK()
+#include "pycore_call.h" // _PyObject_MakeTpCall
#include "pycore_long.h" // _Py_SmallInts
#include "pycore_object.h" // _PyObject_Init()
#include "pycore_runtime.h" // _PY_NSMALLPOSINTS
#include "pycore_structseq.h" // _PyStructSequence_FiniBuiltin()
-#include <ctype.h>
-#include <float.h>
-#include <stddef.h>
-#include <stdlib.h> // abs()
+#include <float.h> // DBL_MANT_DIG
+#include <stddef.h> // offsetof
#include "clinic/longobject.c.h"
/*[clinic input]
@@ -23,7 +22,7 @@ class int "PyObject *" "&PyLong_Type"
#define medium_value(x) ((stwodigits)_PyLong_CompactValue(x))
-#define IS_SMALL_INT(ival) (-_PY_NSMALLNEGINTS <= (ival) && (ival) < _PY_NSMALLPOSINTS)
+#define IS_SMALL_INT(ival) _PY_IS_SMALL_INT(ival)
#define IS_SMALL_UINT(ival) ((ival) < _PY_NSMALLPOSINTS)
#define _MAX_STR_DIGITS_ERROR_FMT_TO_INT "Exceeds the limit (%d digits) for integer string conversion: value has %zd digits; use sys.set_int_max_str_digits() to increase the limit"
@@ -174,7 +173,7 @@ _PyLong_FromDigits(int negative, Py_ssize_t digit_count, digit *digits)
{
assert(digit_count >= 0);
if (digit_count == 0) {
- return (PyLongObject *)Py_NewRef(_PyLong_GetZero());
+ return (PyLongObject *)_PyLong_GetZero();
}
PyLongObject *result = _PyLong_New(digit_count);
if (result == NULL) {
@@ -556,7 +555,7 @@ PyLong_AsLong(PyObject *obj)
method. Return -1 and set an error if overflow occurs. */
int
-_PyLong_AsInt(PyObject *obj)
+PyLong_AsInt(PyObject *obj)
{
int overflow;
long result = PyLong_AsLongAndOverflow(obj, &overflow);
@@ -875,16 +874,9 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n,
++numsignificantbytes;
}
- /* How many Python int digits do we need? We have
- 8*numsignificantbytes bits, and each Python int digit has
- PyLong_SHIFT bits, so it's the ceiling of the quotient. */
- /* catch overflow before it happens */
- if (numsignificantbytes > (PY_SSIZE_T_MAX - PyLong_SHIFT) / 8) {
- PyErr_SetString(PyExc_OverflowError,
- "byte array too long to convert to int");
- return NULL;
- }
- ndigits = (numsignificantbytes * 8 + PyLong_SHIFT - 1) / PyLong_SHIFT;
+ /* avoid integer overflow */
+ ndigits = numsignificantbytes / PyLong_SHIFT * 8
+ + (numsignificantbytes % PyLong_SHIFT * 8 + PyLong_SHIFT - 1) / PyLong_SHIFT;
v = _PyLong_New(ndigits);
if (v == NULL)
return NULL;
@@ -941,7 +933,8 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n,
int
_PyLong_AsByteArray(PyLongObject* v,
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 ndigits; /* number of digits */
@@ -958,8 +951,10 @@ _PyLong_AsByteArray(PyLongObject* v,
ndigits = _PyLong_DigitCount(v);
if (_PyLong_IsNegative(v)) {
if (!is_signed) {
- PyErr_SetString(PyExc_OverflowError,
- "can't convert negative int to unsigned");
+ if (with_exceptions) {
+ PyErr_SetString(PyExc_OverflowError,
+ "can't convert negative int to unsigned");
+ }
return -1;
}
do_twos_comp = 1;
@@ -980,7 +975,12 @@ _PyLong_AsByteArray(PyLongObject* v,
/* Copy over all the Python digits.
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
- 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);
j = 0;
accum = 0;
@@ -1041,13 +1041,19 @@ _PyLong_AsByteArray(PyLongObject* v,
*p = (unsigned char)(accum & 0xff);
p += pincr;
}
- else if (j == n && n > 0 && is_signed) {
+ else if (j == n && is_signed) {
/* The main loop filled the byte array exactly, so the code
just above didn't get to ensure there's a sign bit, and the
loop below wouldn't add one either. Make sure a sign bit
exists. */
- unsigned char msb = *(p - pincr);
- int sign_bit_set = msb >= 0x80;
+ int sign_bit_set;
+ if (n > 0) {
+ unsigned char msb = *(p - pincr);
+ sign_bit_set = msb >= 0x80;
+ }
+ else {
+ sign_bit_set = 0;
+ }
assert(accumbits == 0);
if (sign_bit_set == do_twos_comp)
return 0;
@@ -1065,11 +1071,265 @@ _PyLong_AsByteArray(PyLongObject* v,
return 0;
Overflow:
- PyErr_SetString(PyExc_OverflowError, "int too big to convert");
+ if (with_exceptions) {
+ PyErr_SetString(PyExc_OverflowError, "int too big to convert");
+ }
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 == -1 || (*endianness & 2)) {
+ *endianness = PY_LITTLE_ENDIAN;
+ } else {
+ *endianness &= 1;
+ }
+ assert(*endianness == 0 || *endianness == 1);
+ return 0;
+}
+
+Py_ssize_t
+PyLong_AsNativeBytes(PyObject* vv, void* buffer, Py_ssize_t n, int flags)
+{
+ 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 = flags;
+ if (_resolve_endianness(&little_endian) < 0) {
+ return -1;
+ }
+
+ if (PyLong_Check(vv)) {
+ v = (PyLongObject *)vv;
+ }
+ else if (flags != -1 && (flags & Py_ASNATIVEBYTES_ALLOW_INDEX)) {
+ v = (PyLongObject *)_PyNumber_Index(vv);
+ if (v == NULL) {
+ return -1;
+ }
+ do_decref = 1;
+ }
+ else {
+ PyErr_Format(PyExc_TypeError, "expect int, got %T", vv);
+ return -1;
+ }
+
+ if ((flags != -1 && (flags & Py_ASNATIVEBYTES_REJECT_NEGATIVE))
+ && _PyLong_IsNegative(v)) {
+ PyErr_SetString(PyExc_ValueError, "Cannot convert negative int");
+ if (do_decref) {
+ Py_DECREF(v);
+ }
+ return -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 <= (Py_ssize_t)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 if (cv.v > 0 && _fits_in_n_bits(cv.v, n * 8 + 1)) {
+ /* Positive values with the MSB set do not require an
+ * additional bit when the caller's intent is to treat them
+ * as unsigned. */
+ if (flags == -1 || (flags & Py_ASNATIVEBYTES_UNSIGNED_BUFFER)) {
+ res = n;
+ } else {
+ res = n + 1;
+ }
+ }
+ }
+ 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 - (int)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);
+ }
+
+ /* Calculates the number of bits required for the *absolute* value
+ * of v. This does not take sign into account, only magnitude. */
+ size_t nb = _PyLong_NumBits((PyObject *)v);
+ if (nb == (size_t)-1) {
+ res = -1;
+ } else {
+ /* 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. */
+ res = (Py_ssize_t)(nb / 8) + 1;
+ }
+
+ /* Two edge cases exist that are best handled after extracting the
+ * bits. These may result in us reporting overflow when the value
+ * actually fits.
+ */
+ if (n > 0 && res == n + 1 && nb % 8 == 0) {
+ if (_PyLong_IsNegative(v)) {
+ /* Values of 0x80...00 from negative values that use every
+ * available bit in the buffer do not require an additional
+ * bit to store the sign. */
+ int is_edge_case = 1;
+ unsigned char *b = (unsigned char *)buffer;
+ for (Py_ssize_t i = 0; i < n && is_edge_case; ++i, ++b) {
+ if (i == 0) {
+ is_edge_case = (*b == (little_endian ? 0 : 0x80));
+ } else if (i < n - 1) {
+ is_edge_case = (*b == 0);
+ } else {
+ is_edge_case = (*b == (little_endian ? 0x80 : 0));
+ }
+ }
+ if (is_edge_case) {
+ res = n;
+ }
+ }
+ else {
+ /* Positive values with the MSB set do not require an
+ * additional bit when the caller's intent is to treat them
+ * as unsigned. */
+ unsigned char *b = (unsigned char *)buffer;
+ if (b[little_endian ? n - 1 : 0] & 0x80) {
+ if (flags == -1 || (flags & Py_ASNATIVEBYTES_UNSIGNED_BUFFER)) {
+ res = n;
+ } else {
+ res = n + 1;
+ }
+ }
+ }
+ }
+ }
+
+ if (do_decref) {
+ Py_DECREF(v);
+ }
+
+ return res;
+}
+
+
+PyObject *
+PyLong_FromNativeBytes(const void* buffer, size_t n, int flags)
+{
+ if (!buffer) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ int little_endian = flags;
+ if (_resolve_endianness(&little_endian) < 0) {
+ return NULL;
+ }
+
+ return _PyLong_FromByteArray(
+ (const unsigned char *)buffer,
+ n,
+ little_endian,
+ (flags == -1 || !(flags & Py_ASNATIVEBYTES_UNSIGNED_BUFFER)) ? 1 : 0
+ );
+}
+
+
+PyObject *
+PyLong_FromUnsignedNativeBytes(const void* buffer, size_t n, int flags)
+{
+ if (!buffer) {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+
+ int little_endian = flags;
+ 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 */
PyObject *
@@ -1244,7 +1504,7 @@ PyLong_AsLongLong(PyObject *vv)
}
else {
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) {
Py_DECREF(v);
@@ -1294,7 +1554,7 @@ PyLong_AsUnsignedLongLong(PyObject *vv)
}
else {
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 */
@@ -2797,11 +3057,11 @@ PyLong_FromString(const char *str, char **pend, int base)
}
/* Set sign and normalize */
- if (sign < 0) {
- _PyLong_FlipSign(z);
- }
long_normalize(z);
z = maybe_small_long(z);
+ if (sign < 0) {
+ _PyLong_Negate(&z);
+ }
if (pend != NULL) {
*pend = (char *)str;
@@ -2906,8 +3166,7 @@ long_divrem(PyLongObject *a, PyLongObject *b,
if (*prem == NULL) {
return -1;
}
- PyObject *zero = _PyLong_GetZero();
- *pdiv = (PyLongObject*)Py_NewRef(zero);
+ *pdiv = (PyLongObject*)_PyLong_GetZero();
return 0;
}
if (size_b == 1) {
@@ -3328,16 +3587,9 @@ long_dealloc(PyObject *self)
* we accidentally decref small Ints out of existence. Instead,
* since small Ints are immortal, re-set the reference count.
*/
- PyLongObject *pylong = (PyLongObject*)self;
- if (pylong && _PyLong_IsCompact(pylong)) {
- stwodigits ival = medium_value(pylong);
- if (IS_SMALL_INT(ival)) {
- PyLongObject *small_pylong = (PyLongObject *)get_small_int((sdigit)ival);
- if (pylong == small_pylong) {
- _Py_SetImmortal(self);
- return;
- }
- }
+ if (_PyLong_IsSmallInt((PyLongObject*)self)) {
+ _Py_SetImmortal(self);
+ return;
}
Py_TYPE(self)->tp_free(self);
}
@@ -4065,10 +4317,10 @@ pylong_int_divmod(PyLongObject *v, PyLongObject *w,
if (result == NULL) {
return -1;
}
- if (!PyTuple_Check(result)) {
+ if (!PyTuple_Check(result) || PyTuple_GET_SIZE(result) != 2) {
Py_DECREF(result);
PyErr_SetString(PyExc_ValueError,
- "tuple is required from int_divmod()");
+ "tuple of length 2 is required from int_divmod()");
return -1;
}
PyObject *q = PyTuple_GET_ITEM(result, 0);
@@ -6077,7 +6329,7 @@ int.to_bytes
the most significant byte is at the beginning of the byte array. If
byteorder is 'little', the most significant byte is at the end of the
byte array. To request the native byte order of the host system, use
- `sys.byteorder' as the byte order value. Default is to use 'big'.
+ sys.byteorder as the byte order value. Default is to use 'big'.
*
signed as is_signed: bool = False
Determines whether two's complement is used to represent the integer.
@@ -6090,7 +6342,7 @@ Return an array of bytes representing an integer.
static PyObject *
int_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder,
int is_signed)
-/*[clinic end generated code: output=89c801df114050a3 input=d42ecfb545039d71]*/
+/*[clinic end generated code: output=89c801df114050a3 input=a0103d0e9ad85c2b]*/
{
int little_endian;
PyObject *bytes;
@@ -6119,7 +6371,7 @@ int_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder,
if (_PyLong_AsByteArray((PyLongObject *)self,
(unsigned char *)PyBytes_AS_STRING(bytes),
- length, little_endian, is_signed) < 0) {
+ length, little_endian, is_signed, 1) < 0) {
Py_DECREF(bytes);
return NULL;
}
@@ -6141,7 +6393,7 @@ int.from_bytes
the most significant byte is at the beginning of the byte array. If
byteorder is 'little', the most significant byte is at the end of the
byte array. To request the native byte order of the host system, use
- `sys.byteorder' as the byte order value. Default is to use 'big'.
+ sys.byteorder as the byte order value. Default is to use 'big'.
*
signed as is_signed: bool = False
Indicates whether two's complement is used to represent the integer.
@@ -6152,7 +6404,7 @@ Return the integer represented by the given array of bytes.
static PyObject *
int_from_bytes_impl(PyTypeObject *type, PyObject *bytes_obj,
PyObject *byteorder, int is_signed)
-/*[clinic end generated code: output=efc5d68e31f9314f input=33326dccdd655553]*/
+/*[clinic end generated code: output=efc5d68e31f9314f input=2ff527997fe7b0c5]*/
{
int little_endian;
PyObject *long_obj, *bytes;
@@ -6204,6 +6456,29 @@ int_is_integer_impl(PyObject *self)
Py_RETURN_TRUE;
}
+static PyObject *
+long_vectorcall(PyObject *type, PyObject * const*args,
+ size_t nargsf, PyObject *kwnames)
+{
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (kwnames != NULL) {
+ PyThreadState *tstate = PyThreadState_GET();
+ return _PyObject_MakeTpCall(tstate, type, args, nargs, kwnames);
+ }
+ switch (nargs) {
+ case 0:
+ return _PyLong_GetZero();
+ case 1:
+ return PyNumber_Long(args[0]);
+ case 2:
+ return long_new_impl(_PyType_CAST(type), args[0], args[1]);
+ default:
+ return PyErr_Format(PyExc_TypeError,
+ "int expected at most 2 arguments, got %zd",
+ nargs);
+ }
+}
+
static PyMethodDef long_methods[] = {
{"conjugate", long_long_meth, METH_NOARGS,
"Returns self, the complex conjugate of any int."},
@@ -6341,6 +6616,7 @@ PyTypeObject PyLong_Type = {
0, /* tp_alloc */
long_new, /* tp_new */
PyObject_Free, /* tp_free */
+ .tp_vectorcall = long_vectorcall,
};
static PyTypeObject Int_InfoType;
diff --git a/contrib/tools/python3/Objects/memoryobject.c b/contrib/tools/python3/Objects/memoryobject.c
index 9a5f9c665b2..535e0b3c1dc 100644
--- a/contrib/tools/python3/Objects/memoryobject.c
+++ b/contrib/tools/python3/Objects/memoryobject.c
@@ -12,6 +12,7 @@
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
+#include "pycore_memoryobject.h" // _PyManagedBuffer_Type
#include "pycore_object.h" // _PyObject_GC_UNTRACK()
#include "pycore_strhex.h" // _Py_strhex_with_sep()
#include <stddef.h> // offsetof()
@@ -116,8 +117,9 @@ mbuf_release(_PyManagedBufferObject *self)
}
static void
-mbuf_dealloc(_PyManagedBufferObject *self)
+mbuf_dealloc(PyObject *_self)
{
+ _PyManagedBufferObject *self = (_PyManagedBufferObject *)_self;
assert(self->exports == 0);
mbuf_release(self);
if (self->flags&_Py_MANAGED_BUFFER_FREE_FORMAT)
@@ -126,15 +128,17 @@ mbuf_dealloc(_PyManagedBufferObject *self)
}
static int
-mbuf_traverse(_PyManagedBufferObject *self, visitproc visit, void *arg)
+mbuf_traverse(PyObject *_self, visitproc visit, void *arg)
{
+ _PyManagedBufferObject *self = (_PyManagedBufferObject *)_self;
Py_VISIT(self->master.obj);
return 0;
}
static int
-mbuf_clear(_PyManagedBufferObject *self)
+mbuf_clear(PyObject *_self)
{
+ _PyManagedBufferObject *self = (_PyManagedBufferObject *)_self;
assert(self->exports >= 0);
mbuf_release(self);
return 0;
@@ -145,7 +149,7 @@ PyTypeObject _PyManagedBuffer_Type = {
"managedbuffer",
sizeof(_PyManagedBufferObject),
0,
- (destructor)mbuf_dealloc, /* tp_dealloc */
+ mbuf_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
@@ -162,8 +166,8 @@ PyTypeObject _PyManagedBuffer_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
- (traverseproc)mbuf_traverse, /* tp_traverse */
- (inquiry)mbuf_clear /* tp_clear */
+ mbuf_traverse, /* tp_traverse */
+ mbuf_clear /* tp_clear */
};
@@ -1133,8 +1137,9 @@ memoryview_release_impl(PyMemoryViewObject *self)
}
static void
-memory_dealloc(PyMemoryViewObject *self)
+memory_dealloc(PyObject *_self)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
assert(self->exports == 0);
_PyObject_GC_UNTRACK(self);
_memory_release(self);
@@ -1145,15 +1150,17 @@ memory_dealloc(PyMemoryViewObject *self)
}
static int
-memory_traverse(PyMemoryViewObject *self, visitproc visit, void *arg)
+memory_traverse(PyObject *_self, visitproc visit, void *arg)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
Py_VISIT(self->mbuf);
return 0;
}
static int
-memory_clear(PyMemoryViewObject *self)
+memory_clear(PyObject *_self)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
if (self->exports == 0) {
_memory_release(self);
Py_CLEAR(self->mbuf);
@@ -1508,8 +1515,9 @@ memoryview_toreadonly_impl(PyMemoryViewObject *self)
/**************************************************************************/
static int
-memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
+memory_getbuf(PyObject *_self, Py_buffer *view, int flags)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
Py_buffer *base = &self->view;
int baseflags = self->flags;
@@ -1587,8 +1595,9 @@ memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
}
static void
-memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view)
+memory_releasebuf(PyObject *_self, Py_buffer *view)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
self->exports--;
return;
/* PyBuffer_Release() decrements view->obj after this function returns. */
@@ -1596,8 +1605,8 @@ memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view)
/* Buffer methods */
static PyBufferProcs memory_as_buffer = {
- (getbufferproc)memory_getbuf, /* bf_getbuffer */
- (releasebufferproc)memory_releasebuf, /* bf_releasebuffer */
+ memory_getbuf, /* bf_getbuffer */
+ memory_releasebuf, /* bf_releasebuffer */
};
@@ -2321,7 +2330,13 @@ memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep,
CHECK_RELEASED(self);
if (MV_C_CONTIGUOUS(self->flags)) {
- return _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
+ // Prevent 'self' from being freed if computing len(sep) mutates 'self'
+ // in _Py_strhex_with_sep().
+ // See: https://github.com/python/cpython/issues/143195.
+ self->exports++;
+ PyObject *ret = _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
+ self->exports--;
+ return ret;
}
bytes = PyBytes_FromStringAndSize(NULL, src->len);
@@ -2342,8 +2357,9 @@ memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep,
}
static PyObject *
-memory_repr(PyMemoryViewObject *self)
+memory_repr(PyObject *_self)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
if (self->flags & _Py_MEMORYVIEW_RELEASED)
return PyUnicode_FromFormat("<released memory at %p>", self);
else
@@ -2397,7 +2413,7 @@ ptr_from_tuple(const Py_buffer *view, PyObject *tup)
if (nindices > view->ndim) {
PyErr_Format(PyExc_TypeError,
- "cannot index %zd-dimension view with %zd-element tuple",
+ "cannot index %d-dimension view with %zd-element tuple",
view->ndim, nindices);
return NULL;
}
@@ -2419,8 +2435,9 @@ ptr_from_tuple(const Py_buffer *view, PyObject *tup)
with the type specified by view->format. Otherwise, the item is a sub-view.
The function is used in memory_subscript() and memory_as_sequence. */
static PyObject *
-memory_item(PyMemoryViewObject *self, Py_ssize_t index)
+memory_item(PyObject *_self, Py_ssize_t index)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
Py_buffer *view = &(self->view);
const char *fmt;
@@ -2544,8 +2561,9 @@ is_multiindex(PyObject *key)
0-d memoryview objects can be referenced using mv[...] or mv[()]
but not with anything else. */
static PyObject *
-memory_subscript(PyMemoryViewObject *self, PyObject *key)
+memory_subscript(PyObject *_self, PyObject *key)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
Py_buffer *view;
view = &(self->view);
@@ -2573,7 +2591,7 @@ memory_subscript(PyMemoryViewObject *self, PyObject *key)
index = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (index == -1 && PyErr_Occurred())
return NULL;
- return memory_item(self, index);
+ return memory_item((PyObject *)self, index);
}
else if (PySlice_Check(key)) {
CHECK_RESTRICTED(self);
@@ -2606,8 +2624,9 @@ memory_subscript(PyMemoryViewObject *self, PyObject *key)
}
static int
-memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
+memory_ass_sub(PyObject *_self, PyObject *key, PyObject *value)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
Py_buffer *view = &(self->view);
Py_buffer src;
const char *fmt;
@@ -2708,8 +2727,9 @@ memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
}
static Py_ssize_t
-memory_length(PyMemoryViewObject *self)
+memory_length(PyObject *_self)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED_INT(self);
if (self->view.ndim == 0) {
PyErr_SetString(PyExc_TypeError, "0-dim memory has no length");
@@ -2720,17 +2740,17 @@ memory_length(PyMemoryViewObject *self)
/* As mapping */
static PyMappingMethods memory_as_mapping = {
- (lenfunc)memory_length, /* mp_length */
- (binaryfunc)memory_subscript, /* mp_subscript */
- (objobjargproc)memory_ass_sub, /* mp_ass_subscript */
+ memory_length, /* mp_length */
+ memory_subscript, /* mp_subscript */
+ memory_ass_sub, /* mp_ass_subscript */
};
/* As sequence */
static PySequenceMethods memory_as_sequence = {
- (lenfunc)memory_length, /* sq_length */
+ memory_length, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
- (ssizeargfunc)memory_item, /* sq_item */
+ memory_item, /* sq_item */
};
@@ -3032,8 +3052,9 @@ result:
/**************************************************************************/
static Py_hash_t
-memory_hash(PyMemoryViewObject *self)
+memory_hash(PyObject *_self)
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
if (self->hash == -1) {
Py_buffer *view = &self->view;
char *mem = view->buf;
@@ -3053,9 +3074,16 @@ memory_hash(PyMemoryViewObject *self)
"memoryview: hashing is restricted to formats 'B', 'b' or 'c'");
return -1;
}
- if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
- /* Keep the original error message */
- return -1;
+ if (view->obj != NULL) {
+ // Prevent 'self' from being freed when computing the item's hash.
+ // See https://github.com/python/cpython/issues/142664.
+ self->exports++;
+ Py_hash_t h = PyObject_Hash(view->obj);
+ self->exports--;
+ if (h == -1) {
+ /* Keep the original error message */
+ return -1;
+ }
}
if (!MV_C_CONTIGUOUS(self->flags)) {
@@ -3110,8 +3138,9 @@ _IntTupleFromSsizet(int len, Py_ssize_t *vals)
}
static PyObject *
-memory_obj_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_obj_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
Py_buffer *view = &self->view;
CHECK_RELEASED(self);
@@ -3122,78 +3151,89 @@ memory_obj_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
}
static PyObject *
-memory_nbytes_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_nbytes_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return PyLong_FromSsize_t(self->view.len);
}
static PyObject *
-memory_format_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_format_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return PyUnicode_FromString(self->view.format);
}
static PyObject *
-memory_itemsize_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_itemsize_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return PyLong_FromSsize_t(self->view.itemsize);
}
static PyObject *
-memory_shape_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_shape_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return _IntTupleFromSsizet(self->view.ndim, self->view.shape);
}
static PyObject *
-memory_strides_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_strides_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return _IntTupleFromSsizet(self->view.ndim, self->view.strides);
}
static PyObject *
-memory_suboffsets_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_suboffsets_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets);
}
static PyObject *
-memory_readonly_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_readonly_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return PyBool_FromLong(self->view.readonly);
}
static PyObject *
-memory_ndim_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
+memory_ndim_get(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return PyLong_FromLong(self->view.ndim);
}
static PyObject *
-memory_c_contiguous(PyMemoryViewObject *self, PyObject *dummy)
+memory_c_contiguous(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return PyBool_FromLong(MV_C_CONTIGUOUS(self->flags));
}
static PyObject *
-memory_f_contiguous(PyMemoryViewObject *self, PyObject *dummy)
+memory_f_contiguous(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return PyBool_FromLong(MV_F_CONTIGUOUS(self->flags));
}
static PyObject *
-memory_contiguous(PyMemoryViewObject *self, PyObject *dummy)
+memory_contiguous(PyObject *_self, void *Py_UNUSED(ignored))
{
+ PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
CHECK_RELEASED(self);
return PyBool_FromLong(MV_ANY_CONTIGUOUS(self->flags));
}
@@ -3227,21 +3267,24 @@ PyDoc_STRVAR(memory_f_contiguous_doc,
"A bool indicating whether the memory is Fortran contiguous.");
PyDoc_STRVAR(memory_contiguous_doc,
"A bool indicating whether the memory is contiguous.");
+PyDoc_STRVAR(memory_exit_doc,
+ "__exit__($self, /, *exc_info)\n--\n\n"
+ "Release the underlying buffer exposed by the memoryview object.");
static PyGetSetDef memory_getsetlist[] = {
- {"obj", (getter)memory_obj_get, NULL, memory_obj_doc},
- {"nbytes", (getter)memory_nbytes_get, NULL, memory_nbytes_doc},
- {"readonly", (getter)memory_readonly_get, NULL, memory_readonly_doc},
- {"itemsize", (getter)memory_itemsize_get, NULL, memory_itemsize_doc},
- {"format", (getter)memory_format_get, NULL, memory_format_doc},
- {"ndim", (getter)memory_ndim_get, NULL, memory_ndim_doc},
- {"shape", (getter)memory_shape_get, NULL, memory_shape_doc},
- {"strides", (getter)memory_strides_get, NULL, memory_strides_doc},
- {"suboffsets", (getter)memory_suboffsets_get, NULL, memory_suboffsets_doc},
- {"c_contiguous", (getter)memory_c_contiguous, NULL, memory_c_contiguous_doc},
- {"f_contiguous", (getter)memory_f_contiguous, NULL, memory_f_contiguous_doc},
- {"contiguous", (getter)memory_contiguous, NULL, memory_contiguous_doc},
+ {"obj", memory_obj_get, NULL, memory_obj_doc},
+ {"nbytes", memory_nbytes_get, NULL, memory_nbytes_doc},
+ {"readonly", memory_readonly_get, NULL, memory_readonly_doc},
+ {"itemsize", memory_itemsize_get, NULL, memory_itemsize_doc},
+ {"format", memory_format_get, NULL, memory_format_doc},
+ {"ndim", memory_ndim_get, NULL, memory_ndim_doc},
+ {"shape", memory_shape_get, NULL, memory_shape_doc},
+ {"strides", memory_strides_get, NULL, memory_strides_doc},
+ {"suboffsets", memory_suboffsets_get, NULL, memory_suboffsets_doc},
+ {"c_contiguous", memory_c_contiguous, NULL, memory_c_contiguous_doc},
+ {"f_contiguous", memory_f_contiguous, NULL, memory_f_contiguous_doc},
+ {"contiguous", memory_contiguous, NULL, memory_contiguous_doc},
{NULL, NULL, NULL, NULL},
};
@@ -3255,7 +3298,7 @@ static PyMethodDef memory_methods[] = {
MEMORYVIEW_TOREADONLY_METHODDEF
MEMORYVIEW__FROM_FLAGS_METHODDEF
{"__enter__", memory_enter, METH_NOARGS, NULL},
- {"__exit__", memory_exit, METH_VARARGS, NULL},
+ {"__exit__", memory_exit, METH_VARARGS, memory_exit_doc},
{NULL, NULL}
};
@@ -3274,23 +3317,26 @@ typedef struct {
} memoryiterobject;
static void
-memoryiter_dealloc(memoryiterobject *it)
+memoryiter_dealloc(PyObject *self)
{
+ memoryiterobject *it = (memoryiterobject *)self;
_PyObject_GC_UNTRACK(it);
Py_XDECREF(it->it_seq);
PyObject_GC_Del(it);
}
static int
-memoryiter_traverse(memoryiterobject *it, visitproc visit, void *arg)
+memoryiter_traverse(PyObject *self, visitproc visit, void *arg)
{
+ memoryiterobject *it = (memoryiterobject *)self;
Py_VISIT(it->it_seq);
return 0;
}
static PyObject *
-memoryiter_next(memoryiterobject *it)
+memoryiter_next(PyObject *self)
{
+ memoryiterobject *it = (memoryiterobject *)self;
PyMemoryViewObject *seq;
seq = it->it_seq;
if (seq == NULL) {
@@ -3346,7 +3392,7 @@ memory_iter(PyObject *seq)
return NULL;
}
it->it_fmt = fmt;
- it->it_length = memory_length(obj);
+ it->it_length = memory_length((PyObject *)obj);
it->it_index = 0;
it->it_seq = (PyMemoryViewObject*)Py_NewRef(obj);
_PyObject_GC_TRACK(it);
@@ -3358,12 +3404,12 @@ PyTypeObject _PyMemoryIter_Type = {
.tp_name = "memory_iterator",
.tp_basicsize = sizeof(memoryiterobject),
// methods
- .tp_dealloc = (destructor)memoryiter_dealloc,
+ .tp_dealloc = memoryiter_dealloc,
.tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
- .tp_traverse = (traverseproc)memoryiter_traverse,
+ .tp_traverse = memoryiter_traverse,
.tp_iter = PyObject_SelfIter,
- .tp_iternext = (iternextfunc)memoryiter_next,
+ .tp_iternext = memoryiter_next,
};
PyTypeObject PyMemoryView_Type = {
@@ -3371,16 +3417,16 @@ PyTypeObject PyMemoryView_Type = {
"memoryview", /* tp_name */
offsetof(PyMemoryViewObject, ob_array), /* tp_basicsize */
sizeof(Py_ssize_t), /* tp_itemsize */
- (destructor)memory_dealloc, /* tp_dealloc */
+ memory_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)memory_repr, /* tp_repr */
+ memory_repr, /* tp_repr */
0, /* tp_as_number */
&memory_as_sequence, /* tp_as_sequence */
&memory_as_mapping, /* tp_as_mapping */
- (hashfunc)memory_hash, /* tp_hash */
+ memory_hash, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
@@ -3389,8 +3435,8 @@ PyTypeObject PyMemoryView_Type = {
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_SEQUENCE, /* tp_flags */
memoryview__doc__, /* tp_doc */
- (traverseproc)memory_traverse, /* tp_traverse */
- (inquiry)memory_clear, /* tp_clear */
+ memory_traverse, /* tp_traverse */
+ memory_clear, /* tp_clear */
memory_richcompare, /* tp_richcompare */
offsetof(PyMemoryViewObject, weakreflist),/* tp_weaklistoffset */
memory_iter, /* tp_iter */
diff --git a/contrib/tools/python3/Objects/methodobject.c b/contrib/tools/python3/Objects/methodobject.c
index 44735045163..8fe4c0e0d4a 100644
--- a/contrib/tools/python3/Objects/methodobject.c
+++ b/contrib/tools/python3/Objects/methodobject.c
@@ -2,11 +2,13 @@
/* Method object implementation */
#include "Python.h"
+#include "pycore_call.h" // _Py_CheckFunctionResult()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
#include "pycore_object.h"
#include "pycore_pyerrors.h"
#include "pycore_pystate.h" // _PyThreadState_GET()
-#include "structmember.h" // PyMemberDef
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
+
/* undefine macro trampoline to PyCFunction_NewEx */
#undef PyCFunction_New
@@ -161,9 +163,7 @@ meth_dealloc(PyCFunctionObject *m)
// call PyObject_GC_UnTrack twice on an object.
PyObject_GC_UnTrack(m);
Py_TRASHCAN_BEGIN(m, meth_dealloc);
- if (m->m_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject*) m);
- }
+ FT_CLEAR_WEAKREFS((PyObject*) m, m->m_weakreflist);
// Dereference class before m_self: PyCFunction_GET_CLASS accesses
// PyMethodDef m_ml, which could be kept alive by m_self
Py_XDECREF(PyCFunction_GET_CLASS(m));
@@ -191,7 +191,9 @@ static PyMethodDef meth_methods[] = {
static PyObject *
meth_get__text_signature__(PyCFunctionObject *m, void *closure)
{
- return _PyType_GetTextSignatureFromInternalDoc(m->m_ml->ml_name, m->m_ml->ml_doc);
+ return _PyType_GetTextSignatureFromInternalDoc(m->m_ml->ml_name,
+ m->m_ml->ml_doc,
+ m->m_ml->ml_flags);
}
static PyObject *
@@ -272,7 +274,7 @@ static PyGetSetDef meth_getsets [] = {
#define OFF(x) offsetof(PyCFunctionObject, x)
static PyMemberDef meth_members[] = {
- {"__module__", T_OBJECT, OFF(m_module), 0},
+ {"__module__", _Py_T_OBJECT, OFF(m_module), 0},
{NULL}
};
@@ -317,7 +319,7 @@ static Py_hash_t
meth_hash(PyCFunctionObject *a)
{
Py_hash_t x, y;
- x = _Py_HashPointer(a->m_self);
+ x = PyObject_GenericHash(a->m_self);
y = _Py_HashPointer((void*)(a->m_ml->ml_meth));
x ^= y;
if (x == -1)
@@ -414,7 +416,7 @@ cfunction_vectorcall_FASTCALL(
return NULL;
}
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
- _PyCFunctionFast meth = (_PyCFunctionFast)
+ PyCFunctionFast meth = (PyCFunctionFast)
cfunction_enter_call(tstate, func);
if (meth == NULL) {
return NULL;
@@ -430,7 +432,7 @@ cfunction_vectorcall_FASTCALL_KEYWORDS(
{
PyThreadState *tstate = _PyThreadState_GET();
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
- _PyCFunctionFastWithKeywords meth = (_PyCFunctionFastWithKeywords)
+ PyCFunctionFastWithKeywords meth = (PyCFunctionFastWithKeywords)
cfunction_enter_call(tstate, func);
if (meth == NULL) {
return NULL;
@@ -549,11 +551,3 @@ cfunction_call(PyObject *func, PyObject *args, PyObject *kwargs)
}
return _Py_CheckFunctionResult(tstate, func, result, NULL);
}
-
-#if defined(__EMSCRIPTEN__) && defined(PY_CALL_TRAMPOLINE)
-#error #include <emscripten.h>
-
-EM_JS(PyObject*, _PyCFunctionWithKeywords_TrampolineCall, (PyCFunctionWithKeywords func, PyObject *self, PyObject *args, PyObject *kw), {
- return wasmTable.get(func)(self, args, kw);
-});
-#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/alloc-aligned.c b/contrib/tools/python3/Objects/mimalloc/alloc-aligned.c
new file mode 100644
index 00000000000..4c15f4043ec
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/alloc-aligned.c
@@ -0,0 +1,298 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/prim.h" // mi_prim_get_default_heap
+
+#include <string.h> // memset
+
+// ------------------------------------------------------
+// Aligned Allocation
+// ------------------------------------------------------
+
+// Fallback primitive aligned allocation -- split out for better codegen
+static mi_decl_noinline void* mi_heap_malloc_zero_aligned_at_fallback(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept
+{
+ mi_assert_internal(size <= PTRDIFF_MAX);
+ mi_assert_internal(alignment != 0 && _mi_is_power_of_two(alignment));
+
+ const uintptr_t align_mask = alignment - 1; // for any x, `(x & align_mask) == (x % alignment)`
+ const size_t padsize = size + MI_PADDING_SIZE;
+
+ // use regular allocation if it is guaranteed to fit the alignment constraints
+ if (offset==0 && alignment<=padsize && padsize<=MI_MAX_ALIGN_GUARANTEE && (padsize&align_mask)==0) {
+ void* p = _mi_heap_malloc_zero(heap, size, zero);
+ mi_assert_internal(p == NULL || ((uintptr_t)p % alignment) == 0);
+ return p;
+ }
+
+ void* p;
+ size_t oversize;
+ if mi_unlikely(alignment > MI_ALIGNMENT_MAX) {
+ // use OS allocation for very large alignment and allocate inside a huge page (dedicated segment with 1 page)
+ // This can support alignments >= MI_SEGMENT_SIZE by ensuring the object can be aligned at a point in the
+ // first (and single) page such that the segment info is `MI_SEGMENT_SIZE` bytes before it (so it can be found by aligning the pointer down)
+ if mi_unlikely(offset != 0) {
+ // todo: cannot support offset alignment for very large alignments yet
+ #if MI_DEBUG > 0
+ _mi_error_message(EOVERFLOW, "aligned allocation with a very large alignment cannot be used with an alignment offset (size %zu, alignment %zu, offset %zu)\n", size, alignment, offset);
+ #endif
+ return NULL;
+ }
+ oversize = (size <= MI_SMALL_SIZE_MAX ? MI_SMALL_SIZE_MAX + 1 /* ensure we use generic malloc path */ : size);
+ p = _mi_heap_malloc_zero_ex(heap, oversize, false, alignment); // the page block size should be large enough to align in the single huge page block
+ // zero afterwards as only the area from the aligned_p may be committed!
+ if (p == NULL) return NULL;
+ }
+ else {
+ // otherwise over-allocate
+ oversize = size + alignment - 1;
+ p = _mi_heap_malloc_zero(heap, oversize, zero);
+ if (p == NULL) return NULL;
+ }
+
+ // .. and align within the allocation
+ const uintptr_t poffset = ((uintptr_t)p + offset) & align_mask;
+ const uintptr_t adjust = (poffset == 0 ? 0 : alignment - poffset);
+ mi_assert_internal(adjust < alignment);
+ void* aligned_p = (void*)((uintptr_t)p + adjust);
+ if (aligned_p != p) {
+ mi_page_t* page = _mi_ptr_page(p);
+ mi_page_set_has_aligned(page, true);
+ _mi_padding_shrink(page, (mi_block_t*)p, adjust + size);
+ }
+ // todo: expand padding if overallocated ?
+
+ mi_assert_internal(mi_page_usable_block_size(_mi_ptr_page(p)) >= adjust + size);
+ mi_assert_internal(p == _mi_page_ptr_unalign(_mi_ptr_segment(aligned_p), _mi_ptr_page(aligned_p), aligned_p));
+ mi_assert_internal(((uintptr_t)aligned_p + offset) % alignment == 0);
+ mi_assert_internal(mi_usable_size(aligned_p)>=size);
+ mi_assert_internal(mi_usable_size(p) == mi_usable_size(aligned_p)+adjust);
+
+ // now zero the block if needed
+ if (alignment > MI_ALIGNMENT_MAX) {
+ // for the tracker, on huge aligned allocations only from the start of the large block is defined
+ mi_track_mem_undefined(aligned_p, size);
+ if (zero) {
+ _mi_memzero_aligned(aligned_p, mi_usable_size(aligned_p));
+ }
+ }
+
+ if (p != aligned_p) {
+ mi_track_align(p,aligned_p,adjust,mi_usable_size(aligned_p));
+ }
+ return aligned_p;
+}
+
+// Primitive aligned allocation
+static void* mi_heap_malloc_zero_aligned_at(mi_heap_t* const heap, const size_t size, const size_t alignment, const size_t offset, const bool zero) mi_attr_noexcept
+{
+ // note: we don't require `size > offset`, we just guarantee that the address at offset is aligned regardless of the allocated size.
+ if mi_unlikely(alignment == 0 || !_mi_is_power_of_two(alignment)) { // require power-of-two (see <https://en.cppreference.com/w/c/memory/aligned_alloc>)
+ #if MI_DEBUG > 0
+ _mi_error_message(EOVERFLOW, "aligned allocation requires the alignment to be a power-of-two (size %zu, alignment %zu)\n", size, alignment);
+ #endif
+ return NULL;
+ }
+
+ if mi_unlikely(size > PTRDIFF_MAX) { // we don't allocate more than PTRDIFF_MAX (see <https://sourceware.org/ml/libc-announce/2019/msg00001.html>)
+ #if MI_DEBUG > 0
+ _mi_error_message(EOVERFLOW, "aligned allocation request is too large (size %zu, alignment %zu)\n", size, alignment);
+ #endif
+ return NULL;
+ }
+ const uintptr_t align_mask = alignment-1; // for any x, `(x & align_mask) == (x % alignment)`
+ const size_t padsize = size + MI_PADDING_SIZE; // note: cannot overflow due to earlier size > PTRDIFF_MAX check
+
+ // try first if there happens to be a small block available with just the right alignment
+ if mi_likely(padsize <= MI_SMALL_SIZE_MAX && alignment <= padsize) {
+ mi_page_t* page = _mi_heap_get_free_small_page(heap, padsize);
+ const bool is_aligned = (((uintptr_t)page->free+offset) & align_mask)==0;
+ if mi_likely(page->free != NULL && is_aligned)
+ {
+ #if MI_STAT>1
+ mi_heap_stat_increase(heap, malloc, size);
+ #endif
+ void* p = _mi_page_malloc(heap, page, padsize, zero); // TODO: inline _mi_page_malloc
+ mi_assert_internal(p != NULL);
+ mi_assert_internal(((uintptr_t)p + offset) % alignment == 0);
+ mi_track_malloc(p,size,zero);
+ return p;
+ }
+ }
+ // fallback
+ return mi_heap_malloc_zero_aligned_at_fallback(heap, size, alignment, offset, zero);
+}
+
+
+// ------------------------------------------------------
+// Optimized mi_heap_malloc_aligned / mi_malloc_aligned
+// ------------------------------------------------------
+
+mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, false);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_heap_malloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept {
+ if mi_unlikely(alignment == 0 || !_mi_is_power_of_two(alignment)) return NULL;
+ #if !MI_PADDING
+ // without padding, any small sized allocation is naturally aligned (see also `_mi_segment_page_start`)
+ if mi_likely(_mi_is_power_of_two(size) && size >= alignment && size <= MI_SMALL_SIZE_MAX)
+ #else
+ // with padding, we can only guarantee this for fixed alignments
+ if mi_likely((alignment == sizeof(void*) || (alignment == MI_MAX_ALIGN_SIZE && size > (MI_MAX_ALIGN_SIZE/2)))
+ && size <= MI_SMALL_SIZE_MAX)
+ #endif
+ {
+ // fast path for common alignment and size
+ return mi_heap_malloc_small(heap, size);
+ }
+ else {
+ return mi_heap_malloc_aligned_at(heap, size, alignment, 0);
+ }
+}
+
+// ensure a definition is emitted
+#if defined(__cplusplus)
+static void* _mi_heap_malloc_aligned = (void*)&mi_heap_malloc_aligned;
+#endif
+
+// ------------------------------------------------------
+// Aligned Allocation
+// ------------------------------------------------------
+
+mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned_at(mi_heap_t* heap, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_malloc_zero_aligned_at(heap, size, alignment, offset, true);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_heap_zalloc_aligned(mi_heap_t* heap, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_zalloc_aligned_at(heap, size, alignment, 0);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned_at(mi_heap_t* heap, size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count, size, &total)) return NULL;
+ return mi_heap_zalloc_aligned_at(heap, total, alignment, offset);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_heap_calloc_aligned(mi_heap_t* heap, size_t count, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_calloc_aligned_at(heap,count,size,alignment,0);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_malloc_aligned_at(mi_prim_get_default_heap(), size, alignment, offset);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_malloc_aligned(size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_malloc_aligned(mi_prim_get_default_heap(), size, alignment);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_zalloc_aligned_at(mi_prim_get_default_heap(), size, alignment, offset);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_aligned(size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_zalloc_aligned(mi_prim_get_default_heap(), size, alignment);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_calloc_aligned_at(mi_prim_get_default_heap(), count, size, alignment, offset);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_calloc_aligned(size_t count, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_calloc_aligned(mi_prim_get_default_heap(), count, size, alignment);
+}
+
+
+// ------------------------------------------------------
+// Aligned re-allocation
+// ------------------------------------------------------
+
+static void* mi_heap_realloc_zero_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset, bool zero) mi_attr_noexcept {
+ mi_assert(alignment > 0);
+ if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero);
+ if (p == NULL) return mi_heap_malloc_zero_aligned_at(heap,newsize,alignment,offset,zero);
+ size_t size = mi_usable_size(p);
+ if (newsize <= size && newsize >= (size - (size / 2))
+ && (((uintptr_t)p + offset) % alignment) == 0) {
+ return p; // reallocation still fits, is aligned and not more than 50% waste
+ }
+ else {
+ // note: we don't zero allocate upfront so we only zero initialize the expanded part
+ void* newp = mi_heap_malloc_aligned_at(heap,newsize,alignment,offset);
+ if (newp != NULL) {
+ if (zero && newsize > size) {
+ // also set last word in the previous allocation to zero to ensure any padding is zero-initialized
+ size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0);
+ _mi_memzero((uint8_t*)newp + start, newsize - start);
+ }
+ _mi_memcpy_aligned(newp, p, (newsize > size ? size : newsize));
+ mi_free(p); // only free if successful
+ }
+ return newp;
+ }
+}
+
+static void* mi_heap_realloc_zero_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, bool zero) mi_attr_noexcept {
+ mi_assert(alignment > 0);
+ if (alignment <= sizeof(uintptr_t)) return _mi_heap_realloc_zero(heap,p,newsize,zero);
+ size_t offset = ((uintptr_t)p % alignment); // use offset of previous allocation (p can be NULL)
+ return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,zero);
+}
+
+mi_decl_nodiscard void* mi_heap_realloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_realloc_zero_aligned_at(heap,p,newsize,alignment,offset,false);
+}
+
+mi_decl_nodiscard void* mi_heap_realloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
+ return mi_heap_realloc_zero_aligned(heap,p,newsize,alignment,false);
+}
+
+mi_decl_nodiscard void* mi_heap_rezalloc_aligned_at(mi_heap_t* heap, void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_realloc_zero_aligned_at(heap, p, newsize, alignment, offset, true);
+}
+
+mi_decl_nodiscard void* mi_heap_rezalloc_aligned(mi_heap_t* heap, void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
+ return mi_heap_realloc_zero_aligned(heap, p, newsize, alignment, true);
+}
+
+mi_decl_nodiscard void* mi_heap_recalloc_aligned_at(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(newcount, size, &total)) return NULL;
+ return mi_heap_rezalloc_aligned_at(heap, p, total, alignment, offset);
+}
+
+mi_decl_nodiscard void* mi_heap_recalloc_aligned(mi_heap_t* heap, void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(newcount, size, &total)) return NULL;
+ return mi_heap_rezalloc_aligned(heap, p, total, alignment);
+}
+
+mi_decl_nodiscard void* mi_realloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_realloc_aligned_at(mi_prim_get_default_heap(), p, newsize, alignment, offset);
+}
+
+mi_decl_nodiscard void* mi_realloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
+ return mi_heap_realloc_aligned(mi_prim_get_default_heap(), p, newsize, alignment);
+}
+
+mi_decl_nodiscard void* mi_rezalloc_aligned_at(void* p, size_t newsize, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_rezalloc_aligned_at(mi_prim_get_default_heap(), p, newsize, alignment, offset);
+}
+
+mi_decl_nodiscard void* mi_rezalloc_aligned(void* p, size_t newsize, size_t alignment) mi_attr_noexcept {
+ return mi_heap_rezalloc_aligned(mi_prim_get_default_heap(), p, newsize, alignment);
+}
+
+mi_decl_nodiscard void* mi_recalloc_aligned_at(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept {
+ return mi_heap_recalloc_aligned_at(mi_prim_get_default_heap(), p, newcount, size, alignment, offset);
+}
+
+mi_decl_nodiscard void* mi_recalloc_aligned(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept {
+ return mi_heap_recalloc_aligned(mi_prim_get_default_heap(), p, newcount, size, alignment);
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/alloc-override.c b/contrib/tools/python3/Objects/mimalloc/alloc-override.c
new file mode 100644
index 00000000000..873065dc634
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/alloc-override.c
@@ -0,0 +1,297 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#if !defined(MI_IN_ALLOC_C)
+#error "this file should be included from 'alloc.c' (so aliases can work)"
+#endif
+
+#if defined(MI_MALLOC_OVERRIDE) && defined(_WIN32) && !(defined(MI_SHARED_LIB) && defined(_DLL))
+#error "It is only possible to override "malloc" on Windows when building as a DLL (and linking the C runtime as a DLL)"
+#endif
+
+#if defined(MI_MALLOC_OVERRIDE) && !(defined(_WIN32))
+
+#if defined(__APPLE__)
+#include <AvailabilityMacros.h>
+mi_decl_externc void vfree(void* p);
+mi_decl_externc size_t malloc_size(const void* p);
+mi_decl_externc size_t malloc_good_size(size_t size);
+#endif
+
+// helper definition for C override of C++ new
+typedef struct mi_nothrow_s { int _tag; } mi_nothrow_t;
+
+// ------------------------------------------------------
+// Override system malloc
+// ------------------------------------------------------
+
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__) && !MI_TRACK_ENABLED
+ // gcc, clang: use aliasing to alias the exported function to one of our `mi_` functions
+ #if (defined(__GNUC__) && __GNUC__ >= 9)
+ #pragma GCC diagnostic ignored "-Wattributes" // or we get warnings that nodiscard is ignored on a forward
+ #define MI_FORWARD(fun) __attribute__((alias(#fun), used, visibility("default"), copy(fun)));
+ #else
+ #define MI_FORWARD(fun) __attribute__((alias(#fun), used, visibility("default")));
+ #endif
+ #define MI_FORWARD1(fun,x) MI_FORWARD(fun)
+ #define MI_FORWARD2(fun,x,y) MI_FORWARD(fun)
+ #define MI_FORWARD3(fun,x,y,z) MI_FORWARD(fun)
+ #define MI_FORWARD0(fun,x) MI_FORWARD(fun)
+ #define MI_FORWARD02(fun,x,y) MI_FORWARD(fun)
+#else
+ // otherwise use forwarding by calling our `mi_` function
+ #define MI_FORWARD1(fun,x) { return fun(x); }
+ #define MI_FORWARD2(fun,x,y) { return fun(x,y); }
+ #define MI_FORWARD3(fun,x,y,z) { return fun(x,y,z); }
+ #define MI_FORWARD0(fun,x) { fun(x); }
+ #define MI_FORWARD02(fun,x,y) { fun(x,y); }
+#endif
+
+
+#if defined(__APPLE__) && defined(MI_SHARED_LIB_EXPORT) && defined(MI_OSX_INTERPOSE)
+ // define MI_OSX_IS_INTERPOSED as we should not provide forwarding definitions for
+ // functions that are interposed (or the interposing does not work)
+ #define MI_OSX_IS_INTERPOSED
+
+ mi_decl_externc size_t mi_malloc_size_checked(void *p) {
+ if (!mi_is_in_heap_region(p)) return 0;
+ return mi_usable_size(p);
+ }
+
+ // use interposing so `DYLD_INSERT_LIBRARIES` works without `DYLD_FORCE_FLAT_NAMESPACE=1`
+ // See: <https://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73>
+ struct mi_interpose_s {
+ const void* replacement;
+ const void* target;
+ };
+ #define MI_INTERPOSE_FUN(oldfun,newfun) { (const void*)&newfun, (const void*)&oldfun }
+ #define MI_INTERPOSE_MI(fun) MI_INTERPOSE_FUN(fun,mi_##fun)
+
+ __attribute__((used)) static struct mi_interpose_s _mi_interposes[] __attribute__((section("__DATA, __interpose"))) =
+ {
+ MI_INTERPOSE_MI(malloc),
+ MI_INTERPOSE_MI(calloc),
+ MI_INTERPOSE_MI(realloc),
+ MI_INTERPOSE_MI(strdup),
+ MI_INTERPOSE_MI(strndup),
+ MI_INTERPOSE_MI(realpath),
+ MI_INTERPOSE_MI(posix_memalign),
+ MI_INTERPOSE_MI(reallocf),
+ MI_INTERPOSE_MI(valloc),
+ MI_INTERPOSE_FUN(malloc_size,mi_malloc_size_checked),
+ MI_INTERPOSE_MI(malloc_good_size),
+ #if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
+ MI_INTERPOSE_MI(aligned_alloc),
+ #endif
+ #ifdef MI_OSX_ZONE
+ // we interpose malloc_default_zone in alloc-override-osx.c so we can use mi_free safely
+ MI_INTERPOSE_MI(free),
+ MI_INTERPOSE_FUN(vfree,mi_free),
+ #else
+ // sometimes code allocates from default zone but deallocates using plain free :-( (like NxHashResizeToCapacity <https://github.com/nneonneo/osx-10.9-opensource/blob/master/objc4-551.1/runtime/hashtable2.mm>)
+ MI_INTERPOSE_FUN(free,mi_cfree), // use safe free that checks if pointers are from us
+ MI_INTERPOSE_FUN(vfree,mi_cfree),
+ #endif
+ };
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+ void _ZdlPv(void* p); // delete
+ void _ZdaPv(void* p); // delete[]
+ void _ZdlPvm(void* p, size_t n); // delete
+ void _ZdaPvm(void* p, size_t n); // delete[]
+ void* _Znwm(size_t n); // new
+ void* _Znam(size_t n); // new[]
+ void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag); // new nothrow
+ void* _ZnamRKSt9nothrow_t(size_t n, mi_nothrow_t tag); // new[] nothrow
+ #ifdef __cplusplus
+ }
+ #endif
+ __attribute__((used)) static struct mi_interpose_s _mi_cxx_interposes[] __attribute__((section("__DATA, __interpose"))) =
+ {
+ MI_INTERPOSE_FUN(_ZdlPv,mi_free),
+ MI_INTERPOSE_FUN(_ZdaPv,mi_free),
+ MI_INTERPOSE_FUN(_ZdlPvm,mi_free_size),
+ MI_INTERPOSE_FUN(_ZdaPvm,mi_free_size),
+ MI_INTERPOSE_FUN(_Znwm,mi_new),
+ MI_INTERPOSE_FUN(_Znam,mi_new),
+ MI_INTERPOSE_FUN(_ZnwmRKSt9nothrow_t,mi_new_nothrow),
+ MI_INTERPOSE_FUN(_ZnamRKSt9nothrow_t,mi_new_nothrow),
+ };
+
+#elif defined(_MSC_VER)
+ // cannot override malloc unless using a dll.
+ // we just override new/delete which does work in a static library.
+#else
+ // On all other systems forward to our API
+ mi_decl_export void* malloc(size_t size) MI_FORWARD1(mi_malloc, size)
+ mi_decl_export void* calloc(size_t size, size_t n) MI_FORWARD2(mi_calloc, size, n)
+ mi_decl_export void* realloc(void* p, size_t newsize) MI_FORWARD2(mi_realloc, p, newsize)
+ mi_decl_export void free(void* p) MI_FORWARD0(mi_free, p)
+#endif
+
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__)
+#pragma GCC visibility push(default)
+#endif
+
+// ------------------------------------------------------
+// Override new/delete
+// This is not really necessary as they usually call
+// malloc/free anyway, but it improves performance.
+// ------------------------------------------------------
+#ifdef __cplusplus
+ // ------------------------------------------------------
+ // With a C++ compiler we override the new/delete operators.
+ // see <https://en.cppreference.com/w/cpp/memory/new/operator_new>
+ // ------------------------------------------------------
+ #include <new>
+
+ #ifndef MI_OSX_IS_INTERPOSED
+ void operator delete(void* p) noexcept MI_FORWARD0(mi_free,p)
+ void operator delete[](void* p) noexcept MI_FORWARD0(mi_free,p)
+
+ void* operator new(std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n)
+ void* operator new[](std::size_t n) noexcept(false) MI_FORWARD1(mi_new,n)
+
+ void* operator new (std::size_t n, const std::nothrow_t& tag) noexcept { MI_UNUSED(tag); return mi_new_nothrow(n); }
+ void* operator new[](std::size_t n, const std::nothrow_t& tag) noexcept { MI_UNUSED(tag); return mi_new_nothrow(n); }
+
+ #if (__cplusplus >= 201402L || _MSC_VER >= 1916)
+ void operator delete (void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n)
+ void operator delete[](void* p, std::size_t n) noexcept MI_FORWARD02(mi_free_size,p,n)
+ #endif
+ #endif
+
+ #if (__cplusplus > 201402L && defined(__cpp_aligned_new)) && (!defined(__GNUC__) || (__GNUC__ > 5))
+ void operator delete (void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
+ void operator delete[](void* p, std::align_val_t al) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
+ void operator delete (void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
+ void operator delete[](void* p, std::size_t n, std::align_val_t al) noexcept { mi_free_size_aligned(p, n, static_cast<size_t>(al)); };
+ void operator delete (void* p, std::align_val_t al, const std::nothrow_t&) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
+ void operator delete[](void* p, std::align_val_t al, const std::nothrow_t&) noexcept { mi_free_aligned(p, static_cast<size_t>(al)); }
+
+ void* operator new( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
+ void* operator new[]( std::size_t n, std::align_val_t al) noexcept(false) { return mi_new_aligned(n, static_cast<size_t>(al)); }
+ void* operator new (std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast<size_t>(al)); }
+ void* operator new[](std::size_t n, std::align_val_t al, const std::nothrow_t&) noexcept { return mi_new_aligned_nothrow(n, static_cast<size_t>(al)); }
+ #endif
+
+#elif (defined(__GNUC__) || defined(__clang__))
+ // ------------------------------------------------------
+ // Override by defining the mangled C++ names of the operators (as
+ // used by GCC and CLang).
+ // See <https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling>
+ // ------------------------------------------------------
+
+ void _ZdlPv(void* p) MI_FORWARD0(mi_free,p) // delete
+ void _ZdaPv(void* p) MI_FORWARD0(mi_free,p) // delete[]
+ void _ZdlPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n)
+ void _ZdaPvm(void* p, size_t n) MI_FORWARD02(mi_free_size,p,n)
+ void _ZdlPvSt11align_val_t(void* p, size_t al) { mi_free_aligned(p,al); }
+ void _ZdaPvSt11align_val_t(void* p, size_t al) { mi_free_aligned(p,al); }
+ void _ZdlPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); }
+ void _ZdaPvmSt11align_val_t(void* p, size_t n, size_t al) { mi_free_size_aligned(p,n,al); }
+
+ #if (MI_INTPTR_SIZE==8)
+ void* _Znwm(size_t n) MI_FORWARD1(mi_new,n) // new 64-bit
+ void* _Znam(size_t n) MI_FORWARD1(mi_new,n) // new[] 64-bit
+ void* _ZnwmRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); }
+ void* _ZnamRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); }
+ void* _ZnwmSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al)
+ void* _ZnamSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al)
+ void* _ZnwmSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
+ void* _ZnamSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
+ #elif (MI_INTPTR_SIZE==4)
+ void* _Znwj(size_t n) MI_FORWARD1(mi_new,n) // new 64-bit
+ void* _Znaj(size_t n) MI_FORWARD1(mi_new,n) // new[] 64-bit
+ void* _ZnwjRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); }
+ void* _ZnajRKSt9nothrow_t(size_t n, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_nothrow(n); }
+ void* _ZnwjSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al)
+ void* _ZnajSt11align_val_t(size_t n, size_t al) MI_FORWARD2(mi_new_aligned, n, al)
+ void* _ZnwjSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
+ void* _ZnajSt11align_val_tRKSt9nothrow_t(size_t n, size_t al, mi_nothrow_t tag) { MI_UNUSED(tag); return mi_new_aligned_nothrow(n,al); }
+ #else
+ #error "define overloads for new/delete for this platform (just for performance, can be skipped)"
+ #endif
+#endif // __cplusplus
+
+// ------------------------------------------------------
+// Further Posix & Unix functions definitions
+// ------------------------------------------------------
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef MI_OSX_IS_INTERPOSED
+ // Forward Posix/Unix calls as well
+ void* reallocf(void* p, size_t newsize) MI_FORWARD2(mi_reallocf,p,newsize)
+ size_t malloc_size(const void* p) MI_FORWARD1(mi_usable_size,p)
+ #if !defined(__ANDROID__) && !defined(__FreeBSD__)
+ size_t malloc_usable_size(void *p) MI_FORWARD1(mi_usable_size,p)
+ #else
+ size_t malloc_usable_size(const void *p) MI_FORWARD1(mi_usable_size,p)
+ #endif
+
+ // No forwarding here due to aliasing/name mangling issues
+ void* valloc(size_t size) { return mi_valloc(size); }
+ void vfree(void* p) { mi_free(p); }
+ size_t malloc_good_size(size_t size) { return mi_malloc_good_size(size); }
+ int posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p, alignment, size); }
+
+ // `aligned_alloc` is only available when __USE_ISOC11 is defined.
+ // Note: it seems __USE_ISOC11 is not defined in musl (and perhaps other libc's) so we only check
+ // for it if using glibc.
+ // Note: Conda has a custom glibc where `aligned_alloc` is declared `static inline` and we cannot
+ // override it, but both _ISOC11_SOURCE and __USE_ISOC11 are undefined in Conda GCC7 or GCC9.
+ // Fortunately, in the case where `aligned_alloc` is declared as `static inline` it
+ // uses internally `memalign`, `posix_memalign`, or `_aligned_malloc` so we can avoid overriding it ourselves.
+ #if !defined(__GLIBC__) || __USE_ISOC11
+ void* aligned_alloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); }
+ #endif
+#endif
+
+// no forwarding here due to aliasing/name mangling issues
+void cfree(void* p) { mi_free(p); }
+void* pvalloc(size_t size) { return mi_pvalloc(size); }
+void* reallocarray(void* p, size_t count, size_t size) { return mi_reallocarray(p, count, size); }
+int reallocarr(void* p, size_t count, size_t size) { return mi_reallocarr(p, count, size); }
+void* memalign(size_t alignment, size_t size) { return mi_memalign(alignment, size); }
+void* _aligned_malloc(size_t alignment, size_t size) { return mi_aligned_alloc(alignment, size); }
+
+#if defined(__wasi__)
+ // forward __libc interface (see PR #667)
+ void* __libc_malloc(size_t size) MI_FORWARD1(mi_malloc, size)
+ void* __libc_calloc(size_t count, size_t size) MI_FORWARD2(mi_calloc, count, size)
+ void* __libc_realloc(void* p, size_t size) MI_FORWARD2(mi_realloc, p, size)
+ void __libc_free(void* p) MI_FORWARD0(mi_free, p)
+ void* __libc_memalign(size_t alignment, size_t size) { return mi_memalign(alignment, size); }
+
+#elif defined(__GLIBC__) && defined(__linux__)
+ // forward __libc interface (needed for glibc-based Linux distributions)
+ void* __libc_malloc(size_t size) MI_FORWARD1(mi_malloc,size)
+ void* __libc_calloc(size_t count, size_t size) MI_FORWARD2(mi_calloc,count,size)
+ void* __libc_realloc(void* p, size_t size) MI_FORWARD2(mi_realloc,p,size)
+ void __libc_free(void* p) MI_FORWARD0(mi_free,p)
+ void __libc_cfree(void* p) MI_FORWARD0(mi_free,p)
+
+ void* __libc_valloc(size_t size) { return mi_valloc(size); }
+ void* __libc_pvalloc(size_t size) { return mi_pvalloc(size); }
+ void* __libc_memalign(size_t alignment, size_t size) { return mi_memalign(alignment,size); }
+ int __posix_memalign(void** p, size_t alignment, size_t size) { return mi_posix_memalign(p,alignment,size); }
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#if (defined(__GNUC__) || defined(__clang__)) && !defined(__APPLE__)
+#pragma GCC visibility pop
+#endif
+
+#endif // MI_MALLOC_OVERRIDE && !_WIN32
diff --git a/contrib/tools/python3/Objects/mimalloc/alloc-posix.c b/contrib/tools/python3/Objects/mimalloc/alloc-posix.c
new file mode 100644
index 00000000000..225752fd870
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/alloc-posix.c
@@ -0,0 +1,185 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+// ------------------------------------------------------------------------
+// mi prefixed publi definitions of various Posix, Unix, and C++ functions
+// for convenience and used when overriding these functions.
+// ------------------------------------------------------------------------
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+
+// ------------------------------------------------------
+// Posix & Unix functions definitions
+// ------------------------------------------------------
+
+#include <errno.h>
+#include <string.h> // memset
+#include <stdlib.h> // getenv
+
+#ifdef _MSC_VER
+#pragma warning(disable:4996) // getenv _wgetenv
+#endif
+
+#ifndef EINVAL
+#define EINVAL 22
+#endif
+#ifndef ENOMEM
+#define ENOMEM 12
+#endif
+
+
+mi_decl_nodiscard size_t mi_malloc_size(const void* p) mi_attr_noexcept {
+ // if (!mi_is_in_heap_region(p)) return 0;
+ return mi_usable_size(p);
+}
+
+mi_decl_nodiscard size_t mi_malloc_usable_size(const void *p) mi_attr_noexcept {
+ // if (!mi_is_in_heap_region(p)) return 0;
+ return mi_usable_size(p);
+}
+
+mi_decl_nodiscard size_t mi_malloc_good_size(size_t size) mi_attr_noexcept {
+ return mi_good_size(size);
+}
+
+void mi_cfree(void* p) mi_attr_noexcept {
+ if (mi_is_in_heap_region(p)) {
+ mi_free(p);
+ }
+}
+
+int mi_posix_memalign(void** p, size_t alignment, size_t size) mi_attr_noexcept {
+ // Note: The spec dictates we should not modify `*p` on an error. (issue#27)
+ // <http://man7.org/linux/man-pages/man3/posix_memalign.3.html>
+ if (p == NULL) return EINVAL;
+ if ((alignment % sizeof(void*)) != 0) return EINVAL; // natural alignment
+ // it is also required that alignment is a power of 2 and > 0; this is checked in `mi_malloc_aligned`
+ if (alignment==0 || !_mi_is_power_of_two(alignment)) return EINVAL; // not a power of 2
+ void* q = mi_malloc_aligned(size, alignment);
+ if (q==NULL && size != 0) return ENOMEM;
+ mi_assert_internal(((uintptr_t)q % alignment) == 0);
+ *p = q;
+ return 0;
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_memalign(size_t alignment, size_t size) mi_attr_noexcept {
+ void* p = mi_malloc_aligned(size, alignment);
+ mi_assert_internal(((uintptr_t)p % alignment) == 0);
+ return p;
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_valloc(size_t size) mi_attr_noexcept {
+ return mi_memalign( _mi_os_page_size(), size );
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_pvalloc(size_t size) mi_attr_noexcept {
+ size_t psize = _mi_os_page_size();
+ if (size >= SIZE_MAX - psize) return NULL; // overflow
+ size_t asize = _mi_align_up(size, psize);
+ return mi_malloc_aligned(asize, psize);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_aligned_alloc(size_t alignment, size_t size) mi_attr_noexcept {
+ // C11 requires the size to be an integral multiple of the alignment, see <https://en.cppreference.com/w/c/memory/aligned_alloc>.
+ // unfortunately, it turns out quite some programs pass a size that is not an integral multiple so skip this check..
+ /* if mi_unlikely((size & (alignment - 1)) != 0) { // C11 requires alignment>0 && integral multiple, see <https://en.cppreference.com/w/c/memory/aligned_alloc>
+ #if MI_DEBUG > 0
+ _mi_error_message(EOVERFLOW, "(mi_)aligned_alloc requires the size to be an integral multiple of the alignment (size %zu, alignment %zu)\n", size, alignment);
+ #endif
+ return NULL;
+ }
+ */
+ // C11 also requires alignment to be a power-of-two (and > 0) which is checked in mi_malloc_aligned
+ void* p = mi_malloc_aligned(size, alignment);
+ mi_assert_internal(((uintptr_t)p % alignment) == 0);
+ return p;
+}
+
+mi_decl_nodiscard void* mi_reallocarray( void* p, size_t count, size_t size ) mi_attr_noexcept { // BSD
+ void* newp = mi_reallocn(p,count,size);
+ if (newp==NULL) { errno = ENOMEM; }
+ return newp;
+}
+
+mi_decl_nodiscard int mi_reallocarr( void* p, size_t count, size_t size ) mi_attr_noexcept { // NetBSD
+ mi_assert(p != NULL);
+ if (p == NULL) {
+ errno = EINVAL;
+ return EINVAL;
+ }
+ void** op = (void**)p;
+ void* newp = mi_reallocarray(*op, count, size);
+ if mi_unlikely(newp == NULL) { return errno; }
+ *op = newp;
+ return 0;
+}
+
+void* mi__expand(void* p, size_t newsize) mi_attr_noexcept { // Microsoft
+ void* res = mi_expand(p, newsize);
+ if (res == NULL) { errno = ENOMEM; }
+ return res;
+}
+
+mi_decl_nodiscard mi_decl_restrict unsigned short* mi_wcsdup(const unsigned short* s) mi_attr_noexcept {
+ if (s==NULL) return NULL;
+ size_t len;
+ for(len = 0; s[len] != 0; len++) { }
+ size_t size = (len+1)*sizeof(unsigned short);
+ unsigned short* p = (unsigned short*)mi_malloc(size);
+ if (p != NULL) {
+ _mi_memcpy(p,s,size);
+ }
+ return p;
+}
+
+mi_decl_nodiscard mi_decl_restrict unsigned char* mi_mbsdup(const unsigned char* s) mi_attr_noexcept {
+ return (unsigned char*)mi_strdup((const char*)s);
+}
+
+int mi_dupenv_s(char** buf, size_t* size, const char* name) mi_attr_noexcept {
+ if (buf==NULL || name==NULL) return EINVAL;
+ if (size != NULL) *size = 0;
+ char* p = getenv(name); // mscver warning 4996
+ if (p==NULL) {
+ *buf = NULL;
+ }
+ else {
+ *buf = mi_strdup(p);
+ if (*buf==NULL) return ENOMEM;
+ if (size != NULL) *size = _mi_strlen(p);
+ }
+ return 0;
+}
+
+int mi_wdupenv_s(unsigned short** buf, size_t* size, const unsigned short* name) mi_attr_noexcept {
+ if (buf==NULL || name==NULL) return EINVAL;
+ if (size != NULL) *size = 0;
+#if !defined(_WIN32) || (defined(WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP))
+ // not supported
+ *buf = NULL;
+ return EINVAL;
+#else
+ unsigned short* p = (unsigned short*)_wgetenv((const wchar_t*)name); // msvc warning 4996
+ if (p==NULL) {
+ *buf = NULL;
+ }
+ else {
+ *buf = mi_wcsdup(p);
+ if (*buf==NULL) return ENOMEM;
+ if (size != NULL) *size = wcslen((const wchar_t*)p);
+ }
+ return 0;
+#endif
+}
+
+mi_decl_nodiscard void* mi_aligned_offset_recalloc(void* p, size_t newcount, size_t size, size_t alignment, size_t offset) mi_attr_noexcept { // Microsoft
+ return mi_recalloc_aligned_at(p, newcount, size, alignment, offset);
+}
+
+mi_decl_nodiscard void* mi_aligned_recalloc(void* p, size_t newcount, size_t size, size_t alignment) mi_attr_noexcept { // Microsoft
+ return mi_recalloc_aligned(p, newcount, size, alignment);
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/alloc.c b/contrib/tools/python3/Objects/mimalloc/alloc.c
new file mode 100644
index 00000000000..c133f23fc98
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/alloc.c
@@ -0,0 +1,1074 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2022, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE // for realpath() on Linux
+#endif
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+#include "mimalloc/prim.h" // _mi_prim_thread_id()
+
+#include <string.h> // memset, strlen (for mi_strdup)
+#include <stdlib.h> // malloc, abort
+
+#define _ZSt15get_new_handlerv _Py__ZSt15get_new_handlerv
+
+#define MI_IN_ALLOC_C
+#include "alloc-override.c"
+#undef MI_IN_ALLOC_C
+
+// ------------------------------------------------------
+// Allocation
+// ------------------------------------------------------
+
+#if (MI_DEBUG>0)
+static inline void mi_debug_fill(mi_page_t* page, mi_block_t* block, int c, size_t size) {
+ size_t offset = (size_t)page->debug_offset;
+ if (offset < size) {
+ memset((char*)block + offset, c, size - offset);
+ }
+}
+#endif
+
+// Fast allocation in a page: just pop from the free list.
+// Fall back to generic allocation only if the list is empty.
+extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t size, bool zero) mi_attr_noexcept {
+ mi_assert_internal(page->xblock_size==0||mi_page_block_size(page) >= size);
+ mi_block_t* const block = page->free;
+ if mi_unlikely(block == NULL) {
+ return _mi_malloc_generic(heap, size, zero, 0);
+ }
+ mi_assert_internal(block != NULL && _mi_ptr_page(block) == page);
+ // pop from the free list
+ page->used++;
+ page->free = mi_block_next(page, block);
+ mi_assert_internal(page->free == NULL || _mi_ptr_page(page->free) == page);
+ #if MI_DEBUG>3
+ if (page->free_is_zero) {
+ mi_assert_expensive(mi_mem_is_zero(block+1,size - sizeof(*block)));
+ }
+ #endif
+
+ // allow use of the block internally
+ // note: when tracking we need to avoid ever touching the MI_PADDING since
+ // that is tracked by valgrind etc. as non-accessible (through the red-zone, see `mimalloc/track.h`)
+ mi_track_mem_undefined(block, mi_page_usable_block_size(page));
+
+ // zero the block? note: we need to zero the full block size (issue #63)
+ if mi_unlikely(zero) {
+ mi_assert_internal(page->xblock_size != 0); // do not call with zero'ing for huge blocks (see _mi_malloc_generic)
+ mi_assert_internal(page->xblock_size >= MI_PADDING_SIZE);
+ if (page->free_is_zero) {
+ block->next = 0;
+ mi_track_mem_defined(block, page->xblock_size - MI_PADDING_SIZE);
+ }
+ else {
+ _mi_memzero_aligned(block, page->xblock_size - MI_PADDING_SIZE);
+ }
+ }
+
+#if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN
+ if (!zero && !mi_page_is_huge(page)) {
+ mi_debug_fill(page, block, MI_DEBUG_UNINIT, mi_page_usable_block_size(page));
+ }
+#elif (MI_SECURE!=0)
+ if (!zero) { block->next = 0; } // don't leak internal data
+#endif
+
+#if (MI_STAT>0)
+ const size_t bsize = mi_page_usable_block_size(page);
+ if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) {
+ mi_heap_stat_increase(heap, normal, bsize);
+ mi_heap_stat_counter_increase(heap, normal_count, 1);
+#if (MI_STAT>1)
+ const size_t bin = _mi_bin(bsize);
+ mi_heap_stat_increase(heap, normal_bins[bin], 1);
+#endif
+ }
+#endif
+
+#if MI_PADDING // && !MI_TRACK_ENABLED
+ mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + mi_page_usable_block_size(page));
+ ptrdiff_t delta = ((uint8_t*)padding - (uint8_t*)block - (size - MI_PADDING_SIZE));
+ #if (MI_DEBUG>=2)
+ mi_assert_internal(delta >= 0 && mi_page_usable_block_size(page) >= (size - MI_PADDING_SIZE + delta));
+ #endif
+ mi_track_mem_defined(padding,sizeof(mi_padding_t)); // note: re-enable since mi_page_usable_block_size may set noaccess
+ padding->canary = (uint32_t)(mi_ptr_encode(page,block,page->keys));
+ padding->delta = (uint32_t)(delta);
+ #if MI_PADDING_CHECK
+ if (!mi_page_is_huge(page)) {
+ uint8_t* fill = (uint8_t*)padding - delta;
+ const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // set at most N initial padding bytes
+ for (size_t i = 0; i < maxpad; i++) { fill[i] = MI_DEBUG_PADDING; }
+ }
+ #endif
+#endif
+
+ return block;
+}
+
+static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept {
+ mi_assert(heap != NULL);
+ #if MI_DEBUG
+ const uintptr_t tid = _mi_thread_id();
+ mi_assert(heap->thread_id == 0 || heap->thread_id == tid); // heaps are thread local
+ #endif
+ mi_assert(size <= MI_SMALL_SIZE_MAX);
+ #if (MI_PADDING)
+ if (size == 0) { size = sizeof(void*); }
+ #endif
+ mi_page_t* page = _mi_heap_get_free_small_page(heap, size + MI_PADDING_SIZE);
+ void* const p = _mi_page_malloc(heap, page, size + MI_PADDING_SIZE, zero);
+ mi_track_malloc(p,size,zero);
+ #if MI_STAT>1
+ if (p != NULL) {
+ if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); }
+ mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
+ }
+ #endif
+ #if MI_DEBUG>3
+ if (p != NULL && zero) {
+ mi_assert_expensive(mi_mem_is_zero(p, size));
+ }
+ #endif
+ return p;
+}
+
+// allocate a small block
+mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc_small(mi_heap_t* heap, size_t size) mi_attr_noexcept {
+ return mi_heap_malloc_small_zero(heap, size, false);
+}
+
+mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc_small(size_t size) mi_attr_noexcept {
+ return mi_heap_malloc_small(mi_prim_get_default_heap(), size);
+}
+
+// The main allocation function
+extern inline void* _mi_heap_malloc_zero_ex(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept {
+ if mi_likely(size <= MI_SMALL_SIZE_MAX) {
+ mi_assert_internal(huge_alignment == 0);
+ return mi_heap_malloc_small_zero(heap, size, zero);
+ }
+ else {
+ mi_assert(heap!=NULL);
+ mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local
+ void* const p = _mi_malloc_generic(heap, size + MI_PADDING_SIZE, zero, huge_alignment); // note: size can overflow but it is detected in malloc_generic
+ mi_track_malloc(p,size,zero);
+ #if MI_STAT>1
+ if (p != NULL) {
+ if (!mi_heap_is_initialized(heap)) { heap = mi_prim_get_default_heap(); }
+ mi_heap_stat_increase(heap, malloc, mi_usable_size(p));
+ }
+ #endif
+ #if MI_DEBUG>3
+ if (p != NULL && zero) {
+ mi_assert_expensive(mi_mem_is_zero(p, size));
+ }
+ #endif
+ return p;
+ }
+}
+
+extern inline void* _mi_heap_malloc_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept {
+ return _mi_heap_malloc_zero_ex(heap, size, zero, 0);
+}
+
+mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_malloc(mi_heap_t* heap, size_t size) mi_attr_noexcept {
+ return _mi_heap_malloc_zero(heap, size, false);
+}
+
+mi_decl_nodiscard extern inline mi_decl_restrict void* mi_malloc(size_t size) mi_attr_noexcept {
+ return mi_heap_malloc(mi_prim_get_default_heap(), size);
+}
+
+// zero initialized small block
+mi_decl_nodiscard mi_decl_restrict void* mi_zalloc_small(size_t size) mi_attr_noexcept {
+ return mi_heap_malloc_small_zero(mi_prim_get_default_heap(), size, true);
+}
+
+mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_zalloc(mi_heap_t* heap, size_t size) mi_attr_noexcept {
+ return _mi_heap_malloc_zero(heap, size, true);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_zalloc(size_t size) mi_attr_noexcept {
+ return mi_heap_zalloc(mi_prim_get_default_heap(),size);
+}
+
+
+// ------------------------------------------------------
+// Check for double free in secure and debug mode
+// This is somewhat expensive so only enabled for secure mode 4
+// ------------------------------------------------------
+
+#if (MI_ENCODE_FREELIST && (MI_SECURE>=4 || MI_DEBUG!=0))
+// linear check if the free list contains a specific element
+static bool mi_list_contains(const mi_page_t* page, const mi_block_t* list, const mi_block_t* elem) {
+ while (list != NULL) {
+ if (elem==list) return true;
+ list = mi_block_next(page, list);
+ }
+ return false;
+}
+
+static mi_decl_noinline bool mi_check_is_double_freex(const mi_page_t* page, const mi_block_t* block) {
+ // The decoded value is in the same page (or NULL).
+ // Walk the free lists to verify positively if it is already freed
+ if (mi_list_contains(page, page->free, block) ||
+ mi_list_contains(page, page->local_free, block) ||
+ mi_list_contains(page, mi_page_thread_free(page), block))
+ {
+ _mi_error_message(EAGAIN, "double free detected of block %p with size %zu\n", block, mi_page_block_size(page));
+ return true;
+ }
+ return false;
+}
+
+#define mi_track_page(page,access) { size_t psize; void* pstart = _mi_page_start(_mi_page_segment(page),page,&psize); mi_track_mem_##access( pstart, psize); }
+
+static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) {
+ bool is_double_free = false;
+ mi_block_t* n = mi_block_nextx(page, block, page->keys); // pretend it is freed, and get the decoded first field
+ if (((uintptr_t)n & (MI_INTPTR_SIZE-1))==0 && // quick check: aligned pointer?
+ (n==NULL || mi_is_in_same_page(block, n))) // quick check: in same page or NULL?
+ {
+ // Suspicous: decoded value a in block is in the same page (or NULL) -- maybe a double free?
+ // (continue in separate function to improve code generation)
+ is_double_free = mi_check_is_double_freex(page, block);
+ }
+ return is_double_free;
+}
+#else
+static inline bool mi_check_is_double_free(const mi_page_t* page, const mi_block_t* block) {
+ MI_UNUSED(page);
+ MI_UNUSED(block);
+ return false;
+}
+#endif
+
+// ---------------------------------------------------------------------------
+// Check for heap block overflow by setting up padding at the end of the block
+// ---------------------------------------------------------------------------
+
+#if MI_PADDING // && !MI_TRACK_ENABLED
+static bool mi_page_decode_padding(const mi_page_t* page, const mi_block_t* block, size_t* delta, size_t* bsize) {
+ *bsize = mi_page_usable_block_size(page);
+ const mi_padding_t* const padding = (mi_padding_t*)((uint8_t*)block + *bsize);
+ mi_track_mem_defined(padding,sizeof(mi_padding_t));
+ *delta = padding->delta;
+ uint32_t canary = padding->canary;
+ uintptr_t keys[2];
+ keys[0] = page->keys[0];
+ keys[1] = page->keys[1];
+ bool ok = ((uint32_t)mi_ptr_encode(page,block,keys) == canary && *delta <= *bsize);
+ mi_track_mem_noaccess(padding,sizeof(mi_padding_t));
+ return ok;
+}
+
+// Return the exact usable size of a block.
+static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) {
+ size_t bsize;
+ size_t delta;
+ bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
+ mi_assert_internal(ok); mi_assert_internal(delta <= bsize);
+ return (ok ? bsize - delta : 0);
+}
+
+// When a non-thread-local block is freed, it becomes part of the thread delayed free
+// list that is freed later by the owning heap. If the exact usable size is too small to
+// contain the pointer for the delayed list, then shrink the padding (by decreasing delta)
+// so it will later not trigger an overflow error in `mi_free_block`.
+void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) {
+ size_t bsize;
+ size_t delta;
+ bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
+ mi_assert_internal(ok);
+ if (!ok || (bsize - delta) >= min_size) return; // usually already enough space
+ mi_assert_internal(bsize >= min_size);
+ if (bsize < min_size) return; // should never happen
+ size_t new_delta = (bsize - min_size);
+ mi_assert_internal(new_delta < bsize);
+ mi_padding_t* padding = (mi_padding_t*)((uint8_t*)block + bsize);
+ mi_track_mem_defined(padding,sizeof(mi_padding_t));
+ padding->delta = (uint32_t)new_delta;
+ mi_track_mem_noaccess(padding,sizeof(mi_padding_t));
+}
+#else
+static size_t mi_page_usable_size_of(const mi_page_t* page, const mi_block_t* block) {
+ MI_UNUSED(block);
+ return mi_page_usable_block_size(page);
+}
+
+void _mi_padding_shrink(const mi_page_t* page, const mi_block_t* block, const size_t min_size) {
+ MI_UNUSED(page);
+ MI_UNUSED(block);
+ MI_UNUSED(min_size);
+}
+#endif
+
+#if MI_PADDING && MI_PADDING_CHECK
+
+static bool mi_verify_padding(const mi_page_t* page, const mi_block_t* block, size_t* size, size_t* wrong) {
+ size_t bsize;
+ size_t delta;
+ bool ok = mi_page_decode_padding(page, block, &delta, &bsize);
+ *size = *wrong = bsize;
+ if (!ok) return false;
+ mi_assert_internal(bsize >= delta);
+ *size = bsize - delta;
+ if (!mi_page_is_huge(page)) {
+ uint8_t* fill = (uint8_t*)block + bsize - delta;
+ const size_t maxpad = (delta > MI_MAX_ALIGN_SIZE ? MI_MAX_ALIGN_SIZE : delta); // check at most the first N padding bytes
+ mi_track_mem_defined(fill, maxpad);
+ for (size_t i = 0; i < maxpad; i++) {
+ if (fill[i] != MI_DEBUG_PADDING) {
+ *wrong = bsize - delta + i;
+ ok = false;
+ break;
+ }
+ }
+ mi_track_mem_noaccess(fill, maxpad);
+ }
+ return ok;
+}
+
+static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) {
+ size_t size;
+ size_t wrong;
+ if (!mi_verify_padding(page,block,&size,&wrong)) {
+ _mi_error_message(EFAULT, "buffer overflow in heap block %p of size %zu: write after %zu bytes\n", block, size, wrong );
+ }
+}
+
+#else
+
+static void mi_check_padding(const mi_page_t* page, const mi_block_t* block) {
+ MI_UNUSED(page);
+ MI_UNUSED(block);
+}
+
+#endif
+
+// only maintain stats for smaller objects if requested
+#if (MI_STAT>0)
+static void mi_stat_free(const mi_page_t* page, const mi_block_t* block) {
+ #if (MI_STAT < 2)
+ MI_UNUSED(block);
+ #endif
+ mi_heap_t* const heap = mi_heap_get_default();
+ const size_t bsize = mi_page_usable_block_size(page);
+ #if (MI_STAT>1)
+ const size_t usize = mi_page_usable_size_of(page, block);
+ mi_heap_stat_decrease(heap, malloc, usize);
+ #endif
+ if (bsize <= MI_MEDIUM_OBJ_SIZE_MAX) {
+ mi_heap_stat_decrease(heap, normal, bsize);
+ #if (MI_STAT > 1)
+ mi_heap_stat_decrease(heap, normal_bins[_mi_bin(bsize)], 1);
+ #endif
+ }
+ else if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
+ mi_heap_stat_decrease(heap, large, bsize);
+ }
+ else {
+ mi_heap_stat_decrease(heap, huge, bsize);
+ }
+}
+#else
+static void mi_stat_free(const mi_page_t* page, const mi_block_t* block) {
+ MI_UNUSED(page); MI_UNUSED(block);
+}
+#endif
+
+#if MI_HUGE_PAGE_ABANDON
+#if (MI_STAT>0)
+// maintain stats for huge objects
+static void mi_stat_huge_free(const mi_page_t* page) {
+ mi_heap_t* const heap = mi_heap_get_default();
+ const size_t bsize = mi_page_block_size(page); // to match stats in `page.c:mi_page_huge_alloc`
+ if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
+ mi_heap_stat_decrease(heap, large, bsize);
+ }
+ else {
+ mi_heap_stat_decrease(heap, huge, bsize);
+ }
+}
+#else
+static void mi_stat_huge_free(const mi_page_t* page) {
+ MI_UNUSED(page);
+}
+#endif
+#endif
+
+// ------------------------------------------------------
+// Free
+// ------------------------------------------------------
+
+// multi-threaded free (or free in huge block if compiled with MI_HUGE_PAGE_ABANDON)
+static mi_decl_noinline void _mi_free_block_mt(mi_page_t* page, mi_block_t* block)
+{
+ // The padding check may access the non-thread-owned page for the key values.
+ // that is safe as these are constant and the page won't be freed (as the block is not freed yet).
+ mi_check_padding(page, block);
+ _mi_padding_shrink(page, block, sizeof(mi_block_t)); // for small size, ensure we can fit the delayed thread pointers without triggering overflow detection
+
+ // huge page segments are always abandoned and can be freed immediately
+ mi_segment_t* segment = _mi_page_segment(page);
+ if (segment->kind == MI_SEGMENT_HUGE) {
+ #if MI_HUGE_PAGE_ABANDON
+ // huge page segments are always abandoned and can be freed immediately
+ mi_stat_huge_free(page);
+ _mi_segment_huge_page_free(segment, page, block);
+ return;
+ #else
+ // huge pages are special as they occupy the entire segment
+ // as these are large we reset the memory occupied by the page so it is available to other threads
+ // (as the owning thread needs to actually free the memory later).
+ _mi_segment_huge_page_reset(segment, page, block);
+ #endif
+ }
+
+ #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN // note: when tracking, cannot use mi_usable_size with multi-threading
+ if (segment->kind != MI_SEGMENT_HUGE) { // not for huge segments as we just reset the content
+ mi_debug_fill(page, block, MI_DEBUG_FREED, mi_usable_size(block));
+ }
+ #endif
+
+ // Try to put the block on either the page-local thread free list, or the heap delayed free list.
+ mi_thread_free_t tfreex;
+ bool use_delayed;
+ mi_thread_free_t tfree = mi_atomic_load_relaxed(&page->xthread_free);
+ do {
+ use_delayed = (mi_tf_delayed(tfree) == MI_USE_DELAYED_FREE);
+ if mi_unlikely(use_delayed) {
+ // unlikely: this only happens on the first concurrent free in a page that is in the full list
+ tfreex = mi_tf_set_delayed(tfree,MI_DELAYED_FREEING);
+ }
+ else {
+ // usual: directly add to page thread_free list
+ mi_block_set_next(page, block, mi_tf_block(tfree));
+ tfreex = mi_tf_set_block(tfree,block);
+ }
+ } while (!mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex));
+
+ if mi_unlikely(use_delayed) {
+ // racy read on `heap`, but ok because MI_DELAYED_FREEING is set (see `mi_heap_delete` and `mi_heap_collect_abandon`)
+ mi_heap_t* const heap = (mi_heap_t*)(mi_atomic_load_acquire(&page->xheap)); //mi_page_heap(page);
+ mi_assert_internal(heap != NULL);
+ if (heap != NULL) {
+ // add to the delayed free list of this heap. (do this atomically as the lock only protects heap memory validity)
+ mi_block_t* dfree = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free);
+ do {
+ mi_block_set_nextx(heap,block,dfree, heap->keys);
+ } while (!mi_atomic_cas_ptr_weak_release(mi_block_t,&heap->thread_delayed_free, &dfree, block));
+ }
+
+ // and reset the MI_DELAYED_FREEING flag
+ tfree = mi_atomic_load_relaxed(&page->xthread_free);
+ do {
+ tfreex = tfree;
+ mi_assert_internal(mi_tf_delayed(tfree) == MI_DELAYED_FREEING);
+ tfreex = mi_tf_set_delayed(tfree,MI_NO_DELAYED_FREE);
+ } while (!mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex));
+ }
+}
+
+// regular free
+static inline void _mi_free_block(mi_page_t* page, bool local, mi_block_t* block)
+{
+ // and push it on the free list
+ //const size_t bsize = mi_page_block_size(page);
+ if mi_likely(local) {
+ // owning thread can free a block directly
+ if mi_unlikely(mi_check_is_double_free(page, block)) return;
+ mi_check_padding(page, block);
+ #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN
+ if (!mi_page_is_huge(page)) { // huge page content may be already decommitted
+ mi_debug_fill(page, block, MI_DEBUG_FREED, mi_page_block_size(page));
+ }
+ #endif
+ mi_block_set_next(page, block, page->local_free);
+ page->local_free = block;
+ page->used--;
+ if mi_unlikely(mi_page_all_free(page)) {
+ _mi_page_retire(page);
+ }
+ else if mi_unlikely(mi_page_is_in_full(page)) {
+ _mi_page_unfull(page);
+ }
+ }
+ else {
+ _mi_free_block_mt(page,block);
+ }
+}
+
+
+// Adjust a block that was allocated aligned, to the actual start of the block in the page.
+mi_block_t* _mi_page_ptr_unalign(const mi_segment_t* segment, const mi_page_t* page, const void* p) {
+ mi_assert_internal(page!=NULL && p!=NULL);
+ const size_t diff = (uint8_t*)p - _mi_page_start(segment, page, NULL);
+ const size_t adjust = (diff % mi_page_block_size(page));
+ return (mi_block_t*)((uintptr_t)p - adjust);
+}
+
+
+void mi_decl_noinline _mi_free_generic(const mi_segment_t* segment, mi_page_t* page, bool is_local, void* p) mi_attr_noexcept {
+ mi_block_t* const block = (mi_page_has_aligned(page) ? _mi_page_ptr_unalign(segment, page, p) : (mi_block_t*)p);
+ mi_stat_free(page, block); // stat_free may access the padding
+ mi_track_free_size(block, mi_page_usable_size_of(page,block));
+ _mi_free_block(page, is_local, block);
+}
+
+// Get the segment data belonging to a pointer
+// This is just a single `and` in assembly but does further checks in debug mode
+// (and secure mode) if this was a valid pointer.
+static inline mi_segment_t* mi_checked_ptr_segment(const void* p, const char* msg)
+{
+ MI_UNUSED(msg);
+ mi_assert(p != NULL);
+
+#if (MI_DEBUG>0)
+ if mi_unlikely(((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0) {
+ _mi_error_message(EINVAL, "%s: invalid (unaligned) pointer: %p\n", msg, p);
+ return NULL;
+ }
+#endif
+
+ mi_segment_t* const segment = _mi_ptr_segment(p);
+ mi_assert_internal(segment != NULL);
+
+#if 0 && (MI_DEBUG>0)
+ if mi_unlikely(!mi_is_in_heap_region(p)) {
+ #if (MI_INTPTR_SIZE == 8 && defined(__linux__))
+ if (((uintptr_t)p >> 40) != 0x7F) { // linux tends to align large blocks above 0x7F000000000 (issue #640)
+ #else
+ {
+ #endif
+ _mi_warning_message("%s: pointer might not point to a valid heap region: %p\n"
+ "(this may still be a valid very large allocation (over 64MiB))\n", msg, p);
+ if mi_likely(_mi_ptr_cookie(segment) == segment->cookie) {
+ _mi_warning_message("(yes, the previous pointer %p was valid after all)\n", p);
+ }
+ }
+ }
+#endif
+#if (MI_DEBUG>0 || MI_SECURE>=4)
+ if mi_unlikely(_mi_ptr_cookie(segment) != segment->cookie) {
+ _mi_error_message(EINVAL, "%s: pointer does not point to a valid heap space: %p\n", msg, p);
+ return NULL;
+ }
+#endif
+
+ return segment;
+}
+
+// Free a block
+// fast path written carefully to prevent spilling on the stack
+void mi_free(void* p) mi_attr_noexcept
+{
+ if mi_unlikely(p == NULL) return;
+ mi_segment_t* const segment = mi_checked_ptr_segment(p,"mi_free");
+ const bool is_local= (_mi_prim_thread_id() == mi_atomic_load_relaxed(&segment->thread_id));
+ mi_page_t* const page = _mi_segment_page_of(segment, p);
+
+ if mi_likely(is_local) { // thread-local free?
+ if mi_likely(page->flags.full_aligned == 0) // and it is not a full page (full pages need to move from the full bin), nor has aligned blocks (aligned blocks need to be unaligned)
+ {
+ mi_block_t* const block = (mi_block_t*)p;
+ if mi_unlikely(mi_check_is_double_free(page, block)) return;
+ mi_check_padding(page, block);
+ mi_stat_free(page, block);
+ #if (MI_DEBUG>0) && !MI_TRACK_ENABLED && !MI_TSAN
+ mi_debug_fill(page, block, MI_DEBUG_FREED, mi_page_block_size(page));
+ #endif
+ mi_track_free_size(p, mi_page_usable_size_of(page,block)); // faster then mi_usable_size as we already know the page and that p is unaligned
+ mi_block_set_next(page, block, page->local_free);
+ page->local_free = block;
+ if mi_unlikely(--page->used == 0) { // using this expression generates better code than: page->used--; if (mi_page_all_free(page))
+ _mi_page_retire(page);
+ }
+ }
+ else {
+ // page is full or contains (inner) aligned blocks; use generic path
+ _mi_free_generic(segment, page, true, p);
+ }
+ }
+ else {
+ // not thread-local; use generic path
+ _mi_free_generic(segment, page, false, p);
+ }
+}
+
+// return true if successful
+bool _mi_free_delayed_block(mi_block_t* block) {
+ // get segment and page
+ const mi_segment_t* const segment = _mi_ptr_segment(block);
+ mi_assert_internal(_mi_ptr_cookie(segment) == segment->cookie);
+#ifndef Py_GIL_DISABLED
+ // The GC traverses heaps of other threads, which can trigger this assert.
+ mi_assert_internal(_mi_thread_id() == segment->thread_id);
+#endif
+ mi_page_t* const page = _mi_segment_page_of(segment, block);
+
+ // Clear the no-delayed flag so delayed freeing is used again for this page.
+ // This must be done before collecting the free lists on this page -- otherwise
+ // some blocks may end up in the page `thread_free` list with no blocks in the
+ // heap `thread_delayed_free` list which may cause the page to be never freed!
+ // (it would only be freed if we happen to scan it in `mi_page_queue_find_free_ex`)
+ if (!_mi_page_try_use_delayed_free(page, MI_USE_DELAYED_FREE, false /* dont overwrite never delayed */)) {
+ return false;
+ }
+
+ // collect all other non-local frees to ensure up-to-date `used` count
+ _mi_page_free_collect(page, false);
+
+ // and free the block (possibly freeing the page as well since used is updated)
+ _mi_free_block(page, true, block);
+ return true;
+}
+
+// Bytes available in a block
+mi_decl_noinline static size_t mi_page_usable_aligned_size_of(const mi_segment_t* segment, const mi_page_t* page, const void* p) mi_attr_noexcept {
+ const mi_block_t* block = _mi_page_ptr_unalign(segment, page, p);
+ const size_t size = mi_page_usable_size_of(page, block);
+ const ptrdiff_t adjust = (uint8_t*)p - (uint8_t*)block;
+ mi_assert_internal(adjust >= 0 && (size_t)adjust <= size);
+ return (size - adjust);
+}
+
+static inline size_t _mi_usable_size(const void* p, const char* msg) mi_attr_noexcept {
+ if (p == NULL) return 0;
+ const mi_segment_t* const segment = mi_checked_ptr_segment(p, msg);
+ const mi_page_t* const page = _mi_segment_page_of(segment, p);
+ if mi_likely(!mi_page_has_aligned(page)) {
+ const mi_block_t* block = (const mi_block_t*)p;
+ return mi_page_usable_size_of(page, block);
+ }
+ else {
+ // split out to separate routine for improved code generation
+ return mi_page_usable_aligned_size_of(segment, page, p);
+ }
+}
+
+mi_decl_nodiscard size_t mi_usable_size(const void* p) mi_attr_noexcept {
+ return _mi_usable_size(p, "mi_usable_size");
+}
+
+
+// ------------------------------------------------------
+// Allocation extensions
+// ------------------------------------------------------
+
+void mi_free_size(void* p, size_t size) mi_attr_noexcept {
+ MI_UNUSED_RELEASE(size);
+ mi_assert(p == NULL || size <= _mi_usable_size(p,"mi_free_size"));
+ mi_free(p);
+}
+
+void mi_free_size_aligned(void* p, size_t size, size_t alignment) mi_attr_noexcept {
+ MI_UNUSED_RELEASE(alignment);
+ mi_assert(((uintptr_t)p % alignment) == 0);
+ mi_free_size(p,size);
+}
+
+void mi_free_aligned(void* p, size_t alignment) mi_attr_noexcept {
+ MI_UNUSED_RELEASE(alignment);
+ mi_assert(((uintptr_t)p % alignment) == 0);
+ mi_free(p);
+}
+
+mi_decl_nodiscard extern inline mi_decl_restrict void* mi_heap_calloc(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count,size,&total)) return NULL;
+ return mi_heap_zalloc(heap,total);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_calloc(size_t count, size_t size) mi_attr_noexcept {
+ return mi_heap_calloc(mi_prim_get_default_heap(),count,size);
+}
+
+// Uninitialized `calloc`
+mi_decl_nodiscard extern mi_decl_restrict void* mi_heap_mallocn(mi_heap_t* heap, size_t count, size_t size) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count, size, &total)) return NULL;
+ return mi_heap_malloc(heap, total);
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_mallocn(size_t count, size_t size) mi_attr_noexcept {
+ return mi_heap_mallocn(mi_prim_get_default_heap(),count,size);
+}
+
+// Expand (or shrink) in place (or fail)
+void* mi_expand(void* p, size_t newsize) mi_attr_noexcept {
+ #if MI_PADDING
+ // we do not shrink/expand with padding enabled
+ MI_UNUSED(p); MI_UNUSED(newsize);
+ return NULL;
+ #else
+ if (p == NULL) return NULL;
+ const size_t size = _mi_usable_size(p,"mi_expand");
+ if (newsize > size) return NULL;
+ return p; // it fits
+ #endif
+}
+
+void* _mi_heap_realloc_zero(mi_heap_t* heap, void* p, size_t newsize, bool zero) mi_attr_noexcept {
+ // if p == NULL then behave as malloc.
+ // else if size == 0 then reallocate to a zero-sized block (and don't return NULL, just as mi_malloc(0)).
+ // (this means that returning NULL always indicates an error, and `p` will not have been freed in that case.)
+ const size_t size = _mi_usable_size(p,"mi_realloc"); // also works if p == NULL (with size 0)
+ if mi_unlikely(newsize <= size && newsize >= (size / 2) && newsize > 0) { // note: newsize must be > 0 or otherwise we return NULL for realloc(NULL,0)
+ mi_assert_internal(p!=NULL);
+ // todo: do not track as the usable size is still the same in the free; adjust potential padding?
+ // mi_track_resize(p,size,newsize)
+ // if (newsize < size) { mi_track_mem_noaccess((uint8_t*)p + newsize, size - newsize); }
+ return p; // reallocation still fits and not more than 50% waste
+ }
+ void* newp = mi_heap_malloc(heap,newsize);
+ if mi_likely(newp != NULL) {
+ if (zero && newsize > size) {
+ // also set last word in the previous allocation to zero to ensure any padding is zero-initialized
+ const size_t start = (size >= sizeof(intptr_t) ? size - sizeof(intptr_t) : 0);
+ _mi_memzero((uint8_t*)newp + start, newsize - start);
+ }
+ else if (newsize == 0) {
+ ((uint8_t*)newp)[0] = 0; // work around for applications that expect zero-reallocation to be zero initialized (issue #725)
+ }
+ if mi_likely(p != NULL) {
+ const size_t copysize = (newsize > size ? size : newsize);
+ mi_track_mem_defined(p,copysize); // _mi_useable_size may be too large for byte precise memory tracking..
+ _mi_memcpy(newp, p, copysize);
+ mi_free(p); // only free the original pointer if successful
+ }
+ }
+ return newp;
+}
+
+mi_decl_nodiscard void* mi_heap_realloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept {
+ return _mi_heap_realloc_zero(heap, p, newsize, false);
+}
+
+mi_decl_nodiscard void* mi_heap_reallocn(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count, size, &total)) return NULL;
+ return mi_heap_realloc(heap, p, total);
+}
+
+
+// Reallocate but free `p` on errors
+mi_decl_nodiscard void* mi_heap_reallocf(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept {
+ void* newp = mi_heap_realloc(heap, p, newsize);
+ if (newp==NULL && p!=NULL) mi_free(p);
+ return newp;
+}
+
+mi_decl_nodiscard void* mi_heap_rezalloc(mi_heap_t* heap, void* p, size_t newsize) mi_attr_noexcept {
+ return _mi_heap_realloc_zero(heap, p, newsize, true);
+}
+
+mi_decl_nodiscard void* mi_heap_recalloc(mi_heap_t* heap, void* p, size_t count, size_t size) mi_attr_noexcept {
+ size_t total;
+ if (mi_count_size_overflow(count, size, &total)) return NULL;
+ return mi_heap_rezalloc(heap, p, total);
+}
+
+
+mi_decl_nodiscard void* mi_realloc(void* p, size_t newsize) mi_attr_noexcept {
+ return mi_heap_realloc(mi_prim_get_default_heap(),p,newsize);
+}
+
+mi_decl_nodiscard void* mi_reallocn(void* p, size_t count, size_t size) mi_attr_noexcept {
+ return mi_heap_reallocn(mi_prim_get_default_heap(),p,count,size);
+}
+
+// Reallocate but free `p` on errors
+mi_decl_nodiscard void* mi_reallocf(void* p, size_t newsize) mi_attr_noexcept {
+ return mi_heap_reallocf(mi_prim_get_default_heap(),p,newsize);
+}
+
+mi_decl_nodiscard void* mi_rezalloc(void* p, size_t newsize) mi_attr_noexcept {
+ return mi_heap_rezalloc(mi_prim_get_default_heap(), p, newsize);
+}
+
+mi_decl_nodiscard void* mi_recalloc(void* p, size_t count, size_t size) mi_attr_noexcept {
+ return mi_heap_recalloc(mi_prim_get_default_heap(), p, count, size);
+}
+
+
+
+// ------------------------------------------------------
+// strdup, strndup, and realpath
+// ------------------------------------------------------
+
+// `strdup` using mi_malloc
+mi_decl_nodiscard mi_decl_restrict char* mi_heap_strdup(mi_heap_t* heap, const char* s) mi_attr_noexcept {
+ if (s == NULL) return NULL;
+ size_t n = strlen(s);
+ char* t = (char*)mi_heap_malloc(heap,n+1);
+ if (t == NULL) return NULL;
+ _mi_memcpy(t, s, n);
+ t[n] = 0;
+ return t;
+}
+
+mi_decl_nodiscard mi_decl_restrict char* mi_strdup(const char* s) mi_attr_noexcept {
+ return mi_heap_strdup(mi_prim_get_default_heap(), s);
+}
+
+// `strndup` using mi_malloc
+mi_decl_nodiscard mi_decl_restrict char* mi_heap_strndup(mi_heap_t* heap, const char* s, size_t n) mi_attr_noexcept {
+ if (s == NULL) return NULL;
+ const char* end = (const char*)memchr(s, 0, n); // find end of string in the first `n` characters (returns NULL if not found)
+ const size_t m = (end != NULL ? (size_t)(end - s) : n); // `m` is the minimum of `n` or the end-of-string
+ mi_assert_internal(m <= n);
+ char* t = (char*)mi_heap_malloc(heap, m+1);
+ if (t == NULL) return NULL;
+ _mi_memcpy(t, s, m);
+ t[m] = 0;
+ return t;
+}
+
+mi_decl_nodiscard mi_decl_restrict char* mi_strndup(const char* s, size_t n) mi_attr_noexcept {
+ return mi_heap_strndup(mi_prim_get_default_heap(),s,n);
+}
+
+#ifndef __wasi__
+// `realpath` using mi_malloc
+#ifdef _WIN32
+#ifndef PATH_MAX
+#define PATH_MAX MAX_PATH
+#endif
+#include <windows.h>
+mi_decl_nodiscard mi_decl_restrict char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept {
+ // todo: use GetFullPathNameW to allow longer file names
+ char buf[PATH_MAX];
+ DWORD res = GetFullPathNameA(fname, PATH_MAX, (resolved_name == NULL ? buf : resolved_name), NULL);
+ if (res == 0) {
+ errno = GetLastError(); return NULL;
+ }
+ else if (res > PATH_MAX) {
+ errno = EINVAL; return NULL;
+ }
+ else if (resolved_name != NULL) {
+ return resolved_name;
+ }
+ else {
+ return mi_heap_strndup(heap, buf, PATH_MAX);
+ }
+}
+#else
+/*
+#include <unistd.h> // pathconf
+static size_t mi_path_max(void) {
+ static size_t path_max = 0;
+ if (path_max <= 0) {
+ long m = pathconf("/",_PC_PATH_MAX);
+ if (m <= 0) path_max = 4096; // guess
+ else if (m < 256) path_max = 256; // at least 256
+ else path_max = m;
+ }
+ return path_max;
+}
+*/
+char* mi_heap_realpath(mi_heap_t* heap, const char* fname, char* resolved_name) mi_attr_noexcept {
+ if (resolved_name != NULL) {
+ return realpath(fname,resolved_name);
+ }
+ else {
+ char* rname = realpath(fname, NULL);
+ if (rname == NULL) return NULL;
+ char* result = mi_heap_strdup(heap, rname);
+ free(rname); // use regular free! (which may be redirected to our free but that's ok)
+ return result;
+ }
+ /*
+ const size_t n = mi_path_max();
+ char* buf = (char*)mi_malloc(n+1);
+ if (buf == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ char* rname = realpath(fname,buf);
+ char* result = mi_heap_strndup(heap,rname,n); // ok if `rname==NULL`
+ mi_free(buf);
+ return result;
+ }
+ */
+}
+#endif
+
+mi_decl_nodiscard mi_decl_restrict char* mi_realpath(const char* fname, char* resolved_name) mi_attr_noexcept {
+ return mi_heap_realpath(mi_prim_get_default_heap(),fname,resolved_name);
+}
+#endif
+
+/*-------------------------------------------------------
+C++ new and new_aligned
+The standard requires calling into `get_new_handler` and
+throwing the bad_alloc exception on failure. If we compile
+with a C++ compiler we can implement this precisely. If we
+use a C compiler we cannot throw a `bad_alloc` exception
+but we call `exit` instead (i.e. not returning).
+-------------------------------------------------------*/
+
+#ifdef __cplusplus
+#include <new>
+static bool mi_try_new_handler(bool nothrow) {
+ #if defined(_MSC_VER) || (__cplusplus >= 201103L)
+ std::new_handler h = std::get_new_handler();
+ #else
+ std::new_handler h = std::set_new_handler();
+ std::set_new_handler(h);
+ #endif
+ if (h==NULL) {
+ _mi_error_message(ENOMEM, "out of memory in 'new'");
+ if (!nothrow) {
+ throw std::bad_alloc();
+ }
+ return false;
+ }
+ else {
+ h();
+ return true;
+ }
+}
+#else
+typedef void (*std_new_handler_t)(void);
+
+#if (defined(__GNUC__) || (defined(__clang__) && !defined(_MSC_VER))) // exclude clang-cl, see issue #631
+std_new_handler_t __attribute__((weak)) _ZSt15get_new_handlerv(void) {
+ return NULL;
+}
+static std_new_handler_t mi_get_new_handler(void) {
+ return _ZSt15get_new_handlerv();
+}
+#else
+// note: on windows we could dynamically link to `?get_new_handler@std@@YAP6AXXZXZ`.
+static std_new_handler_t mi_get_new_handler() {
+ return NULL;
+}
+#endif
+
+static bool mi_try_new_handler(bool nothrow) {
+ std_new_handler_t h = mi_get_new_handler();
+ if (h==NULL) {
+ _mi_error_message(ENOMEM, "out of memory in 'new'");
+ if (!nothrow) {
+ abort(); // cannot throw in plain C, use abort
+ }
+ return false;
+ }
+ else {
+ h();
+ return true;
+ }
+}
+#endif
+
+mi_decl_export mi_decl_noinline void* mi_heap_try_new(mi_heap_t* heap, size_t size, bool nothrow ) {
+ void* p = NULL;
+ while(p == NULL && mi_try_new_handler(nothrow)) {
+ p = mi_heap_malloc(heap,size);
+ }
+ return p;
+}
+
+static mi_decl_noinline void* mi_try_new(size_t size, bool nothrow) {
+ return mi_heap_try_new(mi_prim_get_default_heap(), size, nothrow);
+}
+
+
+mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new(mi_heap_t* heap, size_t size) {
+ void* p = mi_heap_malloc(heap,size);
+ if mi_unlikely(p == NULL) return mi_heap_try_new(heap, size, false);
+ return p;
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_new(size_t size) {
+ return mi_heap_alloc_new(mi_prim_get_default_heap(), size);
+}
+
+
+mi_decl_nodiscard mi_decl_restrict void* mi_heap_alloc_new_n(mi_heap_t* heap, size_t count, size_t size) {
+ size_t total;
+ if mi_unlikely(mi_count_size_overflow(count, size, &total)) {
+ mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc
+ return NULL;
+ }
+ else {
+ return mi_heap_alloc_new(heap,total);
+ }
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_new_n(size_t count, size_t size) {
+ return mi_heap_alloc_new_n(mi_prim_get_default_heap(), size, count);
+}
+
+
+mi_decl_nodiscard mi_decl_restrict void* mi_new_nothrow(size_t size) mi_attr_noexcept {
+ void* p = mi_malloc(size);
+ if mi_unlikely(p == NULL) return mi_try_new(size, true);
+ return p;
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_new_aligned(size_t size, size_t alignment) {
+ void* p;
+ do {
+ p = mi_malloc_aligned(size, alignment);
+ }
+ while(p == NULL && mi_try_new_handler(false));
+ return p;
+}
+
+mi_decl_nodiscard mi_decl_restrict void* mi_new_aligned_nothrow(size_t size, size_t alignment) mi_attr_noexcept {
+ void* p;
+ do {
+ p = mi_malloc_aligned(size, alignment);
+ }
+ while(p == NULL && mi_try_new_handler(true));
+ return p;
+}
+
+mi_decl_nodiscard void* mi_new_realloc(void* p, size_t newsize) {
+ void* q;
+ do {
+ q = mi_realloc(p, newsize);
+ } while (q == NULL && mi_try_new_handler(false));
+ return q;
+}
+
+mi_decl_nodiscard void* mi_new_reallocn(void* p, size_t newcount, size_t size) {
+ size_t total;
+ if mi_unlikely(mi_count_size_overflow(newcount, size, &total)) {
+ mi_try_new_handler(false); // on overflow we invoke the try_new_handler once to potentially throw std::bad_alloc
+ return NULL;
+ }
+ else {
+ return mi_new_realloc(p, total);
+ }
+}
+
+// ------------------------------------------------------
+// ensure explicit external inline definitions are emitted!
+// ------------------------------------------------------
+
+#ifdef __cplusplus
+void* _mi_externs[] = {
+ (void*)&_mi_page_malloc,
+ (void*)&_mi_heap_malloc_zero,
+ (void*)&_mi_heap_malloc_zero_ex,
+ (void*)&mi_malloc,
+ (void*)&mi_malloc_small,
+ (void*)&mi_zalloc_small,
+ (void*)&mi_heap_malloc,
+ (void*)&mi_heap_zalloc,
+ (void*)&mi_heap_malloc_small,
+ // (void*)&mi_heap_alloc_new,
+ // (void*)&mi_heap_alloc_new_n
+};
+#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/arena.c b/contrib/tools/python3/Objects/mimalloc/arena.c
new file mode 100644
index 00000000000..f8883603860
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/arena.c
@@ -0,0 +1,935 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019-2023, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* ----------------------------------------------------------------------------
+"Arenas" are fixed area's of OS memory from which we can allocate
+large blocks (>= MI_ARENA_MIN_BLOCK_SIZE, 4MiB).
+In contrast to the rest of mimalloc, the arenas are shared between
+threads and need to be accessed using atomic operations.
+
+Arenas are used to for huge OS page (1GiB) reservations or for reserving
+OS memory upfront which can be improve performance or is sometimes needed
+on embedded devices. We can also employ this with WASI or `sbrk` systems
+to reserve large arenas upfront and be able to reuse the memory more effectively.
+
+The arena allocation needs to be thread safe and we use an atomic bitmap to allocate.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+
+#include <string.h> // memset
+#include <errno.h> // ENOMEM
+
+#include "bitmap.h" // atomic bitmap
+
+/* -----------------------------------------------------------
+ Arena allocation
+----------------------------------------------------------- */
+
+// Block info: bit 0 contains the `in_use` bit, the upper bits the
+// size in count of arena blocks.
+typedef uintptr_t mi_block_info_t;
+#define MI_ARENA_BLOCK_SIZE (MI_SEGMENT_SIZE) // 64MiB (must be at least MI_SEGMENT_ALIGN)
+#define MI_ARENA_MIN_OBJ_SIZE (MI_ARENA_BLOCK_SIZE/2) // 32MiB
+#define MI_MAX_ARENAS (112) // not more than 126 (since we use 7 bits in the memid and an arena index + 1)
+
+// A memory arena descriptor
+typedef struct mi_arena_s {
+ mi_arena_id_t id; // arena id; 0 for non-specific
+ mi_memid_t memid; // memid of the memory area
+ _Atomic(uint8_t*) start; // the start of the memory area
+ size_t block_count; // size of the area in arena blocks (of `MI_ARENA_BLOCK_SIZE`)
+ size_t field_count; // number of bitmap fields (where `field_count * MI_BITMAP_FIELD_BITS >= block_count`)
+ size_t meta_size; // size of the arena structure itself (including its bitmaps)
+ mi_memid_t meta_memid; // memid of the arena structure itself (OS or static allocation)
+ int numa_node; // associated NUMA node
+ bool exclusive; // only allow allocations if specifically for this arena
+ bool is_large; // memory area consists of large- or huge OS pages (always committed)
+ _Atomic(size_t) search_idx; // optimization to start the search for free blocks
+ _Atomic(mi_msecs_t) purge_expire; // expiration time when blocks should be decommitted from `blocks_decommit`.
+ mi_bitmap_field_t* blocks_dirty; // are the blocks potentially non-zero?
+ mi_bitmap_field_t* blocks_committed; // are the blocks committed? (can be NULL for memory that cannot be decommitted)
+ mi_bitmap_field_t* blocks_purge; // blocks that can be (reset) decommitted. (can be NULL for memory that cannot be (reset) decommitted)
+ mi_bitmap_field_t blocks_inuse[1]; // in-place bitmap of in-use blocks (of size `field_count`)
+} mi_arena_t;
+
+
+// The available arenas
+static mi_decl_cache_align _Atomic(mi_arena_t*) mi_arenas[MI_MAX_ARENAS];
+static mi_decl_cache_align _Atomic(size_t) mi_arena_count; // = 0
+
+
+//static bool mi_manage_os_memory_ex2(void* start, size_t size, bool is_large, int numa_node, bool exclusive, mi_memid_t memid, mi_arena_id_t* arena_id) mi_attr_noexcept;
+
+/* -----------------------------------------------------------
+ Arena id's
+ id = arena_index + 1
+----------------------------------------------------------- */
+
+static size_t mi_arena_id_index(mi_arena_id_t id) {
+ return (size_t)(id <= 0 ? MI_MAX_ARENAS : id - 1);
+}
+
+static mi_arena_id_t mi_arena_id_create(size_t arena_index) {
+ mi_assert_internal(arena_index < MI_MAX_ARENAS);
+ return (int)arena_index + 1;
+}
+
+mi_arena_id_t _mi_arena_id_none(void) {
+ return 0;
+}
+
+static bool mi_arena_id_is_suitable(mi_arena_id_t arena_id, bool arena_is_exclusive, mi_arena_id_t req_arena_id) {
+ return ((!arena_is_exclusive && req_arena_id == _mi_arena_id_none()) ||
+ (arena_id == req_arena_id));
+}
+
+bool _mi_arena_memid_is_suitable(mi_memid_t memid, mi_arena_id_t request_arena_id) {
+ if (memid.memkind == MI_MEM_ARENA) {
+ return mi_arena_id_is_suitable(memid.mem.arena.id, memid.mem.arena.is_exclusive, request_arena_id);
+ }
+ else {
+ return mi_arena_id_is_suitable(0, false, request_arena_id);
+ }
+}
+
+bool _mi_arena_memid_is_os_allocated(mi_memid_t memid) {
+ return (memid.memkind == MI_MEM_OS);
+}
+
+/* -----------------------------------------------------------
+ Arena allocations get a (currently) 16-bit memory id where the
+ lower 8 bits are the arena id, and the upper bits the block index.
+----------------------------------------------------------- */
+
+static size_t mi_block_count_of_size(size_t size) {
+ return _mi_divide_up(size, MI_ARENA_BLOCK_SIZE);
+}
+
+static size_t mi_arena_block_size(size_t bcount) {
+ return (bcount * MI_ARENA_BLOCK_SIZE);
+}
+
+static size_t mi_arena_size(mi_arena_t* arena) {
+ return mi_arena_block_size(arena->block_count);
+}
+
+static mi_memid_t mi_memid_create_arena(mi_arena_id_t id, bool is_exclusive, mi_bitmap_index_t bitmap_index) {
+ mi_memid_t memid = _mi_memid_create(MI_MEM_ARENA);
+ memid.mem.arena.id = id;
+ memid.mem.arena.block_index = bitmap_index;
+ memid.mem.arena.is_exclusive = is_exclusive;
+ return memid;
+}
+
+static bool mi_arena_memid_indices(mi_memid_t memid, size_t* arena_index, mi_bitmap_index_t* bitmap_index) {
+ mi_assert_internal(memid.memkind == MI_MEM_ARENA);
+ *arena_index = mi_arena_id_index(memid.mem.arena.id);
+ *bitmap_index = memid.mem.arena.block_index;
+ return memid.mem.arena.is_exclusive;
+}
+
+
+
+/* -----------------------------------------------------------
+ Special static area for mimalloc internal structures
+ to avoid OS calls (for example, for the arena metadata)
+----------------------------------------------------------- */
+
+#define MI_ARENA_STATIC_MAX (MI_INTPTR_SIZE*MI_KiB) // 8 KiB on 64-bit
+
+static uint8_t mi_arena_static[MI_ARENA_STATIC_MAX];
+static _Atomic(size_t) mi_arena_static_top;
+
+static void* mi_arena_static_zalloc(size_t size, size_t alignment, mi_memid_t* memid) {
+ *memid = _mi_memid_none();
+ if (size == 0 || size > MI_ARENA_STATIC_MAX) return NULL;
+ if ((mi_atomic_load_relaxed(&mi_arena_static_top) + size) > MI_ARENA_STATIC_MAX) return NULL;
+
+ // try to claim space
+ if (alignment == 0) { alignment = 1; }
+ const size_t oversize = size + alignment - 1;
+ if (oversize > MI_ARENA_STATIC_MAX) return NULL;
+ const size_t oldtop = mi_atomic_add_acq_rel(&mi_arena_static_top, oversize);
+ size_t top = oldtop + oversize;
+ if (top > MI_ARENA_STATIC_MAX) {
+ // try to roll back, ok if this fails
+ mi_atomic_cas_strong_acq_rel(&mi_arena_static_top, &top, oldtop);
+ return NULL;
+ }
+
+ // success
+ *memid = _mi_memid_create(MI_MEM_STATIC);
+ const size_t start = _mi_align_up(oldtop, alignment);
+ uint8_t* const p = &mi_arena_static[start];
+ _mi_memzero(p, size);
+ return p;
+}
+
+static void* mi_arena_meta_zalloc(size_t size, mi_memid_t* memid, mi_stats_t* stats) {
+ *memid = _mi_memid_none();
+
+ // try static
+ void* p = mi_arena_static_zalloc(size, MI_ALIGNMENT_MAX, memid);
+ if (p != NULL) return p;
+
+ // or fall back to the OS
+ return _mi_os_alloc(size, memid, stats);
+}
+
+static void mi_arena_meta_free(void* p, mi_memid_t memid, size_t size, mi_stats_t* stats) {
+ if (mi_memkind_is_os(memid.memkind)) {
+ _mi_os_free(p, size, memid, stats);
+ }
+ else {
+ mi_assert(memid.memkind == MI_MEM_STATIC);
+ }
+}
+
+static void* mi_arena_block_start(mi_arena_t* arena, mi_bitmap_index_t bindex) {
+ return (arena->start + mi_arena_block_size(mi_bitmap_index_bit(bindex)));
+}
+
+
+/* -----------------------------------------------------------
+ Thread safe allocation in an arena
+----------------------------------------------------------- */
+
+// claim the `blocks_inuse` bits
+static bool mi_arena_try_claim(mi_arena_t* arena, size_t blocks, mi_bitmap_index_t* bitmap_idx)
+{
+ size_t idx = 0; // mi_atomic_load_relaxed(&arena->search_idx); // start from last search; ok to be relaxed as the exact start does not matter
+ if (_mi_bitmap_try_find_from_claim_across(arena->blocks_inuse, arena->field_count, idx, blocks, bitmap_idx)) {
+ mi_atomic_store_relaxed(&arena->search_idx, mi_bitmap_index_field(*bitmap_idx)); // start search from found location next time around
+ return true;
+ };
+ return false;
+}
+
+
+/* -----------------------------------------------------------
+ Arena Allocation
+----------------------------------------------------------- */
+
+static mi_decl_noinline void* mi_arena_try_alloc_at(mi_arena_t* arena, size_t arena_index, size_t needed_bcount,
+ bool commit, mi_memid_t* memid, mi_os_tld_t* tld)
+{
+ MI_UNUSED(arena_index);
+ mi_assert_internal(mi_arena_id_index(arena->id) == arena_index);
+
+ mi_bitmap_index_t bitmap_index;
+ if (!mi_arena_try_claim(arena, needed_bcount, &bitmap_index)) return NULL;
+
+ // claimed it!
+ void* p = mi_arena_block_start(arena, bitmap_index);
+ *memid = mi_memid_create_arena(arena->id, arena->exclusive, bitmap_index);
+ memid->is_pinned = arena->memid.is_pinned;
+
+ // none of the claimed blocks should be scheduled for a decommit
+ if (arena->blocks_purge != NULL) {
+ // this is thread safe as a potential purge only decommits parts that are not yet claimed as used (in `blocks_inuse`).
+ _mi_bitmap_unclaim_across(arena->blocks_purge, arena->field_count, needed_bcount, bitmap_index);
+ }
+
+ // set the dirty bits (todo: no need for an atomic op here?)
+ if (arena->memid.initially_zero && arena->blocks_dirty != NULL) {
+ memid->initially_zero = _mi_bitmap_claim_across(arena->blocks_dirty, arena->field_count, needed_bcount, bitmap_index, NULL);
+ }
+
+ // set commit state
+ if (arena->blocks_committed == NULL) {
+ // always committed
+ memid->initially_committed = true;
+ }
+ else if (commit) {
+ // commit requested, but the range may not be committed as a whole: ensure it is committed now
+ memid->initially_committed = true;
+ bool any_uncommitted;
+ _mi_bitmap_claim_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index, &any_uncommitted);
+ if (any_uncommitted) {
+ bool commit_zero = false;
+ if (!_mi_os_commit(p, mi_arena_block_size(needed_bcount), &commit_zero, tld->stats)) {
+ memid->initially_committed = false;
+ }
+ else {
+ if (commit_zero) { memid->initially_zero = true; }
+ }
+ }
+ }
+ else {
+ // no need to commit, but check if already fully committed
+ memid->initially_committed = _mi_bitmap_is_claimed_across(arena->blocks_committed, arena->field_count, needed_bcount, bitmap_index);
+ }
+
+ return p;
+}
+
+// allocate in a speficic arena
+static void* mi_arena_try_alloc_at_id(mi_arena_id_t arena_id, bool match_numa_node, int numa_node, size_t size, size_t alignment,
+ bool commit, bool allow_large, mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld )
+{
+ MI_UNUSED_RELEASE(alignment);
+ mi_assert_internal(alignment <= MI_SEGMENT_ALIGN);
+ const size_t bcount = mi_block_count_of_size(size);
+ const size_t arena_index = mi_arena_id_index(arena_id);
+ mi_assert_internal(arena_index < mi_atomic_load_relaxed(&mi_arena_count));
+ mi_assert_internal(size <= mi_arena_block_size(bcount));
+
+ // Check arena suitability
+ mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[arena_index]);
+ if (arena == NULL) return NULL;
+ if (!allow_large && arena->is_large) return NULL;
+ if (!mi_arena_id_is_suitable(arena->id, arena->exclusive, req_arena_id)) return NULL;
+ if (req_arena_id == _mi_arena_id_none()) { // in not specific, check numa affinity
+ const bool numa_suitable = (numa_node < 0 || arena->numa_node < 0 || arena->numa_node == numa_node);
+ if (match_numa_node) { if (!numa_suitable) return NULL; }
+ else { if (numa_suitable) return NULL; }
+ }
+
+ // try to allocate
+ void* p = mi_arena_try_alloc_at(arena, arena_index, bcount, commit, memid, tld);
+ mi_assert_internal(p == NULL || _mi_is_aligned(p, alignment));
+ return p;
+}
+
+
+// allocate from an arena with fallback to the OS
+static mi_decl_noinline void* mi_arena_try_alloc(int numa_node, size_t size, size_t alignment,
+ bool commit, bool allow_large,
+ mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld )
+{
+ MI_UNUSED(alignment);
+ mi_assert_internal(alignment <= MI_SEGMENT_ALIGN);
+ const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count);
+ if mi_likely(max_arena == 0) return NULL;
+
+ if (req_arena_id != _mi_arena_id_none()) {
+ // try a specific arena if requested
+ if (mi_arena_id_index(req_arena_id) < max_arena) {
+ void* p = mi_arena_try_alloc_at_id(req_arena_id, true, numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld);
+ if (p != NULL) return p;
+ }
+ }
+ else {
+ // try numa affine allocation
+ for (size_t i = 0; i < max_arena; i++) {
+ void* p = mi_arena_try_alloc_at_id(mi_arena_id_create(i), true, numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld);
+ if (p != NULL) return p;
+ }
+
+ // try from another numa node instead..
+ if (numa_node >= 0) { // if numa_node was < 0 (no specific affinity requested), all arena's have been tried already
+ for (size_t i = 0; i < max_arena; i++) {
+ void* p = mi_arena_try_alloc_at_id(mi_arena_id_create(i), false /* only proceed if not numa local */, numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld);
+ if (p != NULL) return p;
+ }
+ }
+ }
+ return NULL;
+}
+
+// try to reserve a fresh arena space
+static bool mi_arena_reserve(size_t req_size, bool allow_large, mi_arena_id_t req_arena_id, mi_arena_id_t *arena_id)
+{
+ if (_mi_preloading()) return false; // use OS only while pre loading
+ if (req_arena_id != _mi_arena_id_none()) return false;
+
+ const size_t arena_count = mi_atomic_load_acquire(&mi_arena_count);
+ if (arena_count > (MI_MAX_ARENAS - 4)) return false;
+
+ size_t arena_reserve = mi_option_get_size(mi_option_arena_reserve);
+ if (arena_reserve == 0) return false;
+
+ if (!_mi_os_has_virtual_reserve()) {
+ arena_reserve = arena_reserve/4; // be conservative if virtual reserve is not supported (for some embedded systems for example)
+ }
+ arena_reserve = _mi_align_up(arena_reserve, MI_ARENA_BLOCK_SIZE);
+ if (arena_count >= 8 && arena_count <= 128) {
+ arena_reserve = ((size_t)1<<(arena_count/8)) * arena_reserve; // scale up the arena sizes exponentially
+ }
+ if (arena_reserve < req_size) return false; // should be able to at least handle the current allocation size
+
+ // commit eagerly?
+ bool arena_commit = false;
+ if (mi_option_get(mi_option_arena_eager_commit) == 2) { arena_commit = _mi_os_has_overcommit(); }
+ else if (mi_option_get(mi_option_arena_eager_commit) == 1) { arena_commit = true; }
+
+ return (mi_reserve_os_memory_ex(arena_reserve, arena_commit, allow_large, false /* exclusive */, arena_id) == 0);
+}
+
+
+void* _mi_arena_alloc_aligned(size_t size, size_t alignment, size_t align_offset, bool commit, bool allow_large,
+ mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld)
+{
+ mi_assert_internal(memid != NULL && tld != NULL);
+ mi_assert_internal(size > 0);
+ *memid = _mi_memid_none();
+
+ const int numa_node = _mi_os_numa_node(tld); // current numa node
+
+ // try to allocate in an arena if the alignment is small enough and the object is not too small (as for heap meta data)
+ if (size >= MI_ARENA_MIN_OBJ_SIZE && alignment <= MI_SEGMENT_ALIGN && align_offset == 0) {
+ void* p = mi_arena_try_alloc(numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld);
+ if (p != NULL) return p;
+
+ // otherwise, try to first eagerly reserve a new arena
+ if (req_arena_id == _mi_arena_id_none()) {
+ mi_arena_id_t arena_id = 0;
+ if (mi_arena_reserve(size, allow_large, req_arena_id, &arena_id)) {
+ // and try allocate in there
+ mi_assert_internal(req_arena_id == _mi_arena_id_none());
+ p = mi_arena_try_alloc_at_id(arena_id, true, numa_node, size, alignment, commit, allow_large, req_arena_id, memid, tld);
+ if (p != NULL) return p;
+ }
+ }
+ }
+
+ // if we cannot use OS allocation, return NULL
+ if (mi_option_is_enabled(mi_option_limit_os_alloc) || req_arena_id != _mi_arena_id_none()) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ // finally, fall back to the OS
+ if (align_offset > 0) {
+ return _mi_os_alloc_aligned_at_offset(size, alignment, align_offset, commit, allow_large, memid, tld->stats);
+ }
+ else {
+ return _mi_os_alloc_aligned(size, alignment, commit, allow_large, memid, tld->stats);
+ }
+}
+
+void* _mi_arena_alloc(size_t size, bool commit, bool allow_large, mi_arena_id_t req_arena_id, mi_memid_t* memid, mi_os_tld_t* tld)
+{
+ return _mi_arena_alloc_aligned(size, MI_ARENA_BLOCK_SIZE, 0, commit, allow_large, req_arena_id, memid, tld);
+}
+
+
+void* mi_arena_area(mi_arena_id_t arena_id, size_t* size) {
+ if (size != NULL) *size = 0;
+ size_t arena_index = mi_arena_id_index(arena_id);
+ if (arena_index >= MI_MAX_ARENAS) return NULL;
+ mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[arena_index]);
+ if (arena == NULL) return NULL;
+ if (size != NULL) { *size = mi_arena_block_size(arena->block_count); }
+ return arena->start;
+}
+
+
+/* -----------------------------------------------------------
+ Arena purge
+----------------------------------------------------------- */
+
+static long mi_arena_purge_delay(void) {
+ // <0 = no purging allowed, 0=immediate purging, >0=milli-second delay
+ return (mi_option_get(mi_option_purge_delay) * mi_option_get(mi_option_arena_purge_mult));
+}
+
+// reset or decommit in an arena and update the committed/decommit bitmaps
+// assumes we own the area (i.e. blocks_in_use is claimed by us)
+static void mi_arena_purge(mi_arena_t* arena, size_t bitmap_idx, size_t blocks, mi_stats_t* stats) {
+ mi_assert_internal(arena->blocks_committed != NULL);
+ mi_assert_internal(arena->blocks_purge != NULL);
+ mi_assert_internal(!arena->memid.is_pinned);
+ const size_t size = mi_arena_block_size(blocks);
+ void* const p = mi_arena_block_start(arena, bitmap_idx);
+ bool needs_recommit;
+ if (_mi_bitmap_is_claimed_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx)) {
+ // all blocks are committed, we can purge freely
+ needs_recommit = _mi_os_purge(p, size, stats);
+ }
+ else {
+ // some blocks are not committed -- this can happen when a partially committed block is freed
+ // in `_mi_arena_free` and it is conservatively marked as uncommitted but still scheduled for a purge
+ // we need to ensure we do not try to reset (as that may be invalid for uncommitted memory),
+ // and also undo the decommit stats (as it was already adjusted)
+ mi_assert_internal(mi_option_is_enabled(mi_option_purge_decommits));
+ needs_recommit = _mi_os_purge_ex(p, size, false /* allow reset? */, stats);
+ _mi_stat_increase(&stats->committed, size);
+ }
+
+ // clear the purged blocks
+ _mi_bitmap_unclaim_across(arena->blocks_purge, arena->field_count, blocks, bitmap_idx);
+ // update committed bitmap
+ if (needs_recommit) {
+ _mi_bitmap_unclaim_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx);
+ }
+}
+
+// Schedule a purge. This is usually delayed to avoid repeated decommit/commit calls.
+// Note: assumes we (still) own the area as we may purge immediately
+static void mi_arena_schedule_purge(mi_arena_t* arena, size_t bitmap_idx, size_t blocks, mi_stats_t* stats) {
+ mi_assert_internal(arena->blocks_purge != NULL);
+ const long delay = mi_arena_purge_delay();
+ if (delay < 0) return; // is purging allowed at all?
+
+ if (_mi_preloading() || delay == 0) {
+ // decommit directly
+ mi_arena_purge(arena, bitmap_idx, blocks, stats);
+ }
+ else {
+ // schedule decommit
+ mi_msecs_t expire = mi_atomic_loadi64_relaxed(&arena->purge_expire);
+ if (expire != 0) {
+ mi_atomic_addi64_acq_rel(&arena->purge_expire, delay/10); // add smallish extra delay
+ }
+ else {
+ mi_atomic_storei64_release(&arena->purge_expire, _mi_clock_now() + delay);
+ }
+ _mi_bitmap_claim_across(arena->blocks_purge, arena->field_count, blocks, bitmap_idx, NULL);
+ }
+}
+
+// purge a range of blocks
+// return true if the full range was purged.
+// assumes we own the area (i.e. blocks_in_use is claimed by us)
+static bool mi_arena_purge_range(mi_arena_t* arena, size_t idx, size_t startidx, size_t bitlen, size_t purge, mi_stats_t* stats) {
+ const size_t endidx = startidx + bitlen;
+ size_t bitidx = startidx;
+ bool all_purged = false;
+ while (bitidx < endidx) {
+ // count consequetive ones in the purge mask
+ size_t count = 0;
+ while (bitidx + count < endidx && (purge & ((size_t)1 << (bitidx + count))) != 0) {
+ count++;
+ }
+ if (count > 0) {
+ // found range to be purged
+ const mi_bitmap_index_t range_idx = mi_bitmap_index_create(idx, bitidx);
+ mi_arena_purge(arena, range_idx, count, stats);
+ if (count == bitlen) {
+ all_purged = true;
+ }
+ }
+ bitidx += (count+1); // +1 to skip the zero bit (or end)
+ }
+ return all_purged;
+}
+
+// returns true if anything was purged
+static bool mi_arena_try_purge(mi_arena_t* arena, mi_msecs_t now, bool force, mi_stats_t* stats)
+{
+ if (arena->memid.is_pinned || arena->blocks_purge == NULL) return false;
+ mi_msecs_t expire = mi_atomic_loadi64_relaxed(&arena->purge_expire);
+ if (expire == 0) return false;
+ if (!force && expire > now) return false;
+
+ // reset expire (if not already set concurrently)
+ mi_atomic_casi64_strong_acq_rel(&arena->purge_expire, &expire, 0);
+
+ // potential purges scheduled, walk through the bitmap
+ bool any_purged = false;
+ bool full_purge = true;
+ for (size_t i = 0; i < arena->field_count; i++) {
+ size_t purge = mi_atomic_load_relaxed(&arena->blocks_purge[i]);
+ if (purge != 0) {
+ size_t bitidx = 0;
+ while (bitidx < MI_BITMAP_FIELD_BITS) {
+ // find consequetive range of ones in the purge mask
+ size_t bitlen = 0;
+ while (bitidx + bitlen < MI_BITMAP_FIELD_BITS && (purge & ((size_t)1 << (bitidx + bitlen))) != 0) {
+ bitlen++;
+ }
+ // try to claim the longest range of corresponding in_use bits
+ const mi_bitmap_index_t bitmap_index = mi_bitmap_index_create(i, bitidx);
+ while( bitlen > 0 ) {
+ if (_mi_bitmap_try_claim(arena->blocks_inuse, arena->field_count, bitlen, bitmap_index)) {
+ break;
+ }
+ bitlen--;
+ }
+ // actual claimed bits at `in_use`
+ if (bitlen > 0) {
+ // read purge again now that we have the in_use bits
+ purge = mi_atomic_load_acquire(&arena->blocks_purge[i]);
+ if (!mi_arena_purge_range(arena, i, bitidx, bitlen, purge, stats)) {
+ full_purge = false;
+ }
+ any_purged = true;
+ // release the claimed `in_use` bits again
+ _mi_bitmap_unclaim(arena->blocks_inuse, arena->field_count, bitlen, bitmap_index);
+ }
+ bitidx += (bitlen+1); // +1 to skip the zero (or end)
+ } // while bitidx
+ } // purge != 0
+ }
+ // if not fully purged, make sure to purge again in the future
+ if (!full_purge) {
+ const long delay = mi_arena_purge_delay();
+ mi_msecs_t expected = 0;
+ mi_atomic_casi64_strong_acq_rel(&arena->purge_expire,&expected,_mi_clock_now() + delay);
+ }
+ return any_purged;
+}
+
+static void mi_arenas_try_purge( bool force, bool visit_all, mi_stats_t* stats ) {
+ if (_mi_preloading() || mi_arena_purge_delay() <= 0) return; // nothing will be scheduled
+
+ const size_t max_arena = mi_atomic_load_acquire(&mi_arena_count);
+ if (max_arena == 0) return;
+
+ // allow only one thread to purge at a time
+ static mi_atomic_guard_t purge_guard;
+ mi_atomic_guard(&purge_guard)
+ {
+ mi_msecs_t now = _mi_clock_now();
+ size_t max_purge_count = (visit_all ? max_arena : 1);
+ for (size_t i = 0; i < max_arena; i++) {
+ mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[i]);
+ if (arena != NULL) {
+ if (mi_arena_try_purge(arena, now, force, stats)) {
+ if (max_purge_count <= 1) break;
+ max_purge_count--;
+ }
+ }
+ }
+ }
+}
+
+
+/* -----------------------------------------------------------
+ Arena free
+----------------------------------------------------------- */
+
+void _mi_arena_free(void* p, size_t size, size_t committed_size, mi_memid_t memid, mi_stats_t* stats) {
+ mi_assert_internal(size > 0 && stats != NULL);
+ mi_assert_internal(committed_size <= size);
+ if (p==NULL) return;
+ if (size==0) return;
+ const bool all_committed = (committed_size == size);
+
+ if (mi_memkind_is_os(memid.memkind)) {
+ // was a direct OS allocation, pass through
+ if (!all_committed && committed_size > 0) {
+ // if partially committed, adjust the committed stats (as `_mi_os_free` will increase decommit by the full size)
+ _mi_stat_decrease(&stats->committed, committed_size);
+ }
+ _mi_os_free(p, size, memid, stats);
+ }
+ else if (memid.memkind == MI_MEM_ARENA) {
+ // allocated in an arena
+ size_t arena_idx;
+ size_t bitmap_idx;
+ mi_arena_memid_indices(memid, &arena_idx, &bitmap_idx);
+ mi_assert_internal(arena_idx < MI_MAX_ARENAS);
+ mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t,&mi_arenas[arena_idx]);
+ mi_assert_internal(arena != NULL);
+ const size_t blocks = mi_block_count_of_size(size);
+
+ // checks
+ if (arena == NULL) {
+ _mi_error_message(EINVAL, "trying to free from non-existent arena: %p, size %zu, memid: 0x%zx\n", p, size, memid);
+ return;
+ }
+ mi_assert_internal(arena->field_count > mi_bitmap_index_field(bitmap_idx));
+ if (arena->field_count <= mi_bitmap_index_field(bitmap_idx)) {
+ _mi_error_message(EINVAL, "trying to free from non-existent arena block: %p, size %zu, memid: 0x%zx\n", p, size, memid);
+ return;
+ }
+
+ // need to set all memory to undefined as some parts may still be marked as no_access (like padding etc.)
+ mi_track_mem_undefined(p,size);
+
+ // potentially decommit
+ if (arena->memid.is_pinned || arena->blocks_committed == NULL) {
+ mi_assert_internal(all_committed);
+ }
+ else {
+ mi_assert_internal(arena->blocks_committed != NULL);
+ mi_assert_internal(arena->blocks_purge != NULL);
+
+ if (!all_committed) {
+ // mark the entire range as no longer committed (so we recommit the full range when re-using)
+ _mi_bitmap_unclaim_across(arena->blocks_committed, arena->field_count, blocks, bitmap_idx);
+ mi_track_mem_noaccess(p,size);
+ if (committed_size > 0) {
+ // if partially committed, adjust the committed stats (is it will be recommitted when re-using)
+ // in the delayed purge, we now need to not count a decommit if the range is not marked as committed.
+ _mi_stat_decrease(&stats->committed, committed_size);
+ }
+ // note: if not all committed, it may be that the purge will reset/decommit the entire range
+ // that contains already decommitted parts. Since purge consistently uses reset or decommit that
+ // works (as we should never reset decommitted parts).
+ }
+ // (delay) purge the entire range
+ mi_arena_schedule_purge(arena, bitmap_idx, blocks, stats);
+ }
+
+ // and make it available to others again
+ bool all_inuse = _mi_bitmap_unclaim_across(arena->blocks_inuse, arena->field_count, blocks, bitmap_idx);
+ if (!all_inuse) {
+ _mi_error_message(EAGAIN, "trying to free an already freed arena block: %p, size %zu\n", p, size);
+ return;
+ };
+ }
+ else {
+ // arena was none, external, or static; nothing to do
+ mi_assert_internal(memid.memkind < MI_MEM_OS);
+ }
+
+ // purge expired decommits
+ mi_arenas_try_purge(false, false, stats);
+}
+
+// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit`
+// for dynamic libraries that are unloaded and need to release all their allocated memory.
+static void mi_arenas_unsafe_destroy(void) {
+ const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count);
+ size_t new_max_arena = 0;
+ for (size_t i = 0; i < max_arena; i++) {
+ mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[i]);
+ if (arena != NULL) {
+ if (arena->start != NULL && mi_memkind_is_os(arena->memid.memkind)) {
+ mi_atomic_store_ptr_release(mi_arena_t, &mi_arenas[i], NULL);
+ _mi_os_free(arena->start, mi_arena_size(arena), arena->memid, &_mi_stats_main);
+ }
+ else {
+ new_max_arena = i;
+ }
+ mi_arena_meta_free(arena, arena->meta_memid, arena->meta_size, &_mi_stats_main);
+ }
+ }
+
+ // try to lower the max arena.
+ size_t expected = max_arena;
+ mi_atomic_cas_strong_acq_rel(&mi_arena_count, &expected, new_max_arena);
+}
+
+// Purge the arenas; if `force_purge` is true, amenable parts are purged even if not yet expired
+void _mi_arena_collect(bool force_purge, mi_stats_t* stats) {
+ mi_arenas_try_purge(force_purge, true /* visit all */, stats);
+}
+
+// destroy owned arenas; this is unsafe and should only be done using `mi_option_destroy_on_exit`
+// for dynamic libraries that are unloaded and need to release all their allocated memory.
+void _mi_arena_unsafe_destroy_all(mi_stats_t* stats) {
+ mi_arenas_unsafe_destroy();
+ _mi_arena_collect(true /* force purge */, stats); // purge non-owned arenas
+}
+
+// Is a pointer inside any of our arenas?
+bool _mi_arena_contains(const void* p) {
+ const size_t max_arena = mi_atomic_load_relaxed(&mi_arena_count);
+ for (size_t i = 0; i < max_arena; i++) {
+ mi_arena_t* arena = mi_atomic_load_ptr_acquire(mi_arena_t, &mi_arenas[i]);
+ if (arena != NULL && arena->start <= (const uint8_t*)p && arena->start + mi_arena_block_size(arena->block_count) > (const uint8_t*)p) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+/* -----------------------------------------------------------
+ Add an arena.
+----------------------------------------------------------- */
+
+static bool mi_arena_add(mi_arena_t* arena, mi_arena_id_t* arena_id) {
+ mi_assert_internal(arena != NULL);
+ mi_assert_internal((uintptr_t)mi_atomic_load_ptr_relaxed(uint8_t,&arena->start) % MI_SEGMENT_ALIGN == 0);
+ mi_assert_internal(arena->block_count > 0);
+ if (arena_id != NULL) { *arena_id = -1; }
+
+ size_t i = mi_atomic_increment_acq_rel(&mi_arena_count);
+ if (i >= MI_MAX_ARENAS) {
+ mi_atomic_decrement_acq_rel(&mi_arena_count);
+ return false;
+ }
+ arena->id = mi_arena_id_create(i);
+ mi_atomic_store_ptr_release(mi_arena_t,&mi_arenas[i], arena);
+ if (arena_id != NULL) { *arena_id = arena->id; }
+ return true;
+}
+
+static bool mi_manage_os_memory_ex2(void* start, size_t size, bool is_large, int numa_node, bool exclusive, mi_memid_t memid, mi_arena_id_t* arena_id) mi_attr_noexcept
+{
+ if (arena_id != NULL) *arena_id = _mi_arena_id_none();
+ if (size < MI_ARENA_BLOCK_SIZE) return false;
+
+ if (is_large) {
+ mi_assert_internal(memid.initially_committed && memid.is_pinned);
+ }
+
+ const size_t bcount = size / MI_ARENA_BLOCK_SIZE;
+ const size_t fields = _mi_divide_up(bcount, MI_BITMAP_FIELD_BITS);
+ const size_t bitmaps = (memid.is_pinned ? 2 : 4);
+ const size_t asize = sizeof(mi_arena_t) + (bitmaps*fields*sizeof(mi_bitmap_field_t));
+ mi_memid_t meta_memid;
+ mi_arena_t* arena = (mi_arena_t*)mi_arena_meta_zalloc(asize, &meta_memid, &_mi_stats_main); // TODO: can we avoid allocating from the OS?
+ if (arena == NULL) return false;
+
+ // already zero'd due to os_alloc
+ // _mi_memzero(arena, asize);
+ arena->id = _mi_arena_id_none();
+ arena->memid = memid;
+ arena->exclusive = exclusive;
+ arena->meta_size = asize;
+ arena->meta_memid = meta_memid;
+ arena->block_count = bcount;
+ arena->field_count = fields;
+ arena->start = (uint8_t*)start;
+ arena->numa_node = numa_node; // TODO: or get the current numa node if -1? (now it allows anyone to allocate on -1)
+ arena->is_large = is_large;
+ arena->purge_expire = 0;
+ arena->search_idx = 0;
+ arena->blocks_dirty = &arena->blocks_inuse[fields]; // just after inuse bitmap
+ arena->blocks_committed = (arena->memid.is_pinned ? NULL : &arena->blocks_inuse[2*fields]); // just after dirty bitmap
+ arena->blocks_purge = (arena->memid.is_pinned ? NULL : &arena->blocks_inuse[3*fields]); // just after committed bitmap
+ // initialize committed bitmap?
+ if (arena->blocks_committed != NULL && arena->memid.initially_committed) {
+ memset((void*)arena->blocks_committed, 0xFF, fields*sizeof(mi_bitmap_field_t)); // cast to void* to avoid atomic warning
+ }
+
+ // and claim leftover blocks if needed (so we never allocate there)
+ ptrdiff_t post = (fields * MI_BITMAP_FIELD_BITS) - bcount;
+ mi_assert_internal(post >= 0);
+ if (post > 0) {
+ // don't use leftover bits at the end
+ mi_bitmap_index_t postidx = mi_bitmap_index_create(fields - 1, MI_BITMAP_FIELD_BITS - post);
+ _mi_bitmap_claim(arena->blocks_inuse, fields, post, postidx, NULL);
+ }
+ return mi_arena_add(arena, arena_id);
+
+}
+
+bool mi_manage_os_memory_ex(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept {
+ mi_memid_t memid = _mi_memid_create(MI_MEM_EXTERNAL);
+ memid.initially_committed = is_committed;
+ memid.initially_zero = is_zero;
+ memid.is_pinned = is_large;
+ return mi_manage_os_memory_ex2(start,size,is_large,numa_node,exclusive,memid, arena_id);
+}
+
+// Reserve a range of regular OS memory
+int mi_reserve_os_memory_ex(size_t size, bool commit, bool allow_large, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept {
+ if (arena_id != NULL) *arena_id = _mi_arena_id_none();
+ size = _mi_align_up(size, MI_ARENA_BLOCK_SIZE); // at least one block
+ mi_memid_t memid;
+ void* start = _mi_os_alloc_aligned(size, MI_SEGMENT_ALIGN, commit, allow_large, &memid, &_mi_stats_main);
+ if (start == NULL) return ENOMEM;
+ const bool is_large = memid.is_pinned; // todo: use separate is_large field?
+ if (!mi_manage_os_memory_ex2(start, size, is_large, -1 /* numa node */, exclusive, memid, arena_id)) {
+ _mi_os_free_ex(start, size, commit, memid, &_mi_stats_main);
+ _mi_verbose_message("failed to reserve %zu k memory\n", _mi_divide_up(size, 1024));
+ return ENOMEM;
+ }
+ _mi_verbose_message("reserved %zu KiB memory%s\n", _mi_divide_up(size, 1024), is_large ? " (in large os pages)" : "");
+ return 0;
+}
+
+
+// Manage a range of regular OS memory
+bool mi_manage_os_memory(void* start, size_t size, bool is_committed, bool is_large, bool is_zero, int numa_node) mi_attr_noexcept {
+ return mi_manage_os_memory_ex(start, size, is_committed, is_large, is_zero, numa_node, false /* exclusive? */, NULL);
+}
+
+// Reserve a range of regular OS memory
+int mi_reserve_os_memory(size_t size, bool commit, bool allow_large) mi_attr_noexcept {
+ return mi_reserve_os_memory_ex(size, commit, allow_large, false, NULL);
+}
+
+
+/* -----------------------------------------------------------
+ Debugging
+----------------------------------------------------------- */
+
+static size_t mi_debug_show_bitmap(const char* prefix, mi_bitmap_field_t* fields, size_t field_count ) {
+ size_t inuse_count = 0;
+ for (size_t i = 0; i < field_count; i++) {
+ char buf[MI_BITMAP_FIELD_BITS + 1];
+ uintptr_t field = mi_atomic_load_relaxed(&fields[i]);
+ for (size_t bit = 0; bit < MI_BITMAP_FIELD_BITS; bit++) {
+ bool inuse = ((((uintptr_t)1 << bit) & field) != 0);
+ if (inuse) inuse_count++;
+ buf[MI_BITMAP_FIELD_BITS - 1 - bit] = (inuse ? 'x' : '.');
+ }
+ buf[MI_BITMAP_FIELD_BITS] = 0;
+ _mi_verbose_message("%s%s\n", prefix, buf);
+ }
+ return inuse_count;
+}
+
+void mi_debug_show_arenas(void) mi_attr_noexcept {
+ size_t max_arenas = mi_atomic_load_relaxed(&mi_arena_count);
+ for (size_t i = 0; i < max_arenas; i++) {
+ mi_arena_t* arena = mi_atomic_load_ptr_relaxed(mi_arena_t, &mi_arenas[i]);
+ if (arena == NULL) break;
+ size_t inuse_count = 0;
+ _mi_verbose_message("arena %zu: %zu blocks with %zu fields\n", i, arena->block_count, arena->field_count);
+ inuse_count += mi_debug_show_bitmap(" ", arena->blocks_inuse, arena->field_count);
+ _mi_verbose_message(" blocks in use ('x'): %zu\n", inuse_count);
+ }
+}
+
+
+/* -----------------------------------------------------------
+ Reserve a huge page arena.
+----------------------------------------------------------- */
+// reserve at a specific numa node
+int mi_reserve_huge_os_pages_at_ex(size_t pages, int numa_node, size_t timeout_msecs, bool exclusive, mi_arena_id_t* arena_id) mi_attr_noexcept {
+ if (arena_id != NULL) *arena_id = -1;
+ if (pages==0) return 0;
+ if (numa_node < -1) numa_node = -1;
+ if (numa_node >= 0) numa_node = numa_node % _mi_os_numa_node_count();
+ size_t hsize = 0;
+ size_t pages_reserved = 0;
+ mi_memid_t memid;
+ void* p = _mi_os_alloc_huge_os_pages(pages, numa_node, timeout_msecs, &pages_reserved, &hsize, &memid);
+ if (p==NULL || pages_reserved==0) {
+ _mi_warning_message("failed to reserve %zu GiB huge pages\n", pages);
+ return ENOMEM;
+ }
+ _mi_verbose_message("numa node %i: reserved %zu GiB huge pages (of the %zu GiB requested)\n", numa_node, pages_reserved, pages);
+
+ if (!mi_manage_os_memory_ex2(p, hsize, true, numa_node, exclusive, memid, arena_id)) {
+ _mi_os_free(p, hsize, memid, &_mi_stats_main);
+ return ENOMEM;
+ }
+ return 0;
+}
+
+int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs) mi_attr_noexcept {
+ return mi_reserve_huge_os_pages_at_ex(pages, numa_node, timeout_msecs, false, NULL);
+}
+
+// reserve huge pages evenly among the given number of numa nodes (or use the available ones as detected)
+int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs) mi_attr_noexcept {
+ if (pages == 0) return 0;
+
+ // pages per numa node
+ size_t numa_count = (numa_nodes > 0 ? numa_nodes : _mi_os_numa_node_count());
+ if (numa_count <= 0) numa_count = 1;
+ const size_t pages_per = pages / numa_count;
+ const size_t pages_mod = pages % numa_count;
+ const size_t timeout_per = (timeout_msecs==0 ? 0 : (timeout_msecs / numa_count) + 50);
+
+ // reserve evenly among numa nodes
+ for (size_t numa_node = 0; numa_node < numa_count && pages > 0; numa_node++) {
+ size_t node_pages = pages_per; // can be 0
+ if (numa_node < pages_mod) node_pages++;
+ int err = mi_reserve_huge_os_pages_at(node_pages, (int)numa_node, timeout_per);
+ if (err) return err;
+ if (pages < node_pages) {
+ pages = 0;
+ }
+ else {
+ pages -= node_pages;
+ }
+ }
+
+ return 0;
+}
+
+int mi_reserve_huge_os_pages(size_t pages, double max_secs, size_t* pages_reserved) mi_attr_noexcept {
+ MI_UNUSED(max_secs);
+ _mi_warning_message("mi_reserve_huge_os_pages is deprecated: use mi_reserve_huge_os_pages_interleave/at instead\n");
+ if (pages_reserved != NULL) *pages_reserved = 0;
+ int err = mi_reserve_huge_os_pages_interleave(pages, 0, (size_t)(max_secs * 1000.0));
+ if (err==0 && pages_reserved!=NULL) *pages_reserved = pages;
+ return err;
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/bitmap.c b/contrib/tools/python3/Objects/mimalloc/bitmap.c
new file mode 100644
index 00000000000..ec3c755822d
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/bitmap.c
@@ -0,0 +1,432 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019-2023 Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* ----------------------------------------------------------------------------
+Concurrent bitmap that can set/reset sequences of bits atomically,
+represeted as an array of fields where each field is a machine word (`size_t`)
+
+There are two api's; the standard one cannot have sequences that cross
+between the bitmap fields (and a sequence must be <= MI_BITMAP_FIELD_BITS).
+
+The `_across` postfixed functions do allow sequences that can cross over
+between the fields. (This is used in arena allocation)
+---------------------------------------------------------------------------- */
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "bitmap.h"
+
+/* -----------------------------------------------------------
+ Bitmap definition
+----------------------------------------------------------- */
+
+// The bit mask for a given number of blocks at a specified bit index.
+static inline size_t mi_bitmap_mask_(size_t count, size_t bitidx) {
+ mi_assert_internal(count + bitidx <= MI_BITMAP_FIELD_BITS);
+ mi_assert_internal(count > 0);
+ if (count >= MI_BITMAP_FIELD_BITS) return MI_BITMAP_FIELD_FULL;
+ if (count == 0) return 0;
+ return ((((size_t)1 << count) - 1) << bitidx);
+}
+
+
+/* -----------------------------------------------------------
+ Claim a bit sequence atomically
+----------------------------------------------------------- */
+
+// Try to atomically claim a sequence of `count` bits in a single
+// field at `idx` in `bitmap`. Returns `true` on success.
+inline bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_t count, mi_bitmap_index_t* bitmap_idx)
+{
+ mi_assert_internal(bitmap_idx != NULL);
+ mi_assert_internal(count <= MI_BITMAP_FIELD_BITS);
+ mi_assert_internal(count > 0);
+ mi_bitmap_field_t* field = &bitmap[idx];
+ size_t map = mi_atomic_load_relaxed(field);
+ if (map==MI_BITMAP_FIELD_FULL) return false; // short cut
+
+ // search for 0-bit sequence of length count
+ const size_t mask = mi_bitmap_mask_(count, 0);
+ const size_t bitidx_max = MI_BITMAP_FIELD_BITS - count;
+
+#ifdef MI_HAVE_FAST_BITSCAN
+ size_t bitidx = mi_ctz(~map); // quickly find the first zero bit if possible
+#else
+ size_t bitidx = 0; // otherwise start at 0
+#endif
+ size_t m = (mask << bitidx); // invariant: m == mask shifted by bitidx
+
+ // scan linearly for a free range of zero bits
+ while (bitidx <= bitidx_max) {
+ const size_t mapm = (map & m);
+ if (mapm == 0) { // are the mask bits free at bitidx?
+ mi_assert_internal((m >> bitidx) == mask); // no overflow?
+ const size_t newmap = (map | m);
+ mi_assert_internal((newmap^map) >> bitidx == mask);
+ if (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)) { // TODO: use weak cas here?
+ // no success, another thread claimed concurrently.. keep going (with updated `map`)
+ continue;
+ }
+ else {
+ // success, we claimed the bits!
+ *bitmap_idx = mi_bitmap_index_create(idx, bitidx);
+ return true;
+ }
+ }
+ else {
+ // on to the next bit range
+#ifdef MI_HAVE_FAST_BITSCAN
+ mi_assert_internal(mapm != 0);
+ const size_t shift = (count == 1 ? 1 : (MI_INTPTR_BITS - mi_clz(mapm) - bitidx));
+ mi_assert_internal(shift > 0 && shift <= count);
+#else
+ const size_t shift = 1;
+#endif
+ bitidx += shift;
+ m <<= shift;
+ }
+ }
+ // no bits found
+ return false;
+}
+
+// Find `count` bits of 0 and set them to 1 atomically; returns `true` on success.
+// Starts at idx, and wraps around to search in all `bitmap_fields` fields.
+// `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields.
+bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx) {
+ size_t idx = start_field_idx;
+ for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) {
+ if (idx >= bitmap_fields) { idx = 0; } // wrap
+ if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fullfilled
+bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields,
+ const size_t start_field_idx, const size_t count,
+ mi_bitmap_pred_fun_t pred_fun, void* pred_arg,
+ mi_bitmap_index_t* bitmap_idx) {
+ size_t idx = start_field_idx;
+ for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) {
+ if (idx >= bitmap_fields) idx = 0; // wrap
+ if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) {
+ if (pred_fun == NULL || pred_fun(*bitmap_idx, pred_arg)) {
+ return true;
+ }
+ // predicate returned false, unclaim and look further
+ _mi_bitmap_unclaim(bitmap, bitmap_fields, count, *bitmap_idx);
+ }
+ }
+ return false;
+}
+
+// Set `count` bits at `bitmap_idx` to 0 atomically
+// Returns `true` if all `count` bits were 1 previously.
+bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ const size_t idx = mi_bitmap_index_field(bitmap_idx);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ const size_t mask = mi_bitmap_mask_(count, bitidx);
+ mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
+ // mi_assert_internal((bitmap[idx] & mask) == mask);
+ const size_t prev = mi_atomic_and_acq_rel(&bitmap[idx], ~mask);
+ return ((prev & mask) == mask);
+}
+
+
+// Set `count` bits at `bitmap_idx` to 1 atomically
+// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
+bool _mi_bitmap_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_zero) {
+ const size_t idx = mi_bitmap_index_field(bitmap_idx);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ const size_t mask = mi_bitmap_mask_(count, bitidx);
+ mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
+ //mi_assert_internal(any_zero != NULL || (bitmap[idx] & mask) == 0);
+ size_t prev = mi_atomic_or_acq_rel(&bitmap[idx], mask);
+ if (any_zero != NULL) { *any_zero = ((prev & mask) != mask); }
+ return ((prev & mask) == 0);
+}
+
+// Returns `true` if all `count` bits were 1. `any_ones` is `true` if there was at least one bit set to one.
+static bool mi_bitmap_is_claimedx(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_ones) {
+ const size_t idx = mi_bitmap_index_field(bitmap_idx);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ const size_t mask = mi_bitmap_mask_(count, bitidx);
+ mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
+ const size_t field = mi_atomic_load_relaxed(&bitmap[idx]);
+ if (any_ones != NULL) { *any_ones = ((field & mask) != 0); }
+ return ((field & mask) == mask);
+}
+
+// Try to set `count` bits at `bitmap_idx` from 0 to 1 atomically.
+// Returns `true` if successful when all previous `count` bits were 0.
+bool _mi_bitmap_try_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ const size_t idx = mi_bitmap_index_field(bitmap_idx);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ const size_t mask = mi_bitmap_mask_(count, bitidx);
+ mi_assert_internal(bitmap_fields > idx); MI_UNUSED(bitmap_fields);
+ size_t expected = mi_atomic_load_relaxed(&bitmap[idx]);
+ do {
+ if ((expected & mask) != 0) return false;
+ }
+ while (!mi_atomic_cas_strong_acq_rel(&bitmap[idx], &expected, expected | mask));
+ mi_assert_internal((expected & mask) == 0);
+ return true;
+}
+
+
+bool _mi_bitmap_is_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ return mi_bitmap_is_claimedx(bitmap, bitmap_fields, count, bitmap_idx, NULL);
+}
+
+bool _mi_bitmap_is_any_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ bool any_ones;
+ mi_bitmap_is_claimedx(bitmap, bitmap_fields, count, bitmap_idx, &any_ones);
+ return any_ones;
+}
+
+
+//--------------------------------------------------------------------------
+// the `_across` functions work on bitmaps where sequences can cross over
+// between the fields. This is used in arena allocation
+//--------------------------------------------------------------------------
+
+// Try to atomically claim a sequence of `count` bits starting from the field
+// at `idx` in `bitmap` and crossing into subsequent fields. Returns `true` on success.
+// Only needs to consider crossing into the next fields (see `mi_bitmap_try_find_from_claim_across`)
+static bool mi_bitmap_try_find_claim_field_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t idx, const size_t count, const size_t retries, mi_bitmap_index_t* bitmap_idx)
+{
+ mi_assert_internal(bitmap_idx != NULL);
+
+ // check initial trailing zeros
+ mi_bitmap_field_t* field = &bitmap[idx];
+ size_t map = mi_atomic_load_relaxed(field);
+ const size_t initial = mi_clz(map); // count of initial zeros starting at idx
+ mi_assert_internal(initial <= MI_BITMAP_FIELD_BITS);
+ if (initial == 0) return false;
+ if (initial >= count) return _mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx); // no need to cross fields (this case won't happen for us)
+ if (_mi_divide_up(count - initial, MI_BITMAP_FIELD_BITS) >= (bitmap_fields - idx)) return false; // not enough entries
+
+ // scan ahead
+ size_t found = initial;
+ size_t mask = 0; // mask bits for the final field
+ while(found < count) {
+ field++;
+ map = mi_atomic_load_relaxed(field);
+ const size_t mask_bits = (found + MI_BITMAP_FIELD_BITS <= count ? MI_BITMAP_FIELD_BITS : (count - found));
+ mi_assert_internal(mask_bits > 0 && mask_bits <= MI_BITMAP_FIELD_BITS);
+ mask = mi_bitmap_mask_(mask_bits, 0);
+ if ((map & mask) != 0) return false; // some part is already claimed
+ found += mask_bits;
+ }
+ mi_assert_internal(field < &bitmap[bitmap_fields]);
+
+ // we found a range of contiguous zeros up to the final field; mask contains mask in the final field
+ // now try to claim the range atomically
+ mi_bitmap_field_t* const final_field = field;
+ const size_t final_mask = mask;
+ mi_bitmap_field_t* const initial_field = &bitmap[idx];
+ const size_t initial_idx = MI_BITMAP_FIELD_BITS - initial;
+ const size_t initial_mask = mi_bitmap_mask_(initial, initial_idx);
+
+ // initial field
+ size_t newmap;
+ field = initial_field;
+ map = mi_atomic_load_relaxed(field);
+ do {
+ newmap = (map | initial_mask);
+ if ((map & initial_mask) != 0) { goto rollback; };
+ } while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
+
+ // intermediate fields
+ while (++field < final_field) {
+ newmap = MI_BITMAP_FIELD_FULL;
+ map = 0;
+ if (!mi_atomic_cas_strong_acq_rel(field, &map, newmap)) { goto rollback; }
+ }
+
+ // final field
+ mi_assert_internal(field == final_field);
+ map = mi_atomic_load_relaxed(field);
+ do {
+ newmap = (map | final_mask);
+ if ((map & final_mask) != 0) { goto rollback; }
+ } while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
+
+ // claimed!
+ *bitmap_idx = mi_bitmap_index_create(idx, initial_idx);
+ return true;
+
+rollback:
+ // roll back intermediate fields
+ // (we just failed to claim `field` so decrement first)
+ while (--field > initial_field) {
+ newmap = 0;
+ map = MI_BITMAP_FIELD_FULL;
+ mi_assert_internal(mi_atomic_load_relaxed(field) == map);
+ mi_atomic_store_release(field, newmap);
+ }
+ if (field == initial_field) { // (if we failed on the initial field, `field + 1 == initial_field`)
+ map = mi_atomic_load_relaxed(field);
+ do {
+ mi_assert_internal((map & initial_mask) == initial_mask);
+ newmap = (map & ~initial_mask);
+ } while (!mi_atomic_cas_strong_acq_rel(field, &map, newmap));
+ }
+ // retry? (we make a recursive call instead of goto to be able to use const declarations)
+ if (retries <= 2) {
+ return mi_bitmap_try_find_claim_field_across(bitmap, bitmap_fields, idx, count, retries+1, bitmap_idx);
+ }
+ else {
+ return false;
+ }
+}
+
+
+// Find `count` bits of zeros and set them to 1 atomically; returns `true` on success.
+// Starts at idx, and wraps around to search in all `bitmap_fields` fields.
+bool _mi_bitmap_try_find_from_claim_across(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx) {
+ mi_assert_internal(count > 0);
+ if (count <= 2) {
+ // we don't bother with crossover fields for small counts
+ return _mi_bitmap_try_find_from_claim(bitmap, bitmap_fields, start_field_idx, count, bitmap_idx);
+ }
+
+ // visit the fields
+ size_t idx = start_field_idx;
+ for (size_t visited = 0; visited < bitmap_fields; visited++, idx++) {
+ if (idx >= bitmap_fields) { idx = 0; } // wrap
+ // first try to claim inside a field
+ if (count <= MI_BITMAP_FIELD_BITS) {
+ if (_mi_bitmap_try_find_claim_field(bitmap, idx, count, bitmap_idx)) {
+ return true;
+ }
+ }
+ // if that fails, then try to claim across fields
+ if (mi_bitmap_try_find_claim_field_across(bitmap, bitmap_fields, idx, count, 0, bitmap_idx)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Helper for masks across fields; returns the mid count, post_mask may be 0
+static size_t mi_bitmap_mask_across(mi_bitmap_index_t bitmap_idx, size_t bitmap_fields, size_t count, size_t* pre_mask, size_t* mid_mask, size_t* post_mask) {
+ MI_UNUSED(bitmap_fields);
+ const size_t bitidx = mi_bitmap_index_bit_in_field(bitmap_idx);
+ if mi_likely(bitidx + count <= MI_BITMAP_FIELD_BITS) {
+ *pre_mask = mi_bitmap_mask_(count, bitidx);
+ *mid_mask = 0;
+ *post_mask = 0;
+ mi_assert_internal(mi_bitmap_index_field(bitmap_idx) < bitmap_fields);
+ return 0;
+ }
+ else {
+ const size_t pre_bits = MI_BITMAP_FIELD_BITS - bitidx;
+ mi_assert_internal(pre_bits < count);
+ *pre_mask = mi_bitmap_mask_(pre_bits, bitidx);
+ count -= pre_bits;
+ const size_t mid_count = (count / MI_BITMAP_FIELD_BITS);
+ *mid_mask = MI_BITMAP_FIELD_FULL;
+ count %= MI_BITMAP_FIELD_BITS;
+ *post_mask = (count==0 ? 0 : mi_bitmap_mask_(count, 0));
+ mi_assert_internal(mi_bitmap_index_field(bitmap_idx) + mid_count + (count==0 ? 0 : 1) < bitmap_fields);
+ return mid_count;
+ }
+}
+
+// Set `count` bits at `bitmap_idx` to 0 atomically
+// Returns `true` if all `count` bits were 1 previously.
+bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ size_t idx = mi_bitmap_index_field(bitmap_idx);
+ size_t pre_mask;
+ size_t mid_mask;
+ size_t post_mask;
+ size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
+ bool all_one = true;
+ mi_bitmap_field_t* field = &bitmap[idx];
+ size_t prev = mi_atomic_and_acq_rel(field++, ~pre_mask); // clear first part
+ if ((prev & pre_mask) != pre_mask) all_one = false;
+ while(mid_count-- > 0) {
+ prev = mi_atomic_and_acq_rel(field++, ~mid_mask); // clear mid part
+ if ((prev & mid_mask) != mid_mask) all_one = false;
+ }
+ if (post_mask!=0) {
+ prev = mi_atomic_and_acq_rel(field, ~post_mask); // clear end part
+ if ((prev & post_mask) != post_mask) all_one = false;
+ }
+ return all_one;
+}
+
+// Set `count` bits at `bitmap_idx` to 1 atomically
+// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
+bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero) {
+ size_t idx = mi_bitmap_index_field(bitmap_idx);
+ size_t pre_mask;
+ size_t mid_mask;
+ size_t post_mask;
+ size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
+ bool all_zero = true;
+ bool any_zero = false;
+ _Atomic(size_t)*field = &bitmap[idx];
+ size_t prev = mi_atomic_or_acq_rel(field++, pre_mask);
+ if ((prev & pre_mask) != 0) all_zero = false;
+ if ((prev & pre_mask) != pre_mask) any_zero = true;
+ while (mid_count-- > 0) {
+ prev = mi_atomic_or_acq_rel(field++, mid_mask);
+ if ((prev & mid_mask) != 0) all_zero = false;
+ if ((prev & mid_mask) != mid_mask) any_zero = true;
+ }
+ if (post_mask!=0) {
+ prev = mi_atomic_or_acq_rel(field, post_mask);
+ if ((prev & post_mask) != 0) all_zero = false;
+ if ((prev & post_mask) != post_mask) any_zero = true;
+ }
+ if (pany_zero != NULL) { *pany_zero = any_zero; }
+ return all_zero;
+}
+
+
+// Returns `true` if all `count` bits were 1.
+// `any_ones` is `true` if there was at least one bit set to one.
+static bool mi_bitmap_is_claimedx_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_ones) {
+ size_t idx = mi_bitmap_index_field(bitmap_idx);
+ size_t pre_mask;
+ size_t mid_mask;
+ size_t post_mask;
+ size_t mid_count = mi_bitmap_mask_across(bitmap_idx, bitmap_fields, count, &pre_mask, &mid_mask, &post_mask);
+ bool all_ones = true;
+ bool any_ones = false;
+ mi_bitmap_field_t* field = &bitmap[idx];
+ size_t prev = mi_atomic_load_relaxed(field++);
+ if ((prev & pre_mask) != pre_mask) all_ones = false;
+ if ((prev & pre_mask) != 0) any_ones = true;
+ while (mid_count-- > 0) {
+ prev = mi_atomic_load_relaxed(field++);
+ if ((prev & mid_mask) != mid_mask) all_ones = false;
+ if ((prev & mid_mask) != 0) any_ones = true;
+ }
+ if (post_mask!=0) {
+ prev = mi_atomic_load_relaxed(field);
+ if ((prev & post_mask) != post_mask) all_ones = false;
+ if ((prev & post_mask) != 0) any_ones = true;
+ }
+ if (pany_ones != NULL) { *pany_ones = any_ones; }
+ return all_ones;
+}
+
+bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ return mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, NULL);
+}
+
+bool _mi_bitmap_is_any_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx) {
+ bool any_ones;
+ mi_bitmap_is_claimedx_across(bitmap, bitmap_fields, count, bitmap_idx, &any_ones);
+ return any_ones;
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/bitmap.h b/contrib/tools/python3/Objects/mimalloc/bitmap.h
new file mode 100644
index 00000000000..d5b15dab776
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/bitmap.h
@@ -0,0 +1,115 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019-2023 Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* ----------------------------------------------------------------------------
+Concurrent bitmap that can set/reset sequences of bits atomically,
+represeted as an array of fields where each field is a machine word (`size_t`)
+
+There are two api's; the standard one cannot have sequences that cross
+between the bitmap fields (and a sequence must be <= MI_BITMAP_FIELD_BITS).
+(this is used in region allocation)
+
+The `_across` postfixed functions do allow sequences that can cross over
+between the fields. (This is used in arena allocation)
+---------------------------------------------------------------------------- */
+#pragma once
+#ifndef MI_BITMAP_H
+#define MI_BITMAP_H
+
+/* -----------------------------------------------------------
+ Bitmap definition
+----------------------------------------------------------- */
+
+#define MI_BITMAP_FIELD_BITS (8*MI_SIZE_SIZE)
+#define MI_BITMAP_FIELD_FULL (~((size_t)0)) // all bits set
+
+// An atomic bitmap of `size_t` fields
+typedef _Atomic(size_t) mi_bitmap_field_t;
+typedef mi_bitmap_field_t* mi_bitmap_t;
+
+// A bitmap index is the index of the bit in a bitmap.
+typedef size_t mi_bitmap_index_t;
+
+// Create a bit index.
+static inline mi_bitmap_index_t mi_bitmap_index_create(size_t idx, size_t bitidx) {
+ mi_assert_internal(bitidx < MI_BITMAP_FIELD_BITS);
+ return (idx*MI_BITMAP_FIELD_BITS) + bitidx;
+}
+
+// Create a bit index.
+static inline mi_bitmap_index_t mi_bitmap_index_create_from_bit(size_t full_bitidx) {
+ return mi_bitmap_index_create(full_bitidx / MI_BITMAP_FIELD_BITS, full_bitidx % MI_BITMAP_FIELD_BITS);
+}
+
+// Get the field index from a bit index.
+static inline size_t mi_bitmap_index_field(mi_bitmap_index_t bitmap_idx) {
+ return (bitmap_idx / MI_BITMAP_FIELD_BITS);
+}
+
+// Get the bit index in a bitmap field
+static inline size_t mi_bitmap_index_bit_in_field(mi_bitmap_index_t bitmap_idx) {
+ return (bitmap_idx % MI_BITMAP_FIELD_BITS);
+}
+
+// Get the full bit index
+static inline size_t mi_bitmap_index_bit(mi_bitmap_index_t bitmap_idx) {
+ return bitmap_idx;
+}
+
+/* -----------------------------------------------------------
+ Claim a bit sequence atomically
+----------------------------------------------------------- */
+
+// Try to atomically claim a sequence of `count` bits in a single
+// field at `idx` in `bitmap`. Returns `true` on success.
+bool _mi_bitmap_try_find_claim_field(mi_bitmap_t bitmap, size_t idx, const size_t count, mi_bitmap_index_t* bitmap_idx);
+
+// Starts at idx, and wraps around to search in all `bitmap_fields` fields.
+// For now, `count` can be at most MI_BITMAP_FIELD_BITS and will never cross fields.
+bool _mi_bitmap_try_find_from_claim(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx);
+
+// Like _mi_bitmap_try_find_from_claim but with an extra predicate that must be fulfilled
+typedef bool (mi_cdecl *mi_bitmap_pred_fun_t)(mi_bitmap_index_t bitmap_idx, void* pred_arg);
+bool _mi_bitmap_try_find_from_claim_pred(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_pred_fun_t pred_fun, void* pred_arg, mi_bitmap_index_t* bitmap_idx);
+
+// Set `count` bits at `bitmap_idx` to 0 atomically
+// Returns `true` if all `count` bits were 1 previously.
+bool _mi_bitmap_unclaim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
+
+// Try to set `count` bits at `bitmap_idx` from 0 to 1 atomically.
+// Returns `true` if successful when all previous `count` bits were 0.
+bool _mi_bitmap_try_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
+
+// Set `count` bits at `bitmap_idx` to 1 atomically
+// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
+bool _mi_bitmap_claim(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* any_zero);
+
+bool _mi_bitmap_is_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
+bool _mi_bitmap_is_any_claimed(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
+
+
+//--------------------------------------------------------------------------
+// the `_across` functions work on bitmaps where sequences can cross over
+// between the fields. This is used in arena allocation
+//--------------------------------------------------------------------------
+
+// Find `count` bits of zeros and set them to 1 atomically; returns `true` on success.
+// Starts at idx, and wraps around to search in all `bitmap_fields` fields.
+bool _mi_bitmap_try_find_from_claim_across(mi_bitmap_t bitmap, const size_t bitmap_fields, const size_t start_field_idx, const size_t count, mi_bitmap_index_t* bitmap_idx);
+
+// Set `count` bits at `bitmap_idx` to 0 atomically
+// Returns `true` if all `count` bits were 1 previously.
+bool _mi_bitmap_unclaim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
+
+// Set `count` bits at `bitmap_idx` to 1 atomically
+// Returns `true` if all `count` bits were 0 previously. `any_zero` is `true` if there was at least one zero bit.
+bool _mi_bitmap_claim_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx, bool* pany_zero);
+
+bool _mi_bitmap_is_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
+bool _mi_bitmap_is_any_claimed_across(mi_bitmap_t bitmap, size_t bitmap_fields, size_t count, mi_bitmap_index_t bitmap_idx);
+
+#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/heap.c b/contrib/tools/python3/Objects/mimalloc/heap.c
new file mode 100644
index 00000000000..26777f39fb6
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/heap.c
@@ -0,0 +1,693 @@
+/*----------------------------------------------------------------------------
+Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+#include "mimalloc/prim.h" // mi_prim_get_default_heap
+
+#include <string.h> // memset, memcpy
+
+#if defined(_MSC_VER) && (_MSC_VER < 1920)
+#pragma warning(disable:4204) // non-constant aggregate initializer
+#endif
+
+/* -----------------------------------------------------------
+ Helpers
+----------------------------------------------------------- */
+
+// return `true` if ok, `false` to break
+typedef bool (heap_page_visitor_fun)(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2);
+
+// Visit all pages in a heap; returns `false` if break was called.
+static bool mi_heap_visit_pages(mi_heap_t* heap, heap_page_visitor_fun* fn, void* arg1, void* arg2)
+{
+ if (heap==NULL || heap->page_count==0) return true;
+
+ // visit all pages
+ #if MI_DEBUG>1
+ size_t total = heap->page_count;
+ size_t count = 0;
+ #endif
+
+ for (size_t i = 0; i <= MI_BIN_FULL; i++) {
+ mi_page_queue_t* pq = &heap->pages[i];
+ mi_page_t* page = pq->first;
+ while(page != NULL) {
+ mi_page_t* next = page->next; // save next in case the page gets removed from the queue
+ mi_assert_internal(mi_page_heap(page) == heap);
+ #if MI_DEBUG>1
+ count++;
+ #endif
+ if (!fn(heap, pq, page, arg1, arg2)) return false;
+ page = next; // and continue
+ }
+ }
+ mi_assert_internal(count == total);
+ return true;
+}
+
+
+#if MI_DEBUG>=2
+static bool mi_heap_page_is_valid(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) {
+ MI_UNUSED(arg1);
+ MI_UNUSED(arg2);
+ MI_UNUSED(pq);
+ mi_assert_internal(mi_page_heap(page) == heap);
+ mi_segment_t* segment = _mi_page_segment(page);
+ mi_assert_internal(segment->thread_id == heap->thread_id);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ return true;
+}
+#endif
+#if MI_DEBUG>=3
+static bool mi_heap_is_valid(mi_heap_t* heap) {
+ mi_assert_internal(heap!=NULL);
+ mi_heap_visit_pages(heap, &mi_heap_page_is_valid, NULL, NULL);
+ return true;
+}
+#endif
+
+
+
+
+/* -----------------------------------------------------------
+ "Collect" pages by migrating `local_free` and `thread_free`
+ lists and freeing empty pages. This is done when a thread
+ stops (and in that case abandons pages if there are still
+ blocks alive)
+----------------------------------------------------------- */
+
+typedef enum mi_collect_e {
+ MI_NORMAL,
+ MI_FORCE,
+ MI_ABANDON
+} mi_collect_t;
+
+
+static bool mi_heap_page_collect(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg_collect, void* arg2 ) {
+ MI_UNUSED(arg2);
+ MI_UNUSED(heap);
+ mi_assert_internal(mi_heap_page_is_valid(heap, pq, page, NULL, NULL));
+ mi_collect_t collect = *((mi_collect_t*)arg_collect);
+ _mi_page_free_collect(page, collect >= MI_FORCE);
+ if (mi_page_all_free(page)) {
+ // no more used blocks, free the page.
+ // note: this will free retired pages as well.
+ bool freed = _PyMem_mi_page_maybe_free(page, pq, collect >= MI_FORCE);
+ if (!freed && collect == MI_ABANDON) {
+ _mi_page_abandon(page, pq);
+ }
+ }
+ else if (collect == MI_ABANDON) {
+ // still used blocks but the thread is done; abandon the page
+ _mi_page_abandon(page, pq);
+ }
+ return true; // don't break
+}
+
+static bool mi_heap_page_never_delayed_free(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) {
+ MI_UNUSED(arg1);
+ MI_UNUSED(arg2);
+ MI_UNUSED(heap);
+ MI_UNUSED(pq);
+ _mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false);
+ return true; // don't break
+}
+
+static void mi_heap_collect_ex(mi_heap_t* heap, mi_collect_t collect)
+{
+ if (heap==NULL || !mi_heap_is_initialized(heap)) return;
+
+ const bool force = collect >= MI_FORCE;
+ _mi_deferred_free(heap, force);
+
+ // gh-112532: we may be called from a thread that is not the owner of the heap
+ bool is_main_thread = _mi_is_main_thread() && heap->thread_id == _mi_thread_id();
+
+ // note: never reclaim on collect but leave it to threads that need storage to reclaim
+ const bool force_main =
+ #ifdef NDEBUG
+ collect == MI_FORCE
+ #else
+ collect >= MI_FORCE
+ #endif
+ && is_main_thread && mi_heap_is_backing(heap) && !heap->no_reclaim;
+
+ if (force_main) {
+ // the main thread is abandoned (end-of-program), try to reclaim all abandoned segments.
+ // if all memory is freed by now, all segments should be freed.
+ _mi_abandoned_reclaim_all(heap, &heap->tld->segments);
+ }
+
+ // if abandoning, mark all pages to no longer add to delayed_free
+ if (collect == MI_ABANDON) {
+ mi_heap_visit_pages(heap, &mi_heap_page_never_delayed_free, NULL, NULL);
+ }
+
+ // free all current thread delayed blocks.
+ // (if abandoning, after this there are no more thread-delayed references into the pages.)
+ _mi_heap_delayed_free_all(heap);
+
+ // collect retired pages
+ _mi_heap_collect_retired(heap, force);
+
+ // free pages that were delayed with QSBR
+ _PyMem_mi_heap_collect_qsbr(heap);
+
+ // collect all pages owned by this thread
+ mi_heap_visit_pages(heap, &mi_heap_page_collect, &collect, NULL);
+ mi_assert_internal( collect != MI_ABANDON || mi_atomic_load_ptr_acquire(mi_block_t,&heap->thread_delayed_free) == NULL );
+
+ // collect abandoned segments (in particular, purge expired parts of segments in the abandoned segment list)
+ // note: forced purge can be quite expensive if many threads are created/destroyed so we do not force on abandonment
+ _mi_abandoned_collect(heap, collect == MI_FORCE /* force? */, &heap->tld->segments);
+
+ // collect segment local caches
+ if (force) {
+ _mi_segment_thread_collect(&heap->tld->segments);
+ }
+
+ // collect regions on program-exit (or shared library unload)
+ if (force && is_main_thread && mi_heap_is_backing(heap)) {
+ _mi_thread_data_collect(); // collect thread data cache
+ _mi_arena_collect(true /* force purge */, &heap->tld->stats);
+ }
+}
+
+void _mi_heap_collect_abandon(mi_heap_t* heap) {
+ mi_heap_collect_ex(heap, MI_ABANDON);
+}
+
+void mi_heap_collect(mi_heap_t* heap, bool force) mi_attr_noexcept {
+ mi_heap_collect_ex(heap, (force ? MI_FORCE : MI_NORMAL));
+}
+
+void mi_collect(bool force) mi_attr_noexcept {
+ mi_heap_collect(mi_prim_get_default_heap(), force);
+}
+
+
+/* -----------------------------------------------------------
+ Heap new
+----------------------------------------------------------- */
+
+mi_heap_t* mi_heap_get_default(void) {
+ mi_thread_init();
+ return mi_prim_get_default_heap();
+}
+
+static bool mi_heap_is_default(const mi_heap_t* heap) {
+ return (heap == mi_prim_get_default_heap());
+}
+
+
+mi_heap_t* mi_heap_get_backing(void) {
+ mi_heap_t* heap = mi_heap_get_default();
+ mi_assert_internal(heap!=NULL);
+ mi_heap_t* bheap = heap->tld->heap_backing;
+ mi_assert_internal(bheap!=NULL);
+ mi_assert_internal(bheap->thread_id == _mi_thread_id());
+ return bheap;
+}
+
+void _mi_heap_init_ex(mi_heap_t* heap, mi_tld_t* tld, mi_arena_id_t arena_id, bool no_reclaim, uint8_t tag)
+{
+ _mi_memcpy_aligned(heap, &_mi_heap_empty, sizeof(mi_heap_t));
+ heap->tld = tld;
+ heap->thread_id = _mi_thread_id();
+ heap->arena_id = arena_id;
+ if (heap == tld->heap_backing) {
+ _mi_random_init(&heap->random);
+ }
+ else {
+ _mi_random_split(&tld->heap_backing->random, &heap->random);
+ }
+ heap->cookie = _mi_heap_random_next(heap) | 1;
+ heap->keys[0] = _mi_heap_random_next(heap);
+ heap->keys[1] = _mi_heap_random_next(heap);
+ heap->no_reclaim = no_reclaim;
+ heap->tag = tag;
+ // push on the thread local heaps list
+ heap->next = heap->tld->heaps;
+ heap->tld->heaps = heap;
+}
+
+mi_decl_nodiscard mi_heap_t* mi_heap_new_in_arena(mi_arena_id_t arena_id) {
+ mi_heap_t* bheap = mi_heap_get_backing();
+ mi_heap_t* heap = mi_heap_malloc_tp(bheap, mi_heap_t); // todo: OS allocate in secure mode?
+ if (heap == NULL) return NULL;
+ // don't reclaim abandoned pages or otherwise destroy is unsafe
+ _mi_heap_init_ex(heap, bheap->tld, arena_id, true, 0);
+ return heap;
+}
+
+mi_decl_nodiscard mi_heap_t* mi_heap_new(void) {
+ return mi_heap_new_in_arena(_mi_arena_id_none());
+}
+
+bool _mi_heap_memid_is_suitable(mi_heap_t* heap, mi_memid_t memid) {
+ return _mi_arena_memid_is_suitable(memid, heap->arena_id);
+}
+
+uintptr_t _mi_heap_random_next(mi_heap_t* heap) {
+ return _mi_random_next(&heap->random);
+}
+
+// zero out the page queues
+static void mi_heap_reset_pages(mi_heap_t* heap) {
+ mi_assert_internal(heap != NULL);
+ mi_assert_internal(mi_heap_is_initialized(heap));
+ // TODO: copy full empty heap instead?
+ memset(&heap->pages_free_direct, 0, sizeof(heap->pages_free_direct));
+ _mi_memcpy_aligned(&heap->pages, &_mi_heap_empty.pages, sizeof(heap->pages));
+ heap->thread_delayed_free = NULL;
+ heap->page_count = 0;
+}
+
+// called from `mi_heap_destroy` and `mi_heap_delete` to free the internal heap resources.
+static void mi_heap_free(mi_heap_t* heap) {
+ mi_assert(heap != NULL);
+ mi_assert_internal(mi_heap_is_initialized(heap));
+ if (heap==NULL || !mi_heap_is_initialized(heap)) return;
+ if (mi_heap_is_backing(heap)) return; // dont free the backing heap
+
+ // reset default
+ if (mi_heap_is_default(heap)) {
+ _mi_heap_set_default_direct(heap->tld->heap_backing);
+ }
+
+ // remove ourselves from the thread local heaps list
+ // linear search but we expect the number of heaps to be relatively small
+ mi_heap_t* prev = NULL;
+ mi_heap_t* curr = heap->tld->heaps;
+ while (curr != heap && curr != NULL) {
+ prev = curr;
+ curr = curr->next;
+ }
+ mi_assert_internal(curr == heap);
+ if (curr == heap) {
+ if (prev != NULL) { prev->next = heap->next; }
+ else { heap->tld->heaps = heap->next; }
+ }
+ mi_assert_internal(heap->tld->heaps != NULL);
+
+ // and free the used memory
+ mi_free(heap);
+}
+
+
+/* -----------------------------------------------------------
+ Heap destroy
+----------------------------------------------------------- */
+
+static bool _mi_heap_page_destroy(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* arg1, void* arg2) {
+ MI_UNUSED(arg1);
+ MI_UNUSED(arg2);
+ MI_UNUSED(heap);
+ MI_UNUSED(pq);
+
+ // ensure no more thread_delayed_free will be added
+ _mi_page_use_delayed_free(page, MI_NEVER_DELAYED_FREE, false);
+
+ // stats
+ const size_t bsize = mi_page_block_size(page);
+ if (bsize > MI_MEDIUM_OBJ_SIZE_MAX) {
+ if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
+ mi_heap_stat_decrease(heap, large, bsize);
+ }
+ else {
+ mi_heap_stat_decrease(heap, huge, bsize);
+ }
+ }
+#if (MI_STAT)
+ _mi_page_free_collect(page, false); // update used count
+ const size_t inuse = page->used;
+ if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
+ mi_heap_stat_decrease(heap, normal, bsize * inuse);
+#if (MI_STAT>1)
+ mi_heap_stat_decrease(heap, normal_bins[_mi_bin(bsize)], inuse);
+#endif
+ }
+ mi_heap_stat_decrease(heap, malloc, bsize * inuse); // todo: off for aligned blocks...
+#endif
+
+ /// pretend it is all free now
+ mi_assert_internal(mi_page_thread_free(page) == NULL);
+ page->used = 0;
+
+ // and free the page
+ // mi_page_free(page,false);
+ page->next = NULL;
+ page->prev = NULL;
+ _mi_segment_page_free(page,false /* no force? */, &heap->tld->segments);
+
+ return true; // keep going
+}
+
+void _mi_heap_destroy_pages(mi_heap_t* heap) {
+ mi_heap_visit_pages(heap, &_mi_heap_page_destroy, NULL, NULL);
+ mi_heap_reset_pages(heap);
+}
+
+#if MI_TRACK_HEAP_DESTROY
+static bool mi_cdecl mi_heap_track_block_free(const mi_heap_t* heap, const mi_heap_area_t* area, void* block, size_t block_size, void* arg) {
+ MI_UNUSED(heap); MI_UNUSED(area); MI_UNUSED(arg); MI_UNUSED(block_size);
+ mi_track_free_size(block,mi_usable_size(block));
+ return true;
+}
+#endif
+
+void mi_heap_destroy(mi_heap_t* heap) {
+ mi_assert(heap != NULL);
+ mi_assert(mi_heap_is_initialized(heap));
+ mi_assert(heap->no_reclaim);
+ mi_assert_expensive(mi_heap_is_valid(heap));
+ if (heap==NULL || !mi_heap_is_initialized(heap)) return;
+ if (!heap->no_reclaim) {
+ // don't free in case it may contain reclaimed pages
+ mi_heap_delete(heap);
+ }
+ else {
+ // track all blocks as freed
+ #if MI_TRACK_HEAP_DESTROY
+ mi_heap_visit_blocks(heap, true, mi_heap_track_block_free, NULL);
+ #endif
+ // free all pages
+ _mi_heap_destroy_pages(heap);
+ mi_heap_free(heap);
+ }
+}
+
+// forcefully destroy all heaps in the current thread
+void _mi_heap_unsafe_destroy_all(void) {
+ mi_heap_t* bheap = mi_heap_get_backing();
+ mi_heap_t* curr = bheap->tld->heaps;
+ while (curr != NULL) {
+ mi_heap_t* next = curr->next;
+ if (curr->no_reclaim) {
+ mi_heap_destroy(curr);
+ }
+ else {
+ _mi_heap_destroy_pages(curr);
+ }
+ curr = next;
+ }
+}
+
+/* -----------------------------------------------------------
+ Safe Heap delete
+----------------------------------------------------------- */
+
+// Transfer the pages from one heap to the other
+static void mi_heap_absorb(mi_heap_t* heap, mi_heap_t* from) {
+ mi_assert_internal(heap!=NULL);
+ if (from==NULL || from->page_count == 0) return;
+
+ // reduce the size of the delayed frees
+ _mi_heap_delayed_free_partial(from);
+
+ // transfer all pages by appending the queues; this will set a new heap field
+ // so threads may do delayed frees in either heap for a while.
+ // note: appending waits for each page to not be in the `MI_DELAYED_FREEING` state
+ // so after this only the new heap will get delayed frees
+ for (size_t i = 0; i <= MI_BIN_FULL; i++) {
+ mi_page_queue_t* pq = &heap->pages[i];
+ mi_page_queue_t* append = &from->pages[i];
+ size_t pcount = _mi_page_queue_append(heap, pq, append);
+ heap->page_count += pcount;
+ from->page_count -= pcount;
+ }
+ mi_assert_internal(from->page_count == 0);
+
+ // and do outstanding delayed frees in the `from` heap
+ // note: be careful here as the `heap` field in all those pages no longer point to `from`,
+ // turns out to be ok as `_mi_heap_delayed_free` only visits the list and calls a
+ // the regular `_mi_free_delayed_block` which is safe.
+ _mi_heap_delayed_free_all(from);
+ #if !defined(_MSC_VER) || (_MSC_VER > 1900) // somehow the following line gives an error in VS2015, issue #353
+ mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_block_t,&from->thread_delayed_free) == NULL);
+ #endif
+
+ // and reset the `from` heap
+ mi_heap_reset_pages(from);
+}
+
+// Safe delete a heap without freeing any still allocated blocks in that heap.
+void mi_heap_delete(mi_heap_t* heap)
+{
+ mi_assert(heap != NULL);
+ mi_assert(mi_heap_is_initialized(heap));
+ mi_assert_expensive(mi_heap_is_valid(heap));
+ if (heap==NULL || !mi_heap_is_initialized(heap)) return;
+
+ if (!mi_heap_is_backing(heap)) {
+ // tranfer still used pages to the backing heap
+ mi_heap_absorb(heap->tld->heap_backing, heap);
+ }
+ else {
+ // the backing heap abandons its pages
+ _mi_heap_collect_abandon(heap);
+ }
+ mi_assert_internal(heap->page_count==0);
+ mi_heap_free(heap);
+}
+
+mi_heap_t* mi_heap_set_default(mi_heap_t* heap) {
+ mi_assert(heap != NULL);
+ mi_assert(mi_heap_is_initialized(heap));
+ if (heap==NULL || !mi_heap_is_initialized(heap)) return NULL;
+ mi_assert_expensive(mi_heap_is_valid(heap));
+ mi_heap_t* old = mi_prim_get_default_heap();
+ _mi_heap_set_default_direct(heap);
+ return old;
+}
+
+
+
+
+/* -----------------------------------------------------------
+ Analysis
+----------------------------------------------------------- */
+
+// static since it is not thread safe to access heaps from other threads.
+static mi_heap_t* mi_heap_of_block(const void* p) {
+ if (p == NULL) return NULL;
+ mi_segment_t* segment = _mi_ptr_segment(p);
+ bool valid = (_mi_ptr_cookie(segment) == segment->cookie);
+ mi_assert_internal(valid);
+ if mi_unlikely(!valid) return NULL;
+ return mi_page_heap(_mi_segment_page_of(segment,p));
+}
+
+bool mi_heap_contains_block(mi_heap_t* heap, const void* p) {
+ mi_assert(heap != NULL);
+ if (heap==NULL || !mi_heap_is_initialized(heap)) return false;
+ return (heap == mi_heap_of_block(p));
+}
+
+
+static bool mi_heap_page_check_owned(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* p, void* vfound) {
+ MI_UNUSED(heap);
+ MI_UNUSED(pq);
+ bool* found = (bool*)vfound;
+ mi_segment_t* segment = _mi_page_segment(page);
+ void* start = _mi_page_start(segment, page, NULL);
+ void* end = (uint8_t*)start + (page->capacity * mi_page_block_size(page));
+ *found = (p >= start && p < end);
+ return (!*found); // continue if not found
+}
+
+bool mi_heap_check_owned(mi_heap_t* heap, const void* p) {
+ mi_assert(heap != NULL);
+ if (heap==NULL || !mi_heap_is_initialized(heap)) return false;
+ if (((uintptr_t)p & (MI_INTPTR_SIZE - 1)) != 0) return false; // only aligned pointers
+ bool found = false;
+ mi_heap_visit_pages(heap, &mi_heap_page_check_owned, (void*)p, &found);
+ return found;
+}
+
+bool mi_check_owned(const void* p) {
+ return mi_heap_check_owned(mi_prim_get_default_heap(), p);
+}
+
+/* -----------------------------------------------------------
+ Visit all heap blocks and areas
+ Todo: enable visiting abandoned pages, and
+ enable visiting all blocks of all heaps across threads
+----------------------------------------------------------- */
+
+// Separate struct to keep `mi_page_t` out of the public interface
+typedef struct mi_heap_area_ex_s {
+ mi_heap_area_t area;
+ mi_page_t* page;
+} mi_heap_area_ex_t;
+
+static void mi_fast_divisor(size_t divisor, size_t* magic, size_t* shift) {
+ mi_assert_internal(divisor > 0 && divisor <= UINT32_MAX);
+ *shift = MI_INTPTR_BITS - mi_clz(divisor - 1);
+ *magic = (size_t)(((1ULL << 32) * ((1ULL << *shift) - divisor)) / divisor + 1);
+}
+
+static size_t mi_fast_divide(size_t n, size_t magic, size_t shift) {
+ mi_assert_internal(n <= UINT32_MAX);
+ return ((((uint64_t) n * magic) >> 32) + n) >> shift;
+}
+
+bool _mi_heap_area_visit_blocks(const mi_heap_area_t* area, mi_page_t *page, mi_block_visit_fun* visitor, void* arg) {
+ mi_assert(area != NULL);
+ if (area==NULL) return true;
+ mi_assert(page != NULL);
+ if (page == NULL) return true;
+
+ mi_assert_internal(page->local_free == NULL);
+ if (page->used == 0) return true;
+
+ const size_t bsize = mi_page_block_size(page);
+ const size_t ubsize = mi_page_usable_block_size(page); // without padding
+ size_t psize;
+ uint8_t* pstart = _mi_page_start(_mi_page_segment(page), page, &psize);
+ mi_heap_t* heap = mi_page_heap(page);
+
+ if (page->capacity == 1) {
+ // optimize page with one block
+ mi_assert_internal(page->used == 1 && page->free == NULL);
+ return visitor(heap, area, pstart, ubsize, arg);
+ }
+
+ if (page->used == page->capacity) {
+ // optimize full pages
+ uint8_t* block = pstart;
+ for (size_t i = 0; i < page->capacity; i++) {
+ if (!visitor(heap, area, block, ubsize, arg)) return false;
+ block += bsize;
+ }
+ return true;
+ }
+
+ // create a bitmap of free blocks.
+ #define MI_MAX_BLOCKS (MI_SMALL_PAGE_SIZE / sizeof(void*))
+ uintptr_t free_map[MI_MAX_BLOCKS / MI_INTPTR_BITS];
+ size_t bmapsize = (page->capacity + MI_INTPTR_BITS - 1) / MI_INTPTR_BITS;
+ memset(free_map, 0, bmapsize * sizeof(uintptr_t));
+
+ if (page->capacity % MI_INTPTR_BITS != 0) {
+ size_t shift = (page->capacity % MI_INTPTR_BITS);
+ uintptr_t mask = (UINTPTR_MAX << shift);
+ free_map[bmapsize-1] = mask;
+ }
+
+ // fast repeated division by the block size
+ size_t magic, shift;
+ mi_fast_divisor(bsize, &magic, &shift);
+
+ #if MI_DEBUG>1
+ size_t free_count = 0;
+ #endif
+ for (mi_block_t* block = page->free; block != NULL; block = mi_block_next(page,block)) {
+ #if MI_DEBUG>1
+ free_count++;
+ #endif
+ mi_assert_internal((uint8_t*)block >= pstart && (uint8_t*)block < (pstart + psize));
+ size_t offset = (uint8_t*)block - pstart;
+ mi_assert_internal(offset % bsize == 0);
+ size_t blockidx = mi_fast_divide(offset, magic, shift);
+ mi_assert_internal(blockidx == offset / bsize);
+ mi_assert_internal(blockidx < MI_MAX_BLOCKS);
+ size_t bitidx = (blockidx / MI_INTPTR_BITS);
+ size_t bit = blockidx - (bitidx * MI_INTPTR_BITS);
+ free_map[bitidx] |= ((uintptr_t)1 << bit);
+ }
+ mi_assert_internal(page->capacity == (free_count + page->used));
+
+ // walk through all blocks skipping the free ones
+ #if MI_DEBUG>1
+ size_t used_count = 0;
+ #endif
+ uint8_t* block = pstart;
+ for (size_t i = 0; i < bmapsize; i++) {
+ if (free_map[i] == 0) {
+ // every block is in use
+ for (size_t j = 0; j < MI_INTPTR_BITS; j++) {
+ #if MI_DEBUG>1
+ used_count++;
+ #endif
+ if (!visitor(heap, area, block, ubsize, arg)) return false;
+ block += bsize;
+ }
+ }
+ else {
+ uintptr_t m = ~free_map[i];
+ while (m) {
+ #if MI_DEBUG>1
+ used_count++;
+ #endif
+ size_t bitidx = mi_ctz(m);
+ if (!visitor(heap, area, block + (bitidx * bsize), ubsize, arg)) return false;
+ m &= m - 1;
+ }
+ block += bsize * MI_INTPTR_BITS;
+ }
+ }
+ mi_assert_internal(page->used == used_count);
+ return true;
+}
+
+typedef bool (mi_heap_area_visit_fun)(const mi_heap_t* heap, const mi_heap_area_ex_t* area, void* arg);
+
+void _mi_heap_area_init(mi_heap_area_t* area, mi_page_t* page) {
+ _mi_page_free_collect(page,true);
+ const size_t bsize = mi_page_block_size(page);
+ const size_t ubsize = mi_page_usable_block_size(page);
+ area->reserved = page->reserved * bsize;
+ area->committed = page->capacity * bsize;
+ area->blocks = _mi_page_start(_mi_page_segment(page), page, NULL);
+ area->used = page->used; // number of blocks in use (#553)
+ area->block_size = ubsize;
+ area->full_block_size = bsize;
+}
+
+static bool mi_heap_visit_areas_page(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_t* page, void* vfun, void* arg) {
+ MI_UNUSED(heap);
+ MI_UNUSED(pq);
+ mi_heap_area_visit_fun* fun = (mi_heap_area_visit_fun*)vfun;
+ mi_heap_area_ex_t xarea;
+ xarea.page = page;
+ _mi_heap_area_init(&xarea.area, page);
+ return fun(heap, &xarea, arg);
+}
+
+// Visit all heap pages as areas
+static bool mi_heap_visit_areas(const mi_heap_t* heap, mi_heap_area_visit_fun* visitor, void* arg) {
+ if (visitor == NULL) return false;
+ return mi_heap_visit_pages((mi_heap_t*)heap, &mi_heap_visit_areas_page, (void*)(visitor), arg); // note: function pointer to void* :-{
+}
+
+// Just to pass arguments
+typedef struct mi_visit_blocks_args_s {
+ bool visit_blocks;
+ mi_block_visit_fun* visitor;
+ void* arg;
+} mi_visit_blocks_args_t;
+
+static bool mi_heap_area_visitor(const mi_heap_t* heap, const mi_heap_area_ex_t* xarea, void* arg) {
+ mi_visit_blocks_args_t* args = (mi_visit_blocks_args_t*)arg;
+ if (!args->visitor(heap, &xarea->area, NULL, xarea->area.block_size, args->arg)) return false;
+ if (args->visit_blocks) {
+ return _mi_heap_area_visit_blocks(&xarea->area, xarea->page, args->visitor, args->arg);
+ }
+ else {
+ return true;
+ }
+}
+
+// Visit all blocks in a heap
+bool mi_heap_visit_blocks(const mi_heap_t* heap, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) {
+ mi_visit_blocks_args_t args = { visit_blocks, visitor, arg };
+ _mi_heap_delayed_free_partial((mi_heap_t *)heap);
+ return mi_heap_visit_areas(heap, &mi_heap_area_visitor, &args);
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/init.c b/contrib/tools/python3/Objects/mimalloc/init.c
new file mode 100644
index 00000000000..81b241063ff
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/init.c
@@ -0,0 +1,687 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2022, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/prim.h"
+
+#include <string.h> // memcpy, memset
+#include <stdlib.h> // atexit
+
+
+// Empty page used to initialize the small free pages array
+const mi_page_t _mi_page_empty;
+
+#define MI_PAGE_EMPTY() ((mi_page_t*)&_mi_page_empty)
+
+#if (MI_SMALL_WSIZE_MAX==128)
+#if (MI_PADDING>0) && (MI_INTPTR_SIZE >= 8)
+#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }
+#elif (MI_PADDING>0)
+#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY(), MI_PAGE_EMPTY(), MI_PAGE_EMPTY() }
+#else
+#define MI_SMALL_PAGES_EMPTY { MI_INIT128(MI_PAGE_EMPTY), MI_PAGE_EMPTY() }
+#endif
+#else
+#error "define right initialization sizes corresponding to MI_SMALL_WSIZE_MAX"
+#endif
+
+// Empty page queues for every bin
+#define QNULL(sz) { NULL, NULL, (sz)*sizeof(uintptr_t) }
+#define MI_PAGE_QUEUES_EMPTY \
+ { QNULL(1), \
+ QNULL( 1), QNULL( 2), QNULL( 3), QNULL( 4), QNULL( 5), QNULL( 6), QNULL( 7), QNULL( 8), /* 8 */ \
+ QNULL( 10), QNULL( 12), QNULL( 14), QNULL( 16), QNULL( 20), QNULL( 24), QNULL( 28), QNULL( 32), /* 16 */ \
+ QNULL( 40), QNULL( 48), QNULL( 56), QNULL( 64), QNULL( 80), QNULL( 96), QNULL( 112), QNULL( 128), /* 24 */ \
+ QNULL( 160), QNULL( 192), QNULL( 224), QNULL( 256), QNULL( 320), QNULL( 384), QNULL( 448), QNULL( 512), /* 32 */ \
+ QNULL( 640), QNULL( 768), QNULL( 896), QNULL( 1024), QNULL( 1280), QNULL( 1536), QNULL( 1792), QNULL( 2048), /* 40 */ \
+ QNULL( 2560), QNULL( 3072), QNULL( 3584), QNULL( 4096), QNULL( 5120), QNULL( 6144), QNULL( 7168), QNULL( 8192), /* 48 */ \
+ QNULL( 10240), QNULL( 12288), QNULL( 14336), QNULL( 16384), QNULL( 20480), QNULL( 24576), QNULL( 28672), QNULL( 32768), /* 56 */ \
+ QNULL( 40960), QNULL( 49152), QNULL( 57344), QNULL( 65536), QNULL( 81920), QNULL( 98304), QNULL(114688), QNULL(131072), /* 64 */ \
+ QNULL(163840), QNULL(196608), QNULL(229376), QNULL(262144), QNULL(327680), QNULL(393216), QNULL(458752), QNULL(524288), /* 72 */ \
+ QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 1 /* 655360, Huge queue */), \
+ QNULL(MI_MEDIUM_OBJ_WSIZE_MAX + 2) /* Full queue */ }
+
+#define MI_STAT_COUNT_NULL() {0,0,0,0}
+
+// Empty statistics
+#if MI_STAT>1
+#define MI_STAT_COUNT_END_NULL() , { MI_STAT_COUNT_NULL(), MI_INIT32(MI_STAT_COUNT_NULL) }
+#else
+#define MI_STAT_COUNT_END_NULL()
+#endif
+
+#define MI_STATS_NULL \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), MI_STAT_COUNT_NULL(), \
+ MI_STAT_COUNT_NULL(), \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, \
+ { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } \
+ MI_STAT_COUNT_END_NULL()
+
+
+// Empty slice span queues for every bin
+#define SQNULL(sz) { NULL, NULL, sz }
+#define MI_SEGMENT_SPAN_QUEUES_EMPTY \
+ { SQNULL(1), \
+ SQNULL( 1), SQNULL( 2), SQNULL( 3), SQNULL( 4), SQNULL( 5), SQNULL( 6), SQNULL( 7), SQNULL( 10), /* 8 */ \
+ SQNULL( 12), SQNULL( 14), SQNULL( 16), SQNULL( 20), SQNULL( 24), SQNULL( 28), SQNULL( 32), SQNULL( 40), /* 16 */ \
+ SQNULL( 48), SQNULL( 56), SQNULL( 64), SQNULL( 80), SQNULL( 96), SQNULL( 112), SQNULL( 128), SQNULL( 160), /* 24 */ \
+ SQNULL( 192), SQNULL( 224), SQNULL( 256), SQNULL( 320), SQNULL( 384), SQNULL( 448), SQNULL( 512), SQNULL( 640), /* 32 */ \
+ SQNULL( 768), SQNULL( 896), SQNULL( 1024) /* 35 */ }
+
+
+// --------------------------------------------------------
+// Statically allocate an empty heap as the initial
+// thread local value for the default heap,
+// and statically allocate the backing heap for the main
+// thread so it can function without doing any allocation
+// itself (as accessing a thread local for the first time
+// may lead to allocation itself on some platforms)
+// --------------------------------------------------------
+
+mi_decl_cache_align const mi_heap_t _mi_heap_empty = {
+ NULL,
+ MI_SMALL_PAGES_EMPTY,
+ MI_PAGE_QUEUES_EMPTY,
+ MI_ATOMIC_VAR_INIT(NULL),
+ 0, // tid
+ 0, // cookie
+ 0, // arena id
+ { 0, 0 }, // keys
+ { {0}, {0}, 0, true }, // random
+ 0, // page count
+ MI_BIN_FULL, 0, // page retired min/max
+ NULL, // next
+ false,
+ 0,
+ 0
+};
+
+#define tld_empty_stats ((mi_stats_t*)((uint8_t*)&tld_empty + offsetof(mi_tld_t,stats)))
+#define tld_empty_os ((mi_os_tld_t*)((uint8_t*)&tld_empty + offsetof(mi_tld_t,os)))
+
+mi_decl_cache_align static const mi_tld_t tld_empty = {
+ 0,
+ false,
+ NULL, NULL,
+ { MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, tld_empty_stats, tld_empty_os, &_mi_abandoned_default }, // segments
+ { 0, tld_empty_stats }, // os
+ { MI_STATS_NULL } // stats
+};
+
+mi_threadid_t _mi_thread_id(void) mi_attr_noexcept {
+ return _mi_prim_thread_id();
+}
+
+// the thread-local default heap for allocation
+mi_decl_thread mi_heap_t* _mi_heap_default = (mi_heap_t*)&_mi_heap_empty;
+
+extern mi_heap_t _mi_heap_main;
+
+static mi_tld_t tld_main = {
+ 0, false,
+ &_mi_heap_main, & _mi_heap_main,
+ { MI_SEGMENT_SPAN_QUEUES_EMPTY, 0, 0, 0, 0, &tld_main.stats, &tld_main.os, &_mi_abandoned_default }, // segments
+ { 0, &tld_main.stats }, // os
+ { MI_STATS_NULL } // stats
+};
+
+mi_heap_t _mi_heap_main = {
+ &tld_main,
+ MI_SMALL_PAGES_EMPTY,
+ MI_PAGE_QUEUES_EMPTY,
+ MI_ATOMIC_VAR_INIT(NULL),
+ 0, // thread id
+ 0, // initial cookie
+ 0, // arena id
+ { 0, 0 }, // the key of the main heap can be fixed (unlike page keys that need to be secure!)
+ { {0x846ca68b}, {0}, 0, true }, // random
+ 0, // page count
+ MI_BIN_FULL, 0, // page retired min/max
+ NULL, // next heap
+ false // can reclaim
+};
+
+bool _mi_process_is_initialized = false; // set to `true` in `mi_process_init`.
+
+mi_stats_t _mi_stats_main = { MI_STATS_NULL };
+
+
+static void mi_heap_main_init(void) {
+ if (_mi_heap_main.cookie == 0) {
+ _mi_heap_main.thread_id = _mi_thread_id();
+ _mi_heap_main.cookie = 1;
+ #if defined(_WIN32) && !defined(MI_SHARED_LIB)
+ _mi_random_init_weak(&_mi_heap_main.random); // prevent allocation failure during bcrypt dll initialization with static linking
+ #else
+ _mi_random_init(&_mi_heap_main.random);
+ #endif
+ _mi_heap_main.cookie = _mi_heap_random_next(&_mi_heap_main);
+ _mi_heap_main.keys[0] = _mi_heap_random_next(&_mi_heap_main);
+ _mi_heap_main.keys[1] = _mi_heap_random_next(&_mi_heap_main);
+ }
+}
+
+mi_heap_t* _mi_heap_main_get(void) {
+ mi_heap_main_init();
+ return &_mi_heap_main;
+}
+
+
+/* -----------------------------------------------------------
+ Initialization and freeing of the thread local heaps
+----------------------------------------------------------- */
+
+// note: in x64 in release build `sizeof(mi_thread_data_t)` is under 4KiB (= OS page size).
+typedef struct mi_thread_data_s {
+ mi_heap_t heap; // must come first due to cast in `_mi_heap_done`
+ mi_tld_t tld;
+ mi_memid_t memid;
+} mi_thread_data_t;
+
+
+// Thread meta-data is allocated directly from the OS. For
+// some programs that do not use thread pools and allocate and
+// destroy many OS threads, this may causes too much overhead
+// per thread so we maintain a small cache of recently freed metadata.
+
+#define TD_CACHE_SIZE (16)
+static _Atomic(mi_thread_data_t*) td_cache[TD_CACHE_SIZE];
+
+static mi_thread_data_t* mi_thread_data_zalloc(void) {
+ // try to find thread metadata in the cache
+ bool is_zero = false;
+ mi_thread_data_t* td = NULL;
+ for (int i = 0; i < TD_CACHE_SIZE; i++) {
+ td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]);
+ if (td != NULL) {
+ // found cached allocation, try use it
+ td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL);
+ if (td != NULL) {
+ break;
+ }
+ }
+ }
+
+ // if that fails, allocate as meta data
+ if (td == NULL) {
+ mi_memid_t memid;
+ td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &memid, &_mi_stats_main);
+ if (td == NULL) {
+ // if this fails, try once more. (issue #257)
+ td = (mi_thread_data_t*)_mi_os_alloc(sizeof(mi_thread_data_t), &memid, &_mi_stats_main);
+ if (td == NULL) {
+ // really out of memory
+ _mi_error_message(ENOMEM, "unable to allocate thread local heap metadata (%zu bytes)\n", sizeof(mi_thread_data_t));
+ }
+ }
+ if (td != NULL) {
+ td->memid = memid;
+ is_zero = memid.initially_zero;
+ }
+ }
+
+ if (td != NULL && !is_zero) {
+ _mi_memzero_aligned(td, sizeof(*td));
+ }
+ return td;
+}
+
+static void mi_thread_data_free( mi_thread_data_t* tdfree ) {
+ // try to add the thread metadata to the cache
+ for (int i = 0; i < TD_CACHE_SIZE; i++) {
+ mi_thread_data_t* td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]);
+ if (td == NULL) {
+ mi_thread_data_t* expected = NULL;
+ if (mi_atomic_cas_ptr_weak_acq_rel(mi_thread_data_t, &td_cache[i], &expected, tdfree)) {
+ return;
+ }
+ }
+ }
+ // if that fails, just free it directly
+ _mi_os_free(tdfree, sizeof(mi_thread_data_t), tdfree->memid, &_mi_stats_main);
+}
+
+void _mi_thread_data_collect(void) {
+ // free all thread metadata from the cache
+ for (int i = 0; i < TD_CACHE_SIZE; i++) {
+ mi_thread_data_t* td = mi_atomic_load_ptr_relaxed(mi_thread_data_t, &td_cache[i]);
+ if (td != NULL) {
+ td = mi_atomic_exchange_ptr_acq_rel(mi_thread_data_t, &td_cache[i], NULL);
+ if (td != NULL) {
+ _mi_os_free(td, sizeof(mi_thread_data_t), td->memid, &_mi_stats_main);
+ }
+ }
+ }
+}
+
+// Initialize the thread local default heap, called from `mi_thread_init`
+static bool _mi_heap_init(void) {
+ if (mi_heap_is_initialized(mi_prim_get_default_heap())) return true;
+ if (_mi_is_main_thread()) {
+ // mi_assert_internal(_mi_heap_main.thread_id != 0); // can happen on freeBSD where alloc is called before any initialization
+ // the main heap is statically allocated
+ mi_heap_main_init();
+ _mi_heap_set_default_direct(&_mi_heap_main);
+ //mi_assert_internal(_mi_heap_default->tld->heap_backing == mi_prim_get_default_heap());
+ }
+ else {
+ // use `_mi_os_alloc` to allocate directly from the OS
+ mi_thread_data_t* td = mi_thread_data_zalloc();
+ if (td == NULL) return false;
+
+ _mi_tld_init(&td->tld, &td->heap);
+ _mi_heap_init_ex(&td->heap, &td->tld, _mi_arena_id_none(), false, 0);
+ _mi_heap_set_default_direct(&td->heap);
+ }
+ return false;
+}
+
+void _mi_tld_init(mi_tld_t* tld, mi_heap_t* bheap) {
+ _mi_memcpy_aligned(tld, &tld_empty, sizeof(*tld));
+ tld->segments.stats = &tld->stats;
+ tld->segments.os = &tld->os;
+ tld->segments.abandoned = &_mi_abandoned_default;
+ tld->os.stats = &tld->stats;
+ tld->heap_backing = bheap;
+}
+
+// Free the thread local default heap (called from `mi_thread_done`)
+static bool _mi_heap_done(mi_heap_t* heap) {
+ if (!mi_heap_is_initialized(heap)) return true;
+
+ // reset default heap
+ _mi_heap_set_default_direct(_mi_is_main_thread() ? &_mi_heap_main : (mi_heap_t*)&_mi_heap_empty);
+
+ // switch to backing heap
+ heap = heap->tld->heap_backing;
+ if (!mi_heap_is_initialized(heap)) return false;
+
+ // delete all non-backing heaps in this thread
+ mi_heap_t* curr = heap->tld->heaps;
+ while (curr != NULL) {
+ mi_heap_t* next = curr->next; // save `next` as `curr` will be freed
+ if (curr != heap) {
+ mi_assert_internal(!mi_heap_is_backing(curr));
+ mi_heap_delete(curr);
+ }
+ curr = next;
+ }
+ mi_assert_internal(heap->tld->heaps == heap && heap->next == NULL);
+ mi_assert_internal(mi_heap_is_backing(heap));
+
+ // collect if not the main thread
+ if (heap != &_mi_heap_main) {
+ _mi_heap_collect_abandon(heap);
+ }
+
+ // merge stats
+ _mi_stats_done(&heap->tld->stats);
+
+ // free if not the main thread
+ if (heap != &_mi_heap_main) {
+ // the following assertion does not always hold for huge segments as those are always treated
+ // as abondened: one may allocate it in one thread, but deallocate in another in which case
+ // the count can be too large or negative. todo: perhaps not count huge segments? see issue #363
+ // mi_assert_internal(heap->tld->segments.count == 0 || heap->thread_id != _mi_thread_id());
+ mi_thread_data_free((mi_thread_data_t*)heap);
+ }
+ else {
+ #if 0
+ // never free the main thread even in debug mode; if a dll is linked statically with mimalloc,
+ // there may still be delete/free calls after the mi_fls_done is called. Issue #207
+ _mi_heap_destroy_pages(heap);
+ mi_assert_internal(heap->tld->heap_backing == &_mi_heap_main);
+ #endif
+ }
+ return false;
+}
+
+
+
+// --------------------------------------------------------
+// Try to run `mi_thread_done()` automatically so any memory
+// owned by the thread but not yet released can be abandoned
+// and re-owned by another thread.
+//
+// 1. windows dynamic library:
+// call from DllMain on DLL_THREAD_DETACH
+// 2. windows static library:
+// use `FlsAlloc` to call a destructor when the thread is done
+// 3. unix, pthreads:
+// use a pthread key to call a destructor when a pthread is done
+//
+// In the last two cases we also need to call `mi_process_init`
+// to set up the thread local keys.
+// --------------------------------------------------------
+
+// Set up handlers so `mi_thread_done` is called automatically
+static void mi_process_setup_auto_thread_done(void) {
+ static bool tls_initialized = false; // fine if it races
+ if (tls_initialized) return;
+ tls_initialized = true;
+ _mi_prim_thread_init_auto_done();
+ _mi_heap_set_default_direct(&_mi_heap_main);
+}
+
+
+bool _mi_is_main_thread(void) {
+ return (_mi_heap_main.thread_id==0 || _mi_heap_main.thread_id == _mi_thread_id());
+}
+
+static _Atomic(size_t) thread_count = MI_ATOMIC_VAR_INIT(1);
+
+size_t _mi_current_thread_count(void) {
+ return mi_atomic_load_relaxed(&thread_count);
+}
+
+// This is called from the `mi_malloc_generic`
+void mi_thread_init(void) mi_attr_noexcept
+{
+ // ensure our process has started already
+ mi_process_init();
+
+ // initialize the thread local default heap
+ // (this will call `_mi_heap_set_default_direct` and thus set the
+ // fiber/pthread key to a non-zero value, ensuring `_mi_thread_done` is called)
+ if (_mi_heap_init()) return; // returns true if already initialized
+
+ _mi_stat_increase(&_mi_stats_main.threads, 1);
+ mi_atomic_increment_relaxed(&thread_count);
+ //_mi_verbose_message("thread init: 0x%zx\n", _mi_thread_id());
+}
+
+void mi_thread_done(void) mi_attr_noexcept {
+ _mi_thread_done(NULL);
+}
+
+void _mi_thread_done(mi_heap_t* heap)
+{
+ // calling with NULL implies using the default heap
+ if (heap == NULL) {
+ heap = mi_prim_get_default_heap();
+ if (heap == NULL) return;
+ }
+
+ // prevent re-entrancy through heap_done/heap_set_default_direct (issue #699)
+ if (!mi_heap_is_initialized(heap)) {
+ return;
+ }
+
+ // adjust stats
+ mi_atomic_decrement_relaxed(&thread_count);
+ _mi_stat_decrease(&_mi_stats_main.threads, 1);
+
+ // check thread-id as on Windows shutdown with FLS the main (exit) thread may call this on thread-local heaps...
+ if (heap->thread_id != _mi_thread_id()) return;
+
+ // abandon the thread local heap
+ if (_mi_heap_done(heap)) return; // returns true if already ran
+}
+
+void _mi_heap_set_default_direct(mi_heap_t* heap) {
+ mi_assert_internal(heap != NULL);
+ #if defined(MI_TLS_SLOT)
+ mi_prim_tls_slot_set(MI_TLS_SLOT,heap);
+ #elif defined(MI_TLS_PTHREAD_SLOT_OFS)
+ *mi_tls_pthread_heap_slot() = heap;
+ #elif defined(MI_TLS_PTHREAD)
+ // we use _mi_heap_default_key
+ #else
+ _mi_heap_default = heap;
+ #endif
+
+ // ensure the default heap is passed to `_mi_thread_done`
+ // setting to a non-NULL value also ensures `mi_thread_done` is called.
+ _mi_prim_thread_associate_default_heap(heap);
+}
+
+
+// --------------------------------------------------------
+// Run functions on process init/done, and thread init/done
+// --------------------------------------------------------
+static void mi_cdecl mi_process_done(void);
+
+static bool os_preloading = true; // true until this module is initialized
+static bool mi_redirected = false; // true if malloc redirects to mi_malloc
+
+// Returns true if this module has not been initialized; Don't use C runtime routines until it returns false.
+bool mi_decl_noinline _mi_preloading(void) {
+ return os_preloading;
+}
+
+mi_decl_nodiscard bool mi_is_redirected(void) mi_attr_noexcept {
+ return mi_redirected;
+}
+
+// Communicate with the redirection module on Windows
+#if defined(_WIN32) && defined(MI_SHARED_LIB) && !defined(MI_WIN_NOREDIRECT)
+#ifdef __cplusplus
+extern "C" {
+#endif
+mi_decl_export void _mi_redirect_entry(DWORD reason) {
+ // called on redirection; careful as this may be called before DllMain
+ if (reason == DLL_PROCESS_ATTACH) {
+ mi_redirected = true;
+ }
+ else if (reason == DLL_PROCESS_DETACH) {
+ mi_redirected = false;
+ }
+ else if (reason == DLL_THREAD_DETACH) {
+ mi_thread_done();
+ }
+}
+__declspec(dllimport) bool mi_cdecl mi_allocator_init(const char** message);
+__declspec(dllimport) void mi_cdecl mi_allocator_done(void);
+#ifdef __cplusplus
+}
+#endif
+#else
+static bool mi_allocator_init(const char** message) {
+ if (message != NULL) *message = NULL;
+ return true;
+}
+static void mi_allocator_done(void) {
+ // nothing to do
+}
+#endif
+
+// Called once by the process loader
+static void mi_process_load(void) {
+ mi_heap_main_init();
+ #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
+ volatile mi_heap_t* dummy = _mi_heap_default; // access TLS to allocate it before setting tls_initialized to true;
+ if (dummy == NULL) return; // use dummy or otherwise the access may get optimized away (issue #697)
+ #endif
+ os_preloading = false;
+ mi_assert_internal(_mi_is_main_thread());
+ #if !(defined(_WIN32) && defined(MI_SHARED_LIB)) // use Dll process detach (see below) instead of atexit (issue #521)
+ atexit(&mi_process_done);
+ #endif
+ _mi_options_init();
+ mi_process_setup_auto_thread_done();
+ mi_process_init();
+ if (mi_redirected) _mi_verbose_message("malloc is redirected.\n");
+
+ // show message from the redirector (if present)
+ const char* msg = NULL;
+ mi_allocator_init(&msg);
+ if (msg != NULL && (mi_option_is_enabled(mi_option_verbose) || mi_option_is_enabled(mi_option_show_errors))) {
+ _mi_fputs(NULL,NULL,NULL,msg);
+ }
+
+ // reseed random
+ _mi_random_reinit_if_weak(&_mi_heap_main.random);
+}
+
+#if defined(_WIN32) && (defined(_M_IX86) || defined(_M_X64))
+#include <intrin.h>
+mi_decl_cache_align bool _mi_cpu_has_fsrm = false;
+
+static void mi_detect_cpu_features(void) {
+ // FSRM for fast rep movsb support (AMD Zen3+ (~2020) or Intel Ice Lake+ (~2017))
+ int32_t cpu_info[4];
+ __cpuid(cpu_info, 7);
+ _mi_cpu_has_fsrm = ((cpu_info[3] & (1 << 4)) != 0); // bit 4 of EDX : see <https://en.wikipedia.org/wiki/CPUID#EAX=7,_ECX=0:_Extended_Features>
+}
+#else
+static void mi_detect_cpu_features(void) {
+ // nothing
+}
+#endif
+
+// Initialize the process; called by thread_init or the process loader
+void mi_process_init(void) mi_attr_noexcept {
+ // ensure we are called once
+ static mi_atomic_once_t process_init;
+ #if _MSC_VER < 1920
+ mi_heap_main_init(); // vs2017 can dynamically re-initialize _mi_heap_main
+ #endif
+ if (!mi_atomic_once(&process_init)) return;
+ _mi_process_is_initialized = true;
+ _mi_verbose_message("process init: 0x%zx\n", _mi_thread_id());
+ mi_process_setup_auto_thread_done();
+
+ mi_detect_cpu_features();
+ _mi_os_init();
+ mi_heap_main_init();
+ #if MI_DEBUG
+ _mi_verbose_message("debug level : %d\n", MI_DEBUG);
+ #endif
+ _mi_verbose_message("secure level: %d\n", MI_SECURE);
+ _mi_verbose_message("mem tracking: %s\n", MI_TRACK_TOOL);
+ #if MI_TSAN
+ _mi_verbose_message("thread sanitizer enabled\n");
+ #endif
+ mi_thread_init();
+
+ #if defined(_WIN32)
+ // On windows, when building as a static lib the FLS cleanup happens to early for the main thread.
+ // To avoid this, set the FLS value for the main thread to NULL so the fls cleanup
+ // will not call _mi_thread_done on the (still executing) main thread. See issue #508.
+ _mi_prim_thread_associate_default_heap(NULL);
+ #endif
+
+ mi_stats_reset(); // only call stat reset *after* thread init (or the heap tld == NULL)
+ mi_track_init();
+
+ if (mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {
+ size_t pages = mi_option_get_clamp(mi_option_reserve_huge_os_pages, 0, 128*1024);
+ long reserve_at = mi_option_get(mi_option_reserve_huge_os_pages_at);
+ if (reserve_at != -1) {
+ mi_reserve_huge_os_pages_at(pages, reserve_at, pages*500);
+ } else {
+ mi_reserve_huge_os_pages_interleave(pages, 0, pages*500);
+ }
+ }
+ if (mi_option_is_enabled(mi_option_reserve_os_memory)) {
+ long ksize = mi_option_get(mi_option_reserve_os_memory);
+ if (ksize > 0) {
+ mi_reserve_os_memory((size_t)ksize*MI_KiB, true /* commit? */, true /* allow large pages? */);
+ }
+ }
+}
+
+// Called when the process is done (through `at_exit`)
+static void mi_cdecl mi_process_done(void) {
+ // only shutdown if we were initialized
+ if (!_mi_process_is_initialized) return;
+ // ensure we are called once
+ static bool process_done = false;
+ if (process_done) return;
+ process_done = true;
+
+ // release any thread specific resources and ensure _mi_thread_done is called on all but the main thread
+ _mi_prim_thread_done_auto_done();
+
+ #ifndef MI_SKIP_COLLECT_ON_EXIT
+ #if (MI_DEBUG || !defined(MI_SHARED_LIB))
+ // free all memory if possible on process exit. This is not needed for a stand-alone process
+ // but should be done if mimalloc is statically linked into another shared library which
+ // is repeatedly loaded/unloaded, see issue #281.
+ mi_collect(true /* force */ );
+ #endif
+ #endif
+
+ // Forcefully release all retained memory; this can be dangerous in general if overriding regular malloc/free
+ // since after process_done there might still be other code running that calls `free` (like at_exit routines,
+ // or C-runtime termination code.
+ if (mi_option_is_enabled(mi_option_destroy_on_exit)) {
+ mi_collect(true /* force */);
+ _mi_heap_unsafe_destroy_all(); // forcefully release all memory held by all heaps (of this thread only!)
+ _mi_arena_unsafe_destroy_all(& _mi_heap_main_get()->tld->stats);
+ }
+
+ if (mi_option_is_enabled(mi_option_show_stats) || mi_option_is_enabled(mi_option_verbose)) {
+ mi_stats_print(NULL);
+ }
+ mi_allocator_done();
+ _mi_verbose_message("process done: 0x%zx\n", _mi_heap_main.thread_id);
+ os_preloading = true; // don't call the C runtime anymore
+}
+
+
+
+#if defined(_WIN32) && defined(MI_SHARED_LIB)
+ // Windows DLL: easy to hook into process_init and thread_done
+ __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE inst, DWORD reason, LPVOID reserved) {
+ MI_UNUSED(reserved);
+ MI_UNUSED(inst);
+ if (reason==DLL_PROCESS_ATTACH) {
+ mi_process_load();
+ }
+ else if (reason==DLL_PROCESS_DETACH) {
+ mi_process_done();
+ }
+ else if (reason==DLL_THREAD_DETACH) {
+ if (!mi_is_redirected()) {
+ mi_thread_done();
+ }
+ }
+ return TRUE;
+ }
+
+#elif defined(_MSC_VER)
+ // MSVC: use data section magic for static libraries
+ // See <https://www.codeguru.com/cpp/misc/misc/applicationcontrol/article.php/c6945/Running-Code-Before-and-After-Main.htm>
+ static int _mi_process_init(void) {
+ mi_process_load();
+ return 0;
+ }
+ typedef int(*_mi_crt_callback_t)(void);
+ #if defined(_M_X64) || defined(_M_ARM64)
+ __pragma(comment(linker, "/include:" "_mi_msvc_initu"))
+ #pragma section(".CRT$XIU", long, read)
+ #else
+ __pragma(comment(linker, "/include:" "__mi_msvc_initu"))
+ #endif
+ #pragma data_seg(".CRT$XIU")
+ mi_decl_externc _mi_crt_callback_t _mi_msvc_initu[] = { &_mi_process_init };
+ #pragma data_seg()
+
+#elif defined(__cplusplus)
+ // C++: use static initialization to detect process start
+ static bool _mi_process_init(void) {
+ mi_process_load();
+ return (_mi_heap_main.thread_id != 0);
+ }
+ static bool mi_initialized = _mi_process_init();
+
+#elif defined(__GNUC__) || defined(__clang__)
+ // GCC,Clang: use the constructor attribute
+ static void __attribute__((constructor)) _mi_process_init(void) {
+ mi_process_load();
+ }
+
+#else
+#pragma message("define a way to call mi_process_load on your platform")
+#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/options.c b/contrib/tools/python3/Objects/mimalloc/options.c
new file mode 100644
index 00000000000..345b560e3e7
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/options.c
@@ -0,0 +1,571 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+#include "mimalloc/prim.h" // mi_prim_out_stderr
+
+#include <stdio.h> // FILE
+#include <stdlib.h> // abort
+#include <stdarg.h>
+
+
+static long mi_max_error_count = 16; // stop outputting errors after this (use < 0 for no limit)
+static long mi_max_warning_count = 16; // stop outputting warnings after this (use < 0 for no limit)
+
+static void mi_add_stderr_output(void);
+
+int mi_version(void) mi_attr_noexcept {
+ return MI_MALLOC_VERSION;
+}
+
+
+// --------------------------------------------------------
+// Options
+// These can be accessed by multiple threads and may be
+// concurrently initialized, but an initializing data race
+// is ok since they resolve to the same value.
+// --------------------------------------------------------
+typedef enum mi_init_e {
+ UNINIT, // not yet initialized
+ DEFAULTED, // not found in the environment, use default value
+ INITIALIZED // found in environment or set explicitly
+} mi_init_t;
+
+typedef struct mi_option_desc_s {
+ long value; // the value
+ mi_init_t init; // is it initialized yet? (from the environment)
+ mi_option_t option; // for debugging: the option index should match the option
+ const char* name; // option name without `mimalloc_` prefix
+ const char* legacy_name; // potential legacy option name
+} mi_option_desc_t;
+
+#define MI_OPTION(opt) mi_option_##opt, #opt, NULL
+#define MI_OPTION_LEGACY(opt,legacy) mi_option_##opt, #opt, #legacy
+
+static mi_option_desc_t options[_mi_option_last] =
+{
+ // stable options
+ #if MI_DEBUG || defined(MI_SHOW_ERRORS)
+ { 1, UNINIT, MI_OPTION(show_errors) },
+ #else
+ { 0, UNINIT, MI_OPTION(show_errors) },
+ #endif
+ { 0, UNINIT, MI_OPTION(show_stats) },
+ { 0, UNINIT, MI_OPTION(verbose) },
+
+ // the following options are experimental and not all combinations make sense.
+ { 1, UNINIT, MI_OPTION(eager_commit) }, // commit per segment directly (4MiB) (but see also `eager_commit_delay`)
+ { 2, UNINIT, MI_OPTION_LEGACY(arena_eager_commit,eager_region_commit) }, // eager commit arena's? 2 is used to enable this only on an OS that has overcommit (i.e. linux)
+ { 1, UNINIT, MI_OPTION_LEGACY(purge_decommits,reset_decommits) }, // purge decommits memory (instead of reset) (note: on linux this uses MADV_DONTNEED for decommit)
+ { 0, UNINIT, MI_OPTION_LEGACY(allow_large_os_pages,large_os_pages) }, // use large OS pages, use only with eager commit to prevent fragmentation of VMA's
+ { 0, UNINIT, MI_OPTION(reserve_huge_os_pages) }, // per 1GiB huge pages
+ {-1, UNINIT, MI_OPTION(reserve_huge_os_pages_at) }, // reserve huge pages at node N
+ { 0, UNINIT, MI_OPTION(reserve_os_memory) },
+ { 0, UNINIT, MI_OPTION(deprecated_segment_cache) }, // cache N segments per thread
+ { 0, UNINIT, MI_OPTION(deprecated_page_reset) }, // reset page memory on free
+ { 0, UNINIT, MI_OPTION_LEGACY(abandoned_page_purge,abandoned_page_reset) }, // reset free page memory when a thread terminates
+ { 0, UNINIT, MI_OPTION(deprecated_segment_reset) }, // reset segment memory on free (needs eager commit)
+#if defined(__NetBSD__)
+ { 0, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed
+#else
+ { 1, UNINIT, MI_OPTION(eager_commit_delay) }, // the first N segments per thread are not eagerly committed (but per page in the segment on demand)
+#endif
+ { 10, UNINIT, MI_OPTION_LEGACY(purge_delay,reset_delay) }, // purge delay in milli-seconds
+ { 0, UNINIT, MI_OPTION(use_numa_nodes) }, // 0 = use available numa nodes, otherwise use at most N nodes.
+ { 0, UNINIT, MI_OPTION(limit_os_alloc) }, // 1 = do not use OS memory for allocation (but only reserved arenas)
+ { 100, UNINIT, MI_OPTION(os_tag) }, // only apple specific for now but might serve more or less related purpose
+ { 16, UNINIT, MI_OPTION(max_errors) }, // maximum errors that are output
+ { 16, UNINIT, MI_OPTION(max_warnings) }, // maximum warnings that are output
+ { 8, UNINIT, MI_OPTION(max_segment_reclaim)}, // max. number of segment reclaims from the abandoned segments per try.
+ { 0, UNINIT, MI_OPTION(destroy_on_exit)}, // release all OS memory on process exit; careful with dangling pointer or after-exit frees!
+ #if (MI_INTPTR_SIZE>4)
+ { 1024L * 1024L, UNINIT, MI_OPTION(arena_reserve) }, // reserve memory N KiB at a time
+ #else
+ { 128L * 1024L, UNINIT, MI_OPTION(arena_reserve) },
+ #endif
+ { 10, UNINIT, MI_OPTION(arena_purge_mult) }, // purge delay multiplier for arena's
+ { 1, UNINIT, MI_OPTION_LEGACY(purge_extend_delay, decommit_extend_delay) },
+};
+
+static void mi_option_init(mi_option_desc_t* desc);
+
+void _mi_options_init(void) {
+ // called on process load; should not be called before the CRT is initialized!
+ // (e.g. do not call this from process_init as that may run before CRT initialization)
+ mi_add_stderr_output(); // now it safe to use stderr for output
+ for(int i = 0; i < _mi_option_last; i++ ) {
+ mi_option_t option = (mi_option_t)i;
+ long l = mi_option_get(option); MI_UNUSED(l); // initialize
+ // if (option != mi_option_verbose)
+ {
+ mi_option_desc_t* desc = &options[option];
+ _mi_verbose_message("option '%s': %ld\n", desc->name, desc->value);
+ }
+ }
+ mi_max_error_count = mi_option_get(mi_option_max_errors);
+ mi_max_warning_count = mi_option_get(mi_option_max_warnings);
+}
+
+mi_decl_nodiscard long mi_option_get(mi_option_t option) {
+ mi_assert(option >= 0 && option < _mi_option_last);
+ if (option < 0 || option >= _mi_option_last) return 0;
+ mi_option_desc_t* desc = &options[option];
+ mi_assert(desc->option == option); // index should match the option
+ if mi_unlikely(desc->init == UNINIT) {
+ mi_option_init(desc);
+ }
+ return desc->value;
+}
+
+mi_decl_nodiscard long mi_option_get_clamp(mi_option_t option, long min, long max) {
+ long x = mi_option_get(option);
+ return (x < min ? min : (x > max ? max : x));
+}
+
+mi_decl_nodiscard size_t mi_option_get_size(mi_option_t option) {
+ mi_assert_internal(option == mi_option_reserve_os_memory || option == mi_option_arena_reserve);
+ long x = mi_option_get(option);
+ return (x < 0 ? 0 : (size_t)x * MI_KiB);
+}
+
+void mi_option_set(mi_option_t option, long value) {
+ mi_assert(option >= 0 && option < _mi_option_last);
+ if (option < 0 || option >= _mi_option_last) return;
+ mi_option_desc_t* desc = &options[option];
+ mi_assert(desc->option == option); // index should match the option
+ desc->value = value;
+ desc->init = INITIALIZED;
+}
+
+void mi_option_set_default(mi_option_t option, long value) {
+ mi_assert(option >= 0 && option < _mi_option_last);
+ if (option < 0 || option >= _mi_option_last) return;
+ mi_option_desc_t* desc = &options[option];
+ if (desc->init != INITIALIZED) {
+ desc->value = value;
+ }
+}
+
+mi_decl_nodiscard bool mi_option_is_enabled(mi_option_t option) {
+ return (mi_option_get(option) != 0);
+}
+
+void mi_option_set_enabled(mi_option_t option, bool enable) {
+ mi_option_set(option, (enable ? 1 : 0));
+}
+
+void mi_option_set_enabled_default(mi_option_t option, bool enable) {
+ mi_option_set_default(option, (enable ? 1 : 0));
+}
+
+void mi_option_enable(mi_option_t option) {
+ mi_option_set_enabled(option,true);
+}
+
+void mi_option_disable(mi_option_t option) {
+ mi_option_set_enabled(option,false);
+}
+
+static void mi_cdecl mi_out_stderr(const char* msg, void* arg) {
+ MI_UNUSED(arg);
+ if (msg != NULL && msg[0] != 0) {
+ _mi_prim_out_stderr(msg);
+ }
+}
+
+// Since an output function can be registered earliest in the `main`
+// function we also buffer output that happens earlier. When
+// an output function is registered it is called immediately with
+// the output up to that point.
+#ifndef MI_MAX_DELAY_OUTPUT
+#define MI_MAX_DELAY_OUTPUT ((size_t)(32*1024))
+#endif
+static char out_buf[MI_MAX_DELAY_OUTPUT+1];
+static _Atomic(size_t) out_len;
+
+static void mi_cdecl mi_out_buf(const char* msg, void* arg) {
+ MI_UNUSED(arg);
+ if (msg==NULL) return;
+ if (mi_atomic_load_relaxed(&out_len)>=MI_MAX_DELAY_OUTPUT) return;
+ size_t n = _mi_strlen(msg);
+ if (n==0) return;
+ // claim space
+ size_t start = mi_atomic_add_acq_rel(&out_len, n);
+ if (start >= MI_MAX_DELAY_OUTPUT) return;
+ // check bound
+ if (start+n >= MI_MAX_DELAY_OUTPUT) {
+ n = MI_MAX_DELAY_OUTPUT-start-1;
+ }
+ _mi_memcpy(&out_buf[start], msg, n);
+}
+
+static void mi_out_buf_flush(mi_output_fun* out, bool no_more_buf, void* arg) {
+ if (out==NULL) return;
+ // claim (if `no_more_buf == true`, no more output will be added after this point)
+ size_t count = mi_atomic_add_acq_rel(&out_len, (no_more_buf ? MI_MAX_DELAY_OUTPUT : 1));
+ // and output the current contents
+ if (count>MI_MAX_DELAY_OUTPUT) count = MI_MAX_DELAY_OUTPUT;
+ out_buf[count] = 0;
+ out(out_buf,arg);
+ if (!no_more_buf) {
+ out_buf[count] = '\n'; // if continue with the buffer, insert a newline
+ }
+}
+
+
+// Once this module is loaded, switch to this routine
+// which outputs to stderr and the delayed output buffer.
+static void mi_cdecl mi_out_buf_stderr(const char* msg, void* arg) {
+ mi_out_stderr(msg,arg);
+ mi_out_buf(msg,arg);
+}
+
+
+
+// --------------------------------------------------------
+// Default output handler
+// --------------------------------------------------------
+
+// Should be atomic but gives errors on many platforms as generally we cannot cast a function pointer to a uintptr_t.
+// For now, don't register output from multiple threads.
+static mi_output_fun* volatile mi_out_default; // = NULL
+static _Atomic(void*) mi_out_arg; // = NULL
+
+static mi_output_fun* mi_out_get_default(void** parg) {
+ if (parg != NULL) { *parg = mi_atomic_load_ptr_acquire(void,&mi_out_arg); }
+ mi_output_fun* out = mi_out_default;
+ return (out == NULL ? &mi_out_buf : out);
+}
+
+void mi_register_output(mi_output_fun* out, void* arg) mi_attr_noexcept {
+ mi_out_default = (out == NULL ? &mi_out_stderr : out); // stop using the delayed output buffer
+ mi_atomic_store_ptr_release(void,&mi_out_arg, arg);
+ if (out!=NULL) mi_out_buf_flush(out,true,arg); // output all the delayed output now
+}
+
+// add stderr to the delayed output after the module is loaded
+static void mi_add_stderr_output(void) {
+ mi_assert_internal(mi_out_default == NULL);
+ mi_out_buf_flush(&mi_out_stderr, false, NULL); // flush current contents to stderr
+ mi_out_default = &mi_out_buf_stderr; // and add stderr to the delayed output
+}
+
+// --------------------------------------------------------
+// Messages, all end up calling `_mi_fputs`.
+// --------------------------------------------------------
+static _Atomic(size_t) error_count; // = 0; // when >= max_error_count stop emitting errors
+static _Atomic(size_t) warning_count; // = 0; // when >= max_warning_count stop emitting warnings
+
+// When overriding malloc, we may recurse into mi_vfprintf if an allocation
+// inside the C runtime causes another message.
+// In some cases (like on macOS) the loader already allocates which
+// calls into mimalloc; if we then access thread locals (like `recurse`)
+// this may crash as the access may call _tlv_bootstrap that tries to
+// (recursively) invoke malloc again to allocate space for the thread local
+// variables on demand. This is why we use a _mi_preloading test on such
+// platforms. However, C code generator may move the initial thread local address
+// load before the `if` and we therefore split it out in a separate funcion.
+static mi_decl_thread bool recurse = false;
+
+static mi_decl_noinline bool mi_recurse_enter_prim(void) {
+ if (recurse) return false;
+ recurse = true;
+ return true;
+}
+
+static mi_decl_noinline void mi_recurse_exit_prim(void) {
+ recurse = false;
+}
+
+static bool mi_recurse_enter(void) {
+ #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
+ if (_mi_preloading()) return false;
+ #endif
+ return mi_recurse_enter_prim();
+}
+
+static void mi_recurse_exit(void) {
+ #if defined(__APPLE__) || defined(MI_TLS_RECURSE_GUARD)
+ if (_mi_preloading()) return;
+ #endif
+ mi_recurse_exit_prim();
+}
+
+void _mi_fputs(mi_output_fun* out, void* arg, const char* prefix, const char* message) {
+ if (out==NULL || (void*)out==(void*)stdout || (void*)out==(void*)stderr) { // TODO: use mi_out_stderr for stderr?
+ if (!mi_recurse_enter()) return;
+ out = mi_out_get_default(&arg);
+ if (prefix != NULL) out(prefix, arg);
+ out(message, arg);
+ mi_recurse_exit();
+ }
+ else {
+ if (prefix != NULL) out(prefix, arg);
+ out(message, arg);
+ }
+}
+
+// Define our own limited `fprintf` that avoids memory allocation.
+// We do this using `snprintf` with a limited buffer.
+static void mi_vfprintf( mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args ) {
+ char buf[512];
+ if (fmt==NULL) return;
+ if (!mi_recurse_enter()) return;
+ vsnprintf(buf,sizeof(buf)-1,fmt,args);
+ mi_recurse_exit();
+ _mi_fputs(out,arg,prefix,buf);
+}
+
+void _mi_fprintf( mi_output_fun* out, void* arg, const char* fmt, ... ) {
+ va_list args;
+ va_start(args,fmt);
+ mi_vfprintf(out,arg,NULL,fmt,args);
+ va_end(args);
+}
+
+static void mi_vfprintf_thread(mi_output_fun* out, void* arg, const char* prefix, const char* fmt, va_list args) {
+ if (prefix != NULL && _mi_strnlen(prefix,33) <= 32 && !_mi_is_main_thread()) {
+ char tprefix[64];
+ snprintf(tprefix, sizeof(tprefix), "%sthread 0x%llx: ", prefix, (unsigned long long)_mi_thread_id());
+ mi_vfprintf(out, arg, tprefix, fmt, args);
+ }
+ else {
+ mi_vfprintf(out, arg, prefix, fmt, args);
+ }
+}
+
+void _mi_trace_message(const char* fmt, ...) {
+ if (mi_option_get(mi_option_verbose) <= 1) return; // only with verbose level 2 or higher
+ va_list args;
+ va_start(args, fmt);
+ mi_vfprintf_thread(NULL, NULL, "mimalloc: ", fmt, args);
+ va_end(args);
+}
+
+void _mi_verbose_message(const char* fmt, ...) {
+ if (!mi_option_is_enabled(mi_option_verbose)) return;
+ va_list args;
+ va_start(args,fmt);
+ mi_vfprintf(NULL, NULL, "mimalloc: ", fmt, args);
+ va_end(args);
+}
+
+static void mi_show_error_message(const char* fmt, va_list args) {
+ if (!mi_option_is_enabled(mi_option_verbose)) {
+ if (!mi_option_is_enabled(mi_option_show_errors)) return;
+ if (mi_max_error_count >= 0 && (long)mi_atomic_increment_acq_rel(&error_count) > mi_max_error_count) return;
+ }
+ mi_vfprintf_thread(NULL, NULL, "mimalloc: error: ", fmt, args);
+}
+
+void _mi_warning_message(const char* fmt, ...) {
+ if (!mi_option_is_enabled(mi_option_verbose)) {
+ if (!mi_option_is_enabled(mi_option_show_errors)) return;
+ if (mi_max_warning_count >= 0 && (long)mi_atomic_increment_acq_rel(&warning_count) > mi_max_warning_count) return;
+ }
+ va_list args;
+ va_start(args,fmt);
+ mi_vfprintf_thread(NULL, NULL, "mimalloc: warning: ", fmt, args);
+ va_end(args);
+}
+
+
+#if MI_DEBUG
+void _mi_assert_fail(const char* assertion, const char* fname, unsigned line, const char* func ) {
+ _mi_fprintf(NULL, NULL, "mimalloc: assertion failed: at \"%s\":%u, %s\n assertion: \"%s\"\n", fname, line, (func==NULL?"":func), assertion);
+ abort();
+}
+#endif
+
+// --------------------------------------------------------
+// Errors
+// --------------------------------------------------------
+
+static mi_error_fun* volatile mi_error_handler; // = NULL
+static _Atomic(void*) mi_error_arg; // = NULL
+
+static void mi_error_default(int err) {
+ MI_UNUSED(err);
+#if (MI_DEBUG>0)
+ if (err==EFAULT) {
+ #ifdef _MSC_VER
+ __debugbreak();
+ #endif
+ abort();
+ }
+#endif
+#if (MI_SECURE>0)
+ if (err==EFAULT) { // abort on serious errors in secure mode (corrupted meta-data)
+ abort();
+ }
+#endif
+#if defined(MI_XMALLOC)
+ if (err==ENOMEM || err==EOVERFLOW) { // abort on memory allocation fails in xmalloc mode
+ abort();
+ }
+#endif
+}
+
+void mi_register_error(mi_error_fun* fun, void* arg) {
+ mi_error_handler = fun; // can be NULL
+ mi_atomic_store_ptr_release(void,&mi_error_arg, arg);
+}
+
+void _mi_error_message(int err, const char* fmt, ...) {
+ // show detailed error message
+ va_list args;
+ va_start(args, fmt);
+ mi_show_error_message(fmt, args);
+ va_end(args);
+ // and call the error handler which may abort (or return normally)
+ if (mi_error_handler != NULL) {
+ mi_error_handler(err, mi_atomic_load_ptr_acquire(void,&mi_error_arg));
+ }
+ else {
+ mi_error_default(err);
+ }
+}
+
+// --------------------------------------------------------
+// Initialize options by checking the environment
+// --------------------------------------------------------
+char _mi_toupper(char c) {
+ if (c >= 'a' && c <= 'z') return (c - 'a' + 'A');
+ else return c;
+}
+
+int _mi_strnicmp(const char* s, const char* t, size_t n) {
+ if (n == 0) return 0;
+ for (; *s != 0 && *t != 0 && n > 0; s++, t++, n--) {
+ if (_mi_toupper(*s) != _mi_toupper(*t)) break;
+ }
+ return (n == 0 ? 0 : *s - *t);
+}
+
+void _mi_strlcpy(char* dest, const char* src, size_t dest_size) {
+ if (dest==NULL || src==NULL || dest_size == 0) return;
+ // copy until end of src, or when dest is (almost) full
+ while (*src != 0 && dest_size > 1) {
+ *dest++ = *src++;
+ dest_size--;
+ }
+ // always zero terminate
+ *dest = 0;
+}
+
+void _mi_strlcat(char* dest, const char* src, size_t dest_size) {
+ if (dest==NULL || src==NULL || dest_size == 0) return;
+ // find end of string in the dest buffer
+ while (*dest != 0 && dest_size > 1) {
+ dest++;
+ dest_size--;
+ }
+ // and catenate
+ _mi_strlcpy(dest, src, dest_size);
+}
+
+size_t _mi_strlen(const char* s) {
+ if (s==NULL) return 0;
+ size_t len = 0;
+ while(s[len] != 0) { len++; }
+ return len;
+}
+
+size_t _mi_strnlen(const char* s, size_t max_len) {
+ if (s==NULL) return 0;
+ size_t len = 0;
+ while(s[len] != 0 && len < max_len) { len++; }
+ return len;
+}
+
+#ifdef MI_NO_GETENV
+static bool mi_getenv(const char* name, char* result, size_t result_size) {
+ MI_UNUSED(name);
+ MI_UNUSED(result);
+ MI_UNUSED(result_size);
+ return false;
+}
+#else
+static bool mi_getenv(const char* name, char* result, size_t result_size) {
+ if (name==NULL || result == NULL || result_size < 64) return false;
+ return _mi_prim_getenv(name,result,result_size);
+}
+#endif
+
+// TODO: implement ourselves to reduce dependencies on the C runtime
+#include <stdlib.h> // strtol
+#include <string.h> // strstr
+
+
+static void mi_option_init(mi_option_desc_t* desc) {
+ // Read option value from the environment
+ char s[64 + 1];
+ char buf[64+1];
+ _mi_strlcpy(buf, "mimalloc_", sizeof(buf));
+ _mi_strlcat(buf, desc->name, sizeof(buf));
+ bool found = mi_getenv(buf, s, sizeof(s));
+ if (!found && desc->legacy_name != NULL) {
+ _mi_strlcpy(buf, "mimalloc_", sizeof(buf));
+ _mi_strlcat(buf, desc->legacy_name, sizeof(buf));
+ found = mi_getenv(buf, s, sizeof(s));
+ if (found) {
+ _mi_warning_message("environment option \"mimalloc_%s\" is deprecated -- use \"mimalloc_%s\" instead.\n", desc->legacy_name, desc->name);
+ }
+ }
+
+ if (found) {
+ size_t len = _mi_strnlen(s, sizeof(buf) - 1);
+ for (size_t i = 0; i < len; i++) {
+ buf[i] = _mi_toupper(s[i]);
+ }
+ buf[len] = 0;
+ if (buf[0] == 0 || strstr("1;TRUE;YES;ON", buf) != NULL) {
+ desc->value = 1;
+ desc->init = INITIALIZED;
+ }
+ else if (strstr("0;FALSE;NO;OFF", buf) != NULL) {
+ desc->value = 0;
+ desc->init = INITIALIZED;
+ }
+ else {
+ char* end = buf;
+ long value = strtol(buf, &end, 10);
+ if (desc->option == mi_option_reserve_os_memory || desc->option == mi_option_arena_reserve) {
+ // this option is interpreted in KiB to prevent overflow of `long`
+ if (*end == 'K') { end++; }
+ else if (*end == 'M') { value *= MI_KiB; end++; }
+ else if (*end == 'G') { value *= MI_MiB; end++; }
+ else { value = (value + MI_KiB - 1) / MI_KiB; }
+ if (end[0] == 'I' && end[1] == 'B') { end += 2; }
+ else if (*end == 'B') { end++; }
+ }
+ if (*end == 0) {
+ desc->value = value;
+ desc->init = INITIALIZED;
+ }
+ else {
+ // set `init` first to avoid recursion through _mi_warning_message on mimalloc_verbose.
+ desc->init = DEFAULTED;
+ if (desc->option == mi_option_verbose && desc->value == 0) {
+ // if the 'mimalloc_verbose' env var has a bogus value we'd never know
+ // (since the value defaults to 'off') so in that case briefly enable verbose
+ desc->value = 1;
+ _mi_warning_message("environment option mimalloc_%s has an invalid value.\n", desc->name);
+ desc->value = 0;
+ }
+ else {
+ _mi_warning_message("environment option mimalloc_%s has an invalid value.\n", desc->name);
+ }
+ }
+ }
+ mi_assert_internal(desc->init != UNINIT);
+ }
+ else if (!_mi_preloading()) {
+ desc->init = DEFAULTED;
+ }
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/os.c b/contrib/tools/python3/Objects/mimalloc/os.c
new file mode 100644
index 00000000000..c9103168c12
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/os.c
@@ -0,0 +1,698 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2023, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+#include "mimalloc/prim.h"
+
+
+/* -----------------------------------------------------------
+ Initialization.
+ On windows initializes support for aligned allocation and
+ large OS pages (if MIMALLOC_LARGE_OS_PAGES is true).
+----------------------------------------------------------- */
+
+static mi_os_mem_config_t mi_os_mem_config = {
+ 4096, // page size
+ 0, // large page size (usually 2MiB)
+ 4096, // allocation granularity
+ true, // has overcommit? (if true we use MAP_NORESERVE on mmap systems)
+ false, // must free whole? (on mmap systems we can free anywhere in a mapped range, but on Windows we must free the entire span)
+ true // has virtual reserve? (if true we can reserve virtual address space without using commit or physical memory)
+};
+
+bool _mi_os_has_overcommit(void) {
+ return mi_os_mem_config.has_overcommit;
+}
+
+bool _mi_os_has_virtual_reserve(void) {
+ return mi_os_mem_config.has_virtual_reserve;
+}
+
+
+// OS (small) page size
+size_t _mi_os_page_size(void) {
+ return mi_os_mem_config.page_size;
+}
+
+// if large OS pages are supported (2 or 4MiB), then return the size, otherwise return the small page size (4KiB)
+size_t _mi_os_large_page_size(void) {
+ return (mi_os_mem_config.large_page_size != 0 ? mi_os_mem_config.large_page_size : _mi_os_page_size());
+}
+
+bool _mi_os_use_large_page(size_t size, size_t alignment) {
+ // if we have access, check the size and alignment requirements
+ if (mi_os_mem_config.large_page_size == 0 || !mi_option_is_enabled(mi_option_allow_large_os_pages)) return false;
+ return ((size % mi_os_mem_config.large_page_size) == 0 && (alignment % mi_os_mem_config.large_page_size) == 0);
+}
+
+// round to a good OS allocation size (bounded by max 12.5% waste)
+size_t _mi_os_good_alloc_size(size_t size) {
+ size_t align_size;
+ if (size < 512*MI_KiB) align_size = _mi_os_page_size();
+ else if (size < 2*MI_MiB) align_size = 64*MI_KiB;
+ else if (size < 8*MI_MiB) align_size = 256*MI_KiB;
+ else if (size < 32*MI_MiB) align_size = 1*MI_MiB;
+ else align_size = 4*MI_MiB;
+ if mi_unlikely(size >= (SIZE_MAX - align_size)) return size; // possible overflow?
+ return _mi_align_up(size, align_size);
+}
+
+void _mi_os_init(void) {
+ _mi_prim_mem_init(&mi_os_mem_config);
+}
+
+
+/* -----------------------------------------------------------
+ Util
+-------------------------------------------------------------- */
+bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* stats);
+bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats);
+
+static void* mi_align_up_ptr(void* p, size_t alignment) {
+ return (void*)_mi_align_up((uintptr_t)p, alignment);
+}
+
+static void* mi_align_down_ptr(void* p, size_t alignment) {
+ return (void*)_mi_align_down((uintptr_t)p, alignment);
+}
+
+
+/* -----------------------------------------------------------
+ aligned hinting
+-------------------------------------------------------------- */
+
+// On 64-bit systems, we can do efficient aligned allocation by using
+// the 2TiB to 30TiB area to allocate those.
+#if (MI_INTPTR_SIZE >= 8)
+static mi_decl_cache_align _Atomic(uintptr_t)aligned_base;
+
+// Return a MI_SEGMENT_SIZE aligned address that is probably available.
+// If this returns NULL, the OS will determine the address but on some OS's that may not be
+// properly aligned which can be more costly as it needs to be adjusted afterwards.
+// For a size > 1GiB this always returns NULL in order to guarantee good ASLR randomization;
+// (otherwise an initial large allocation of say 2TiB has a 50% chance to include (known) addresses
+// in the middle of the 2TiB - 6TiB address range (see issue #372))
+
+#define MI_HINT_BASE ((uintptr_t)2 << 40) // 2TiB start
+#define MI_HINT_AREA ((uintptr_t)4 << 40) // upto 6TiB (since before win8 there is "only" 8TiB available to processes)
+#define MI_HINT_MAX ((uintptr_t)30 << 40) // wrap after 30TiB (area after 32TiB is used for huge OS pages)
+
+void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size)
+{
+ if (try_alignment <= 1 || try_alignment > MI_SEGMENT_SIZE) return NULL;
+ size = _mi_align_up(size, MI_SEGMENT_SIZE);
+ if (size > 1*MI_GiB) return NULL; // guarantee the chance of fixed valid address is at most 1/(MI_HINT_AREA / 1<<30) = 1/4096.
+ #if (MI_SECURE>0)
+ size += MI_SEGMENT_SIZE; // put in `MI_SEGMENT_SIZE` virtual gaps between hinted blocks; this splits VLA's but increases guarded areas.
+ #endif
+
+ uintptr_t hint = mi_atomic_add_acq_rel(&aligned_base, size);
+ if (hint == 0 || hint > MI_HINT_MAX) { // wrap or initialize
+ uintptr_t init = MI_HINT_BASE;
+ #if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of aligned allocations unless in debug mode
+ mi_heap_t* heap = mi_prim_get_default_heap();
+ // gh-123022: default heap may not be initialized in CPython in background threads
+ if (mi_heap_is_initialized(heap)) {
+ uintptr_t r = _mi_heap_random_next(heap);
+ init = init + ((MI_SEGMENT_SIZE * ((r>>17) & 0xFFFFF)) % MI_HINT_AREA); // (randomly 20 bits)*4MiB == 0 to 4TiB
+ }
+ #endif
+ uintptr_t expected = hint + size;
+ mi_atomic_cas_strong_acq_rel(&aligned_base, &expected, init);
+ hint = mi_atomic_add_acq_rel(&aligned_base, size); // this may still give 0 or > MI_HINT_MAX but that is ok, it is a hint after all
+ }
+ if (hint%try_alignment != 0) return NULL;
+ return (void*)hint;
+}
+#else
+void* _mi_os_get_aligned_hint(size_t try_alignment, size_t size) {
+ MI_UNUSED(try_alignment); MI_UNUSED(size);
+ return NULL;
+}
+#endif
+
+
+/* -----------------------------------------------------------
+ Free memory
+-------------------------------------------------------------- */
+
+static void mi_os_free_huge_os_pages(void* p, size_t size, mi_stats_t* stats);
+
+static void mi_os_prim_free(void* addr, size_t size, bool still_committed, mi_stats_t* tld_stats) {
+ MI_UNUSED(tld_stats);
+ mi_assert_internal((size % _mi_os_page_size()) == 0);
+ if (addr == NULL || size == 0) return; // || _mi_os_is_huge_reserved(addr)
+ int err = _mi_prim_free(addr, size);
+ if (err != 0) {
+ _mi_warning_message("unable to free OS memory (error: %d (0x%x), size: 0x%zx bytes, address: %p)\n", err, err, size, addr);
+ }
+ mi_stats_t* stats = &_mi_stats_main;
+ if (still_committed) { _mi_stat_decrease(&stats->committed, size); }
+ _mi_stat_decrease(&stats->reserved, size);
+}
+
+void _mi_os_free_ex(void* addr, size_t size, bool still_committed, mi_memid_t memid, mi_stats_t* tld_stats) {
+ if (mi_memkind_is_os(memid.memkind)) {
+ size_t csize = _mi_os_good_alloc_size(size);
+ void* base = addr;
+ // different base? (due to alignment)
+ if (memid.mem.os.base != NULL) {
+ mi_assert(memid.mem.os.base <= addr);
+ mi_assert((uint8_t*)memid.mem.os.base + memid.mem.os.alignment >= (uint8_t*)addr);
+ base = memid.mem.os.base;
+ csize += ((uint8_t*)addr - (uint8_t*)memid.mem.os.base);
+ }
+ // free it
+ if (memid.memkind == MI_MEM_OS_HUGE) {
+ mi_assert(memid.is_pinned);
+ mi_os_free_huge_os_pages(base, csize, tld_stats);
+ }
+ else {
+ mi_os_prim_free(base, csize, still_committed, tld_stats);
+ }
+ }
+ else {
+ // nothing to do
+ mi_assert(memid.memkind < MI_MEM_OS);
+ }
+}
+
+void _mi_os_free(void* p, size_t size, mi_memid_t memid, mi_stats_t* tld_stats) {
+ _mi_os_free_ex(p, size, true, memid, tld_stats);
+}
+
+
+/* -----------------------------------------------------------
+ Primitive allocation from the OS.
+-------------------------------------------------------------- */
+
+// Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned.
+static void* mi_os_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, mi_stats_t* stats) {
+ mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
+ mi_assert_internal(is_zero != NULL);
+ mi_assert_internal(is_large != NULL);
+ if (size == 0) return NULL;
+ if (!commit) { allow_large = false; }
+ if (try_alignment == 0) { try_alignment = 1; } // avoid 0 to ensure there will be no divide by zero when aligning
+
+ *is_zero = false;
+ void* p = NULL;
+ int err = _mi_prim_alloc(size, try_alignment, commit, allow_large, is_large, is_zero, &p);
+ if (err != 0) {
+ _mi_warning_message("unable to allocate OS memory (error: %d (0x%x), size: 0x%zx bytes, align: 0x%zx, commit: %d, allow large: %d)\n", err, err, size, try_alignment, commit, allow_large);
+ }
+ mi_stat_counter_increase(stats->mmap_calls, 1);
+ if (p != NULL) {
+ _mi_stat_increase(&stats->reserved, size);
+ if (commit) {
+ _mi_stat_increase(&stats->committed, size);
+ // seems needed for asan (or `mimalloc-test-api` fails)
+ #ifdef MI_TRACK_ASAN
+ if (*is_zero) { mi_track_mem_defined(p,size); }
+ else { mi_track_mem_undefined(p,size); }
+ #endif
+ }
+ }
+ return p;
+}
+
+
+// Primitive aligned allocation from the OS.
+// This function guarantees the allocated memory is aligned.
+static void* mi_os_prim_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** base, mi_stats_t* stats) {
+ mi_assert_internal(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0));
+ mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
+ mi_assert_internal(is_large != NULL);
+ mi_assert_internal(is_zero != NULL);
+ mi_assert_internal(base != NULL);
+ if (!commit) allow_large = false;
+ if (!(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0))) return NULL;
+ size = _mi_align_up(size, _mi_os_page_size());
+
+ // try first with a hint (this will be aligned directly on Win 10+ or BSD)
+ void* p = mi_os_prim_alloc(size, alignment, commit, allow_large, is_large, is_zero, stats);
+ if (p == NULL) return NULL;
+
+ // aligned already?
+ if (((uintptr_t)p % alignment) == 0) {
+ *base = p;
+ }
+ else {
+ // if not aligned, free it, overallocate, and unmap around it
+ // NOTE(sgross): this warning causes issues in Python tests
+ // _mi_warning_message("unable to allocate aligned OS memory directly, fall back to over-allocation (size: 0x%zx bytes, address: %p, alignment: 0x%zx, commit: %d)\n", size, p, alignment, commit);
+ mi_os_prim_free(p, size, commit, stats);
+ if (size >= (SIZE_MAX - alignment)) return NULL; // overflow
+ const size_t over_size = size + alignment;
+
+ if (mi_os_mem_config.must_free_whole) { // win32 virtualAlloc cannot free parts of an allocate block
+ // over-allocate uncommitted (virtual) memory
+ p = mi_os_prim_alloc(over_size, 1 /*alignment*/, false /* commit? */, false /* allow_large */, is_large, is_zero, stats);
+ if (p == NULL) return NULL;
+
+ // set p to the aligned part in the full region
+ // note: this is dangerous on Windows as VirtualFree needs the actual base pointer
+ // this is handled though by having the `base` field in the memid's
+ *base = p; // remember the base
+ p = mi_align_up_ptr(p, alignment);
+
+ // explicitly commit only the aligned part
+ if (commit) {
+ _mi_os_commit(p, size, NULL, stats);
+ }
+ }
+ else { // mmap can free inside an allocation
+ // overallocate...
+ p = mi_os_prim_alloc(over_size, 1, commit, false, is_large, is_zero, stats);
+ if (p == NULL) return NULL;
+
+ // and selectively unmap parts around the over-allocated area. (noop on sbrk)
+ void* aligned_p = mi_align_up_ptr(p, alignment);
+ size_t pre_size = (uint8_t*)aligned_p - (uint8_t*)p;
+ size_t mid_size = _mi_align_up(size, _mi_os_page_size());
+ size_t post_size = over_size - pre_size - mid_size;
+ mi_assert_internal(pre_size < over_size&& post_size < over_size&& mid_size >= size);
+ if (pre_size > 0) { mi_os_prim_free(p, pre_size, commit, stats); }
+ if (post_size > 0) { mi_os_prim_free((uint8_t*)aligned_p + mid_size, post_size, commit, stats); }
+ // we can return the aligned pointer on `mmap` (and sbrk) systems
+ p = aligned_p;
+ *base = aligned_p; // since we freed the pre part, `*base == p`.
+ }
+ }
+
+ mi_assert_internal(p == NULL || (p != NULL && *base != NULL && ((uintptr_t)p % alignment) == 0));
+ return p;
+}
+
+
+/* -----------------------------------------------------------
+ OS API: alloc and alloc_aligned
+----------------------------------------------------------- */
+
+void* _mi_os_alloc(size_t size, mi_memid_t* memid, mi_stats_t* tld_stats) {
+ MI_UNUSED(tld_stats);
+ *memid = _mi_memid_none();
+ mi_stats_t* stats = &_mi_stats_main;
+ if (size == 0) return NULL;
+ size = _mi_os_good_alloc_size(size);
+ bool os_is_large = false;
+ bool os_is_zero = false;
+ void* p = mi_os_prim_alloc(size, 0, true, false, &os_is_large, &os_is_zero, stats);
+ if (p != NULL) {
+ *memid = _mi_memid_create_os(true, os_is_zero, os_is_large);
+ }
+ return p;
+}
+
+void* _mi_os_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, mi_memid_t* memid, mi_stats_t* tld_stats)
+{
+ MI_UNUSED(&_mi_os_get_aligned_hint); // suppress unused warnings
+ MI_UNUSED(tld_stats);
+ *memid = _mi_memid_none();
+ if (size == 0) return NULL;
+ size = _mi_os_good_alloc_size(size);
+ alignment = _mi_align_up(alignment, _mi_os_page_size());
+
+ bool os_is_large = false;
+ bool os_is_zero = false;
+ void* os_base = NULL;
+ void* p = mi_os_prim_alloc_aligned(size, alignment, commit, allow_large, &os_is_large, &os_is_zero, &os_base, &_mi_stats_main /*tld->stats*/ );
+ if (p != NULL) {
+ *memid = _mi_memid_create_os(commit, os_is_zero, os_is_large);
+ memid->mem.os.base = os_base;
+ memid->mem.os.alignment = alignment;
+ }
+ return p;
+}
+
+/* -----------------------------------------------------------
+ OS aligned allocation with an offset. This is used
+ for large alignments > MI_ALIGNMENT_MAX. We use a large mimalloc
+ page where the object can be aligned at an offset from the start of the segment.
+ As we may need to overallocate, we need to free such pointers using `mi_free_aligned`
+ to use the actual start of the memory region.
+----------------------------------------------------------- */
+
+void* _mi_os_alloc_aligned_at_offset(size_t size, size_t alignment, size_t offset, bool commit, bool allow_large, mi_memid_t* memid, mi_stats_t* tld_stats) {
+ mi_assert(offset <= MI_SEGMENT_SIZE);
+ mi_assert(offset <= size);
+ mi_assert((alignment % _mi_os_page_size()) == 0);
+ *memid = _mi_memid_none();
+ if (offset > MI_SEGMENT_SIZE) return NULL;
+ if (offset == 0) {
+ // regular aligned allocation
+ return _mi_os_alloc_aligned(size, alignment, commit, allow_large, memid, tld_stats);
+ }
+ else {
+ // overallocate to align at an offset
+ const size_t extra = _mi_align_up(offset, alignment) - offset;
+ const size_t oversize = size + extra;
+ void* const start = _mi_os_alloc_aligned(oversize, alignment, commit, allow_large, memid, tld_stats);
+ if (start == NULL) return NULL;
+
+ void* const p = (uint8_t*)start + extra;
+ mi_assert(_mi_is_aligned((uint8_t*)p + offset, alignment));
+ // decommit the overallocation at the start
+ if (commit && extra > _mi_os_page_size()) {
+ _mi_os_decommit(start, extra, tld_stats);
+ }
+ return p;
+ }
+}
+
+/* -----------------------------------------------------------
+ OS memory API: reset, commit, decommit, protect, unprotect.
+----------------------------------------------------------- */
+
+// OS page align within a given area, either conservative (pages inside the area only),
+// or not (straddling pages outside the area is possible)
+static void* mi_os_page_align_areax(bool conservative, void* addr, size_t size, size_t* newsize) {
+ mi_assert(addr != NULL && size > 0);
+ if (newsize != NULL) *newsize = 0;
+ if (size == 0 || addr == NULL) return NULL;
+
+ // page align conservatively within the range
+ void* start = (conservative ? mi_align_up_ptr(addr, _mi_os_page_size())
+ : mi_align_down_ptr(addr, _mi_os_page_size()));
+ void* end = (conservative ? mi_align_down_ptr((uint8_t*)addr + size, _mi_os_page_size())
+ : mi_align_up_ptr((uint8_t*)addr + size, _mi_os_page_size()));
+ ptrdiff_t diff = (uint8_t*)end - (uint8_t*)start;
+ if (diff <= 0) return NULL;
+
+ mi_assert_internal((conservative && (size_t)diff <= size) || (!conservative && (size_t)diff >= size));
+ if (newsize != NULL) *newsize = (size_t)diff;
+ return start;
+}
+
+static void* mi_os_page_align_area_conservative(void* addr, size_t size, size_t* newsize) {
+ return mi_os_page_align_areax(true, addr, size, newsize);
+}
+
+bool _mi_os_commit(void* addr, size_t size, bool* is_zero, mi_stats_t* tld_stats) {
+ MI_UNUSED(tld_stats);
+ mi_stats_t* stats = &_mi_stats_main;
+ if (is_zero != NULL) { *is_zero = false; }
+ _mi_stat_increase(&stats->committed, size); // use size for precise commit vs. decommit
+ _mi_stat_counter_increase(&stats->commit_calls, 1);
+
+ // page align range
+ size_t csize;
+ void* start = mi_os_page_align_areax(false /* conservative? */, addr, size, &csize);
+ if (csize == 0) return true;
+
+ // commit
+ bool os_is_zero = false;
+ int err = _mi_prim_commit(start, csize, &os_is_zero);
+ if (err != 0) {
+ _mi_warning_message("cannot commit OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", err, err, start, csize);
+ return false;
+ }
+ if (os_is_zero && is_zero != NULL) {
+ *is_zero = true;
+ mi_assert_expensive(mi_mem_is_zero(start, csize));
+ }
+ // note: the following seems required for asan (otherwise `mimalloc-test-stress` fails)
+ #ifdef MI_TRACK_ASAN
+ if (os_is_zero) { mi_track_mem_defined(start,csize); }
+ else { mi_track_mem_undefined(start,csize); }
+ #endif
+ return true;
+}
+
+static bool mi_os_decommit_ex(void* addr, size_t size, bool* needs_recommit, mi_stats_t* tld_stats) {
+ MI_UNUSED(tld_stats);
+ mi_stats_t* stats = &_mi_stats_main;
+ mi_assert_internal(needs_recommit!=NULL);
+ _mi_stat_decrease(&stats->committed, size);
+
+ // page align
+ size_t csize;
+ void* start = mi_os_page_align_area_conservative(addr, size, &csize);
+ if (csize == 0) return true;
+
+ // decommit
+ *needs_recommit = true;
+ int err = _mi_prim_decommit(start,csize,needs_recommit);
+ if (err != 0) {
+ _mi_warning_message("cannot decommit OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", err, err, start, csize);
+ }
+ mi_assert_internal(err == 0);
+ return (err == 0);
+}
+
+bool _mi_os_decommit(void* addr, size_t size, mi_stats_t* tld_stats) {
+ bool needs_recommit;
+ return mi_os_decommit_ex(addr, size, &needs_recommit, tld_stats);
+}
+
+
+// Signal to the OS that the address range is no longer in use
+// but may be used later again. This will release physical memory
+// pages and reduce swapping while keeping the memory committed.
+// We page align to a conservative area inside the range to reset.
+bool _mi_os_reset(void* addr, size_t size, mi_stats_t* stats) {
+ // page align conservatively within the range
+ size_t csize;
+ void* start = mi_os_page_align_area_conservative(addr, size, &csize);
+ if (csize == 0) return true; // || _mi_os_is_huge_reserved(addr)
+ _mi_stat_increase(&stats->reset, csize);
+ _mi_stat_counter_increase(&stats->reset_calls, 1);
+
+ #if (MI_DEBUG>1) && !MI_SECURE && !MI_TRACK_ENABLED // && !MI_TSAN
+ memset(start, 0, csize); // pretend it is eagerly reset
+ #endif
+
+ int err = _mi_prim_reset(start, csize);
+ if (err != 0) {
+ _mi_warning_message("cannot reset OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", err, err, start, csize);
+ }
+ return (err == 0);
+}
+
+
+// either resets or decommits memory, returns true if the memory needs
+// to be recommitted if it is to be re-used later on.
+bool _mi_os_purge_ex(void* p, size_t size, bool allow_reset, mi_stats_t* stats)
+{
+ if (mi_option_get(mi_option_purge_delay) < 0) return false; // is purging allowed?
+ _mi_stat_counter_increase(&stats->purge_calls, 1);
+ _mi_stat_increase(&stats->purged, size);
+
+ if (mi_option_is_enabled(mi_option_purge_decommits) && // should decommit?
+ !_mi_preloading()) // don't decommit during preloading (unsafe)
+ {
+ bool needs_recommit = true;
+ mi_os_decommit_ex(p, size, &needs_recommit, stats);
+ return needs_recommit;
+ }
+ else {
+ if (allow_reset) { // this can sometimes be not allowed if the range is not fully committed
+ _mi_os_reset(p, size, stats);
+ }
+ return false; // needs no recommit
+ }
+}
+
+// either resets or decommits memory, returns true if the memory needs
+// to be recommitted if it is to be re-used later on.
+bool _mi_os_purge(void* p, size_t size, mi_stats_t * stats) {
+ return _mi_os_purge_ex(p, size, true, stats);
+}
+
+// Protect a region in memory to be not accessible.
+static bool mi_os_protectx(void* addr, size_t size, bool protect) {
+ // page align conservatively within the range
+ size_t csize = 0;
+ void* start = mi_os_page_align_area_conservative(addr, size, &csize);
+ if (csize == 0) return false;
+ /*
+ if (_mi_os_is_huge_reserved(addr)) {
+ _mi_warning_message("cannot mprotect memory allocated in huge OS pages\n");
+ }
+ */
+ int err = _mi_prim_protect(start,csize,protect);
+ if (err != 0) {
+ _mi_warning_message("cannot %s OS memory (error: %d (0x%x), address: %p, size: 0x%zx bytes)\n", (protect ? "protect" : "unprotect"), err, err, start, csize);
+ }
+ return (err == 0);
+}
+
+bool _mi_os_protect(void* addr, size_t size) {
+ return mi_os_protectx(addr, size, true);
+}
+
+bool _mi_os_unprotect(void* addr, size_t size) {
+ return mi_os_protectx(addr, size, false);
+}
+
+
+
+/* ----------------------------------------------------------------------------
+Support for allocating huge OS pages (1Gib) that are reserved up-front
+and possibly associated with a specific NUMA node. (use `numa_node>=0`)
+-----------------------------------------------------------------------------*/
+#define MI_HUGE_OS_PAGE_SIZE (MI_GiB)
+
+
+#if (MI_INTPTR_SIZE >= 8)
+// To ensure proper alignment, use our own area for huge OS pages
+static mi_decl_cache_align _Atomic(uintptr_t) mi_huge_start; // = 0
+
+// Claim an aligned address range for huge pages
+static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) {
+ if (total_size != NULL) *total_size = 0;
+ const size_t size = pages * MI_HUGE_OS_PAGE_SIZE;
+
+ uintptr_t start = 0;
+ uintptr_t end = 0;
+ uintptr_t huge_start = mi_atomic_load_relaxed(&mi_huge_start);
+ do {
+ start = huge_start;
+ if (start == 0) {
+ // Initialize the start address after the 32TiB area
+ start = ((uintptr_t)32 << 40); // 32TiB virtual start address
+ #if (MI_SECURE>0 || MI_DEBUG==0) // security: randomize start of huge pages unless in debug mode
+ mi_heap_t* heap = mi_prim_get_default_heap();
+ // gh-123022: default heap may not be initialized in CPython in background threads
+ if (mi_heap_is_initialized(heap)) {
+ uintptr_t r = _mi_heap_random_next(heap);
+ start = start + ((uintptr_t)MI_HUGE_OS_PAGE_SIZE * ((r>>17) & 0x0FFF)); // (randomly 12bits)*1GiB == between 0 to 4TiB
+ }
+ #endif
+ }
+ end = start + size;
+ mi_assert_internal(end % MI_SEGMENT_SIZE == 0);
+ } while (!mi_atomic_cas_strong_acq_rel(&mi_huge_start, &huge_start, end));
+
+ if (total_size != NULL) *total_size = size;
+ return (uint8_t*)start;
+}
+#else
+static uint8_t* mi_os_claim_huge_pages(size_t pages, size_t* total_size) {
+ MI_UNUSED(pages);
+ if (total_size != NULL) *total_size = 0;
+ return NULL;
+}
+#endif
+
+// Allocate MI_SEGMENT_SIZE aligned huge pages
+void* _mi_os_alloc_huge_os_pages(size_t pages, int numa_node, mi_msecs_t max_msecs, size_t* pages_reserved, size_t* psize, mi_memid_t* memid) {
+ *memid = _mi_memid_none();
+ if (psize != NULL) *psize = 0;
+ if (pages_reserved != NULL) *pages_reserved = 0;
+ size_t size = 0;
+ uint8_t* start = mi_os_claim_huge_pages(pages, &size);
+ if (start == NULL) return NULL; // or 32-bit systems
+
+ // Allocate one page at the time but try to place them contiguously
+ // We allocate one page at the time to be able to abort if it takes too long
+ // or to at least allocate as many as available on the system.
+ mi_msecs_t start_t = _mi_clock_start();
+ size_t page = 0;
+ bool all_zero = true;
+ while (page < pages) {
+ // allocate a page
+ bool is_zero = false;
+ void* addr = start + (page * MI_HUGE_OS_PAGE_SIZE);
+ void* p = NULL;
+ int err = _mi_prim_alloc_huge_os_pages(addr, MI_HUGE_OS_PAGE_SIZE, numa_node, &is_zero, &p);
+ if (!is_zero) { all_zero = false; }
+ if (err != 0) {
+ _mi_warning_message("unable to allocate huge OS page (error: %d (0x%x), address: %p, size: %zx bytes)\n", err, err, addr, MI_HUGE_OS_PAGE_SIZE);
+ break;
+ }
+
+ // Did we succeed at a contiguous address?
+ if (p != addr) {
+ // no success, issue a warning and break
+ if (p != NULL) {
+ _mi_warning_message("could not allocate contiguous huge OS page %zu at %p\n", page, addr);
+ mi_os_prim_free(p, MI_HUGE_OS_PAGE_SIZE, true, &_mi_stats_main);
+ }
+ break;
+ }
+
+ // success, record it
+ page++; // increase before timeout check (see issue #711)
+ _mi_stat_increase(&_mi_stats_main.committed, MI_HUGE_OS_PAGE_SIZE);
+ _mi_stat_increase(&_mi_stats_main.reserved, MI_HUGE_OS_PAGE_SIZE);
+
+ // check for timeout
+ if (max_msecs > 0) {
+ mi_msecs_t elapsed = _mi_clock_end(start_t);
+ if (page >= 1) {
+ mi_msecs_t estimate = ((elapsed / (page+1)) * pages);
+ if (estimate > 2*max_msecs) { // seems like we are going to timeout, break
+ elapsed = max_msecs + 1;
+ }
+ }
+ if (elapsed > max_msecs) {
+ _mi_warning_message("huge OS page allocation timed out (after allocating %zu page(s))\n", page);
+ break;
+ }
+ }
+ }
+ mi_assert_internal(page*MI_HUGE_OS_PAGE_SIZE <= size);
+ if (pages_reserved != NULL) { *pages_reserved = page; }
+ if (psize != NULL) { *psize = page * MI_HUGE_OS_PAGE_SIZE; }
+ if (page != 0) {
+ mi_assert(start != NULL);
+ *memid = _mi_memid_create_os(true /* is committed */, all_zero, true /* is_large */);
+ memid->memkind = MI_MEM_OS_HUGE;
+ mi_assert(memid->is_pinned);
+ #ifdef MI_TRACK_ASAN
+ if (all_zero) { mi_track_mem_defined(start,size); }
+ #endif
+ }
+ return (page == 0 ? NULL : start);
+}
+
+// free every huge page in a range individually (as we allocated per page)
+// note: needed with VirtualAlloc but could potentially be done in one go on mmap'd systems.
+static void mi_os_free_huge_os_pages(void* p, size_t size, mi_stats_t* stats) {
+ if (p==NULL || size==0) return;
+ uint8_t* base = (uint8_t*)p;
+ while (size >= MI_HUGE_OS_PAGE_SIZE) {
+ mi_os_prim_free(base, MI_HUGE_OS_PAGE_SIZE, true, stats);
+ size -= MI_HUGE_OS_PAGE_SIZE;
+ base += MI_HUGE_OS_PAGE_SIZE;
+ }
+}
+
+/* ----------------------------------------------------------------------------
+Support NUMA aware allocation
+-----------------------------------------------------------------------------*/
+
+_Atomic(size_t) _mi_numa_node_count; // = 0 // cache the node count
+
+size_t _mi_os_numa_node_count_get(void) {
+ size_t count = mi_atomic_load_acquire(&_mi_numa_node_count);
+ if (count <= 0) {
+ long ncount = mi_option_get(mi_option_use_numa_nodes); // given explicitly?
+ if (ncount > 0) {
+ count = (size_t)ncount;
+ }
+ else {
+ count = _mi_prim_numa_node_count(); // or detect dynamically
+ if (count == 0) count = 1;
+ }
+ mi_atomic_store_release(&_mi_numa_node_count, count); // save it
+ _mi_verbose_message("using %zd numa regions\n", count);
+ }
+ return count;
+}
+
+int _mi_os_numa_node_get(mi_os_tld_t* tld) {
+ MI_UNUSED(tld);
+ size_t numa_count = _mi_os_numa_node_count();
+ if (numa_count<=1) return 0; // optimize on single numa node systems: always node 0
+ // never more than the node count and >= 0
+ size_t numa_node = _mi_prim_numa_node();
+ if (numa_node >= numa_count) { numa_node = numa_node % numa_count; }
+ return (int)numa_node;
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/page-queue.c b/contrib/tools/python3/Objects/mimalloc/page-queue.c
new file mode 100644
index 00000000000..cb54b374019
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/page-queue.c
@@ -0,0 +1,332 @@
+/*----------------------------------------------------------------------------
+Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* -----------------------------------------------------------
+ Definition of page queues for each block size
+----------------------------------------------------------- */
+
+#ifndef MI_IN_PAGE_C
+#error "this file should be included from 'page.c'"
+#endif
+
+/* -----------------------------------------------------------
+ Minimal alignment in machine words (i.e. `sizeof(void*)`)
+----------------------------------------------------------- */
+
+#if (MI_MAX_ALIGN_SIZE > 4*MI_INTPTR_SIZE)
+ #error "define alignment for more than 4x word size for this platform"
+#elif (MI_MAX_ALIGN_SIZE > 2*MI_INTPTR_SIZE)
+ #define MI_ALIGN4W // 4 machine words minimal alignment
+#elif (MI_MAX_ALIGN_SIZE > MI_INTPTR_SIZE)
+ #define MI_ALIGN2W // 2 machine words minimal alignment
+#else
+ // ok, default alignment is 1 word
+#endif
+
+
+/* -----------------------------------------------------------
+ Queue query
+----------------------------------------------------------- */
+
+
+static inline bool mi_page_queue_is_huge(const mi_page_queue_t* pq) {
+ return (pq->block_size == (MI_MEDIUM_OBJ_SIZE_MAX+sizeof(uintptr_t)));
+}
+
+static inline bool mi_page_queue_is_full(const mi_page_queue_t* pq) {
+ return (pq->block_size == (MI_MEDIUM_OBJ_SIZE_MAX+(2*sizeof(uintptr_t))));
+}
+
+static inline bool mi_page_queue_is_special(const mi_page_queue_t* pq) {
+ return (pq->block_size > MI_MEDIUM_OBJ_SIZE_MAX);
+}
+
+/* -----------------------------------------------------------
+ Bins
+----------------------------------------------------------- */
+
+// Return the bin for a given field size.
+// Returns MI_BIN_HUGE if the size is too large.
+// We use `wsize` for the size in "machine word sizes",
+// i.e. byte size == `wsize*sizeof(void*)`.
+static inline uint8_t mi_bin(size_t size) {
+ size_t wsize = _mi_wsize_from_size(size);
+ uint8_t bin;
+ if (wsize <= 1) {
+ bin = 1;
+ }
+ #if defined(MI_ALIGN4W)
+ else if (wsize <= 4) {
+ bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
+ }
+ #elif defined(MI_ALIGN2W)
+ else if (wsize <= 8) {
+ bin = (uint8_t)((wsize+1)&~1); // round to double word sizes
+ }
+ #else
+ else if (wsize <= 8) {
+ bin = (uint8_t)wsize;
+ }
+ #endif
+ else if (wsize > MI_MEDIUM_OBJ_WSIZE_MAX) {
+ bin = MI_BIN_HUGE;
+ }
+ else {
+ #if defined(MI_ALIGN4W)
+ if (wsize <= 16) { wsize = (wsize+3)&~3; } // round to 4x word sizes
+ #endif
+ wsize--;
+ // find the highest bit
+ uint8_t b = (uint8_t)mi_bsr(wsize); // note: wsize != 0
+ // and use the top 3 bits to determine the bin (~12.5% worst internal fragmentation).
+ // - adjust with 3 because we use do not round the first 8 sizes
+ // which each get an exact bin
+ bin = ((b << 2) + (uint8_t)((wsize >> (b - 2)) & 0x03)) - 3;
+ mi_assert_internal(bin < MI_BIN_HUGE);
+ }
+ mi_assert_internal(bin > 0 && bin <= MI_BIN_HUGE);
+ return bin;
+}
+
+
+
+/* -----------------------------------------------------------
+ Queue of pages with free blocks
+----------------------------------------------------------- */
+
+uint8_t _mi_bin(size_t size) {
+ return mi_bin(size);
+}
+
+size_t _mi_bin_size(uint8_t bin) {
+ return _mi_heap_empty.pages[bin].block_size;
+}
+
+// Good size for allocation
+size_t mi_good_size(size_t size) mi_attr_noexcept {
+ if (size <= MI_MEDIUM_OBJ_SIZE_MAX) {
+ return _mi_bin_size(mi_bin(size));
+ }
+ else {
+ return _mi_align_up(size,_mi_os_page_size());
+ }
+}
+
+#if (MI_DEBUG>1)
+static bool mi_page_queue_contains(mi_page_queue_t* queue, const mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_page_t* list = queue->first;
+ while (list != NULL) {
+ mi_assert_internal(list->next == NULL || list->next->prev == list);
+ mi_assert_internal(list->prev == NULL || list->prev->next == list);
+ if (list == page) break;
+ list = list->next;
+ }
+ return (list == page);
+}
+
+#endif
+
+#if (MI_DEBUG>1)
+static bool mi_heap_contains_queue(const mi_heap_t* heap, const mi_page_queue_t* pq) {
+ return (pq >= &heap->pages[0] && pq <= &heap->pages[MI_BIN_FULL]);
+}
+#endif
+
+static mi_page_queue_t* mi_page_queue_of(const mi_page_t* page) {
+ uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : mi_bin(page->xblock_size));
+ mi_heap_t* heap = mi_page_heap(page);
+ mi_assert_internal(heap != NULL && bin <= MI_BIN_FULL);
+ mi_page_queue_t* pq = &heap->pages[bin];
+ mi_assert_internal(bin >= MI_BIN_HUGE || page->xblock_size == pq->block_size);
+ mi_assert_expensive(mi_page_queue_contains(pq, page));
+ return pq;
+}
+
+static mi_page_queue_t* mi_heap_page_queue_of(mi_heap_t* heap, const mi_page_t* page) {
+ uint8_t bin = (mi_page_is_in_full(page) ? MI_BIN_FULL : mi_bin(page->xblock_size));
+ mi_assert_internal(bin <= MI_BIN_FULL);
+ mi_page_queue_t* pq = &heap->pages[bin];
+ mi_assert_internal(mi_page_is_in_full(page) || page->xblock_size == pq->block_size);
+ return pq;
+}
+
+// The current small page array is for efficiency and for each
+// small size (up to 256) it points directly to the page for that
+// size without having to compute the bin. This means when the
+// current free page queue is updated for a small bin, we need to update a
+// range of entries in `_mi_page_small_free`.
+static inline void mi_heap_queue_first_update(mi_heap_t* heap, const mi_page_queue_t* pq) {
+ mi_assert_internal(mi_heap_contains_queue(heap,pq));
+ size_t size = pq->block_size;
+ if (size > MI_SMALL_SIZE_MAX) return;
+
+ mi_page_t* page = pq->first;
+ if (pq->first == NULL) page = (mi_page_t*)&_mi_page_empty;
+
+ // find index in the right direct page array
+ size_t start;
+ size_t idx = _mi_wsize_from_size(size);
+ mi_page_t** pages_free = heap->pages_free_direct;
+
+ if (pages_free[idx] == page) return; // already set
+
+ // find start slot
+ if (idx<=1) {
+ start = 0;
+ }
+ else {
+ // find previous size; due to minimal alignment upto 3 previous bins may need to be skipped
+ uint8_t bin = mi_bin(size);
+ const mi_page_queue_t* prev = pq - 1;
+ while( bin == mi_bin(prev->block_size) && prev > &heap->pages[0]) {
+ prev--;
+ }
+ start = 1 + _mi_wsize_from_size(prev->block_size);
+ if (start > idx) start = idx;
+ }
+
+ // set size range to the right page
+ mi_assert(start <= idx);
+ for (size_t sz = start; sz <= idx; sz++) {
+ pages_free[sz] = page;
+ }
+}
+
+/*
+static bool mi_page_queue_is_empty(mi_page_queue_t* queue) {
+ return (queue->first == NULL);
+}
+*/
+
+static void mi_page_queue_remove(mi_page_queue_t* queue, mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(mi_page_queue_contains(queue, page));
+ mi_assert_internal(page->xblock_size == queue->block_size || (page->xblock_size > MI_MEDIUM_OBJ_SIZE_MAX && mi_page_queue_is_huge(queue)) || (mi_page_is_in_full(page) && mi_page_queue_is_full(queue)));
+ mi_heap_t* heap = mi_page_heap(page);
+
+ if (page->prev != NULL) page->prev->next = page->next;
+ if (page->next != NULL) page->next->prev = page->prev;
+ if (page == queue->last) queue->last = page->prev;
+ if (page == queue->first) {
+ queue->first = page->next;
+ // update first
+ mi_assert_internal(mi_heap_contains_queue(heap, queue));
+ mi_heap_queue_first_update(heap,queue);
+ }
+ heap->page_count--;
+ page->next = NULL;
+ page->prev = NULL;
+ // mi_atomic_store_ptr_release(mi_atomic_cast(void*, &page->heap), NULL);
+ mi_page_set_in_full(page,false);
+}
+
+
+static void mi_page_queue_push(mi_heap_t* heap, mi_page_queue_t* queue, mi_page_t* page) {
+ mi_assert_internal(mi_page_heap(page) == heap);
+ mi_assert_internal(!mi_page_queue_contains(queue, page));
+ #if MI_HUGE_PAGE_ABANDON
+ mi_assert_internal(_mi_page_segment(page)->kind != MI_SEGMENT_HUGE);
+ #endif
+ mi_assert_internal(page->xblock_size == queue->block_size ||
+ (page->xblock_size > MI_MEDIUM_OBJ_SIZE_MAX) ||
+ (mi_page_is_in_full(page) && mi_page_queue_is_full(queue)));
+
+ mi_page_set_in_full(page, mi_page_queue_is_full(queue));
+ // mi_atomic_store_ptr_release(mi_atomic_cast(void*, &page->heap), heap);
+ page->next = queue->first;
+ page->prev = NULL;
+ if (queue->first != NULL) {
+ mi_assert_internal(queue->first->prev == NULL);
+ queue->first->prev = page;
+ queue->first = page;
+ }
+ else {
+ queue->first = queue->last = page;
+ }
+
+ // update direct
+ mi_heap_queue_first_update(heap, queue);
+ heap->page_count++;
+}
+
+
+static void mi_page_queue_enqueue_from(mi_page_queue_t* to, mi_page_queue_t* from, mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(mi_page_queue_contains(from, page));
+ mi_assert_expensive(!mi_page_queue_contains(to, page));
+
+ mi_assert_internal((page->xblock_size == to->block_size && page->xblock_size == from->block_size) ||
+ (page->xblock_size == to->block_size && mi_page_queue_is_full(from)) ||
+ (page->xblock_size == from->block_size && mi_page_queue_is_full(to)) ||
+ (page->xblock_size > MI_LARGE_OBJ_SIZE_MAX && mi_page_queue_is_huge(to)) ||
+ (page->xblock_size > MI_LARGE_OBJ_SIZE_MAX && mi_page_queue_is_full(to)));
+
+ mi_heap_t* heap = mi_page_heap(page);
+ if (page->prev != NULL) page->prev->next = page->next;
+ if (page->next != NULL) page->next->prev = page->prev;
+ if (page == from->last) from->last = page->prev;
+ if (page == from->first) {
+ from->first = page->next;
+ // update first
+ mi_assert_internal(mi_heap_contains_queue(heap, from));
+ mi_heap_queue_first_update(heap, from);
+ }
+
+ page->prev = to->last;
+ page->next = NULL;
+ if (to->last != NULL) {
+ mi_assert_internal(heap == mi_page_heap(to->last));
+ to->last->next = page;
+ to->last = page;
+ }
+ else {
+ to->first = page;
+ to->last = page;
+ mi_heap_queue_first_update(heap, to);
+ }
+
+ mi_page_set_in_full(page, mi_page_queue_is_full(to));
+}
+
+// Only called from `mi_heap_absorb`.
+size_t _mi_page_queue_append(mi_heap_t* heap, mi_page_queue_t* pq, mi_page_queue_t* append) {
+ mi_assert_internal(mi_heap_contains_queue(heap,pq));
+ mi_assert_internal(pq->block_size == append->block_size);
+
+ if (append->first==NULL) return 0;
+
+ // set append pages to new heap and count
+ size_t count = 0;
+ for (mi_page_t* page = append->first; page != NULL; page = page->next) {
+ // inline `mi_page_set_heap` to avoid wrong assertion during absorption;
+ // in this case it is ok to be delayed freeing since both "to" and "from" heap are still alive.
+ mi_atomic_store_release(&page->xheap, (uintptr_t)heap);
+ // set the flag to delayed free (not overriding NEVER_DELAYED_FREE) which has as a
+ // side effect that it spins until any DELAYED_FREEING is finished. This ensures
+ // that after appending only the new heap will be used for delayed free operations.
+ _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, false);
+ count++;
+ }
+
+ if (pq->last==NULL) {
+ // take over afresh
+ mi_assert_internal(pq->first==NULL);
+ pq->first = append->first;
+ pq->last = append->last;
+ mi_heap_queue_first_update(heap, pq);
+ }
+ else {
+ // append to end
+ mi_assert_internal(pq->last!=NULL);
+ mi_assert_internal(append->first!=NULL);
+ pq->last->next = append->first;
+ append->first->prev = pq->last;
+ pq->last = append->last;
+ }
+ return count;
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/page.c b/contrib/tools/python3/Objects/mimalloc/page.c
new file mode 100644
index 00000000000..25ecd6ec7d7
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/page.c
@@ -0,0 +1,972 @@
+/*----------------------------------------------------------------------------
+Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* -----------------------------------------------------------
+ The core of the allocator. Every segment contains
+ pages of a certain block size. The main function
+ exported is `mi_malloc_generic`.
+----------------------------------------------------------- */
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+
+/* -----------------------------------------------------------
+ Definition of page queues for each block size
+----------------------------------------------------------- */
+
+#define MI_IN_PAGE_C
+#include "page-queue.c"
+#undef MI_IN_PAGE_C
+
+
+/* -----------------------------------------------------------
+ Page helpers
+----------------------------------------------------------- */
+
+// Index a block in a page
+static inline mi_block_t* mi_page_block_at(const mi_page_t* page, void* page_start, size_t block_size, size_t i) {
+ MI_UNUSED(page);
+ mi_assert_internal(page != NULL);
+ mi_assert_internal(i <= page->reserved);
+ return (mi_block_t*)((uint8_t*)page_start + (i * block_size));
+}
+
+static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t size, mi_tld_t* tld);
+static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld);
+
+#if (MI_DEBUG>=3)
+static size_t mi_page_list_count(mi_page_t* page, mi_block_t* head) {
+ size_t count = 0;
+ while (head != NULL) {
+ mi_assert_internal(page == _mi_ptr_page(head));
+ count++;
+ head = mi_block_next(page, head);
+ }
+ return count;
+}
+
+/*
+// Start of the page available memory
+static inline uint8_t* mi_page_area(const mi_page_t* page) {
+ return _mi_page_start(_mi_page_segment(page), page, NULL);
+}
+*/
+
+static bool mi_page_list_is_valid(mi_page_t* page, mi_block_t* p) {
+ size_t psize;
+ uint8_t* page_area = _mi_page_start(_mi_page_segment(page), page, &psize);
+ mi_block_t* start = (mi_block_t*)page_area;
+ mi_block_t* end = (mi_block_t*)(page_area + psize);
+ while(p != NULL) {
+ if (p < start || p >= end) return false;
+ p = mi_block_next(page, p);
+ }
+#if MI_DEBUG>3 // generally too expensive to check this
+ if (page->free_is_zero) {
+ const size_t ubsize = mi_page_usable_block_size(page);
+ for (mi_block_t* block = page->free; block != NULL; block = mi_block_next(page, block)) {
+ mi_assert_expensive(mi_mem_is_zero(block + 1, ubsize - sizeof(mi_block_t)));
+ }
+ }
+#endif
+ return true;
+}
+
+static bool mi_page_is_valid_init(mi_page_t* page) {
+ mi_assert_internal(page->xblock_size > 0);
+ mi_assert_internal(page->used <= page->capacity);
+ mi_assert_internal(page->capacity <= page->reserved);
+
+ mi_segment_t* segment = _mi_page_segment(page);
+ uint8_t* start = _mi_page_start(segment,page,NULL);
+ mi_assert_internal(start == _mi_segment_page_start(segment,page,NULL));
+ //const size_t bsize = mi_page_block_size(page);
+ //mi_assert_internal(start + page->capacity*page->block_size == page->top);
+
+ mi_assert_internal(mi_page_list_is_valid(page,page->free));
+ mi_assert_internal(mi_page_list_is_valid(page,page->local_free));
+
+ #if MI_DEBUG>3 // generally too expensive to check this
+ if (page->free_is_zero) {
+ const size_t ubsize = mi_page_usable_block_size(page);
+ for(mi_block_t* block = page->free; block != NULL; block = mi_block_next(page,block)) {
+ mi_assert_expensive(mi_mem_is_zero(block + 1, ubsize - sizeof(mi_block_t)));
+ }
+ }
+ #endif
+
+ #if !MI_TRACK_ENABLED && !MI_TSAN
+ mi_block_t* tfree = mi_page_thread_free(page);
+ mi_assert_internal(mi_page_list_is_valid(page, tfree));
+ //size_t tfree_count = mi_page_list_count(page, tfree);
+ //mi_assert_internal(tfree_count <= page->thread_freed + 1);
+ #endif
+
+ size_t free_count = mi_page_list_count(page, page->free) + mi_page_list_count(page, page->local_free);
+ mi_assert_internal(page->used + free_count == page->capacity);
+
+ return true;
+}
+
+extern bool _mi_process_is_initialized; // has mi_process_init been called?
+
+bool _mi_page_is_valid(mi_page_t* page) {
+ mi_assert_internal(mi_page_is_valid_init(page));
+ #if MI_SECURE
+ mi_assert_internal(page->keys[0] != 0);
+ #endif
+ if (mi_page_heap(page)!=NULL) {
+ mi_segment_t* segment = _mi_page_segment(page);
+
+ mi_assert_internal(!_mi_process_is_initialized || segment->thread_id==0 || segment->thread_id == mi_page_heap(page)->thread_id);
+ #if MI_HUGE_PAGE_ABANDON
+ if (segment->kind != MI_SEGMENT_HUGE)
+ #endif
+ {
+ mi_page_queue_t* pq = mi_page_queue_of(page);
+ mi_assert_internal(mi_page_queue_contains(pq, page));
+ mi_assert_internal(pq->block_size==mi_page_block_size(page) || mi_page_block_size(page) > MI_MEDIUM_OBJ_SIZE_MAX || mi_page_is_in_full(page));
+ mi_assert_internal(mi_heap_contains_queue(mi_page_heap(page),pq));
+ }
+ }
+ return true;
+}
+#endif
+
+void _mi_page_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) {
+ while (!_mi_page_try_use_delayed_free(page, delay, override_never)) {
+ mi_atomic_yield();
+ }
+}
+
+bool _mi_page_try_use_delayed_free(mi_page_t* page, mi_delayed_t delay, bool override_never) {
+ mi_thread_free_t tfreex;
+ mi_delayed_t old_delay;
+ mi_thread_free_t tfree;
+ size_t yield_count = 0;
+ do {
+ tfree = mi_atomic_load_acquire(&page->xthread_free); // note: must acquire as we can break/repeat this loop and not do a CAS;
+ tfreex = mi_tf_set_delayed(tfree, delay);
+ old_delay = mi_tf_delayed(tfree);
+ if mi_unlikely(old_delay == MI_DELAYED_FREEING) {
+ if (yield_count >= 4) return false; // give up after 4 tries
+ yield_count++;
+ mi_atomic_yield(); // delay until outstanding MI_DELAYED_FREEING are done.
+ // tfree = mi_tf_set_delayed(tfree, MI_NO_DELAYED_FREE); // will cause CAS to busy fail
+ }
+ else if (delay == old_delay) {
+ break; // avoid atomic operation if already equal
+ }
+ else if (!override_never && old_delay == MI_NEVER_DELAYED_FREE) {
+ break; // leave never-delayed flag set
+ }
+ } while ((old_delay == MI_DELAYED_FREEING) ||
+ !mi_atomic_cas_weak_release(&page->xthread_free, &tfree, tfreex));
+
+ return true; // success
+}
+
+/* -----------------------------------------------------------
+ Page collect the `local_free` and `thread_free` lists
+----------------------------------------------------------- */
+
+// Collect the local `thread_free` list using an atomic exchange.
+// Note: The exchange must be done atomically as this is used right after
+// moving to the full list in `mi_page_collect_ex` and we need to
+// ensure that there was no race where the page became unfull just before the move.
+static void _mi_page_thread_free_collect(mi_page_t* page)
+{
+ mi_block_t* head;
+ mi_thread_free_t tfreex;
+ mi_thread_free_t tfree = mi_atomic_load_relaxed(&page->xthread_free);
+ do {
+ head = mi_tf_block(tfree);
+ tfreex = mi_tf_set_block(tfree,NULL);
+ } while (!mi_atomic_cas_weak_acq_rel(&page->xthread_free, &tfree, tfreex));
+
+ // return if the list is empty
+ if (head == NULL) return;
+
+ // find the tail -- also to get a proper count (without data races)
+ uint32_t max_count = page->capacity; // cannot collect more than capacity
+ uint32_t count = 1;
+ mi_block_t* tail = head;
+ mi_block_t* next;
+ while ((next = mi_block_next(page,tail)) != NULL && count <= max_count) {
+ count++;
+ tail = next;
+ }
+ // if `count > max_count` there was a memory corruption (possibly infinite list due to double multi-threaded free)
+ if (count > max_count) {
+ _mi_error_message(EFAULT, "corrupted thread-free list\n");
+ return; // the thread-free items cannot be freed
+ }
+
+ // and append the current local free list
+ mi_block_set_next(page,tail, page->local_free);
+ page->local_free = head;
+
+ // update counts now
+ page->used -= count;
+}
+
+void _mi_page_free_collect(mi_page_t* page, bool force) {
+ mi_assert_internal(page!=NULL);
+
+ // collect the thread free list
+ if (force || mi_page_thread_free(page) != NULL) { // quick test to avoid an atomic operation
+ _mi_page_thread_free_collect(page);
+ }
+
+ // and the local free list
+ if (page->local_free != NULL) {
+ // any previous QSBR goals are no longer valid because we reused the page
+ _PyMem_mi_page_clear_qsbr(page);
+
+ if mi_likely(page->free == NULL) {
+ // usual case
+ page->free = page->local_free;
+ page->local_free = NULL;
+ page->free_is_zero = false;
+ }
+ else if (force) {
+ // append -- only on shutdown (force) as this is a linear operation
+ mi_block_t* tail = page->local_free;
+ mi_block_t* next;
+ while ((next = mi_block_next(page, tail)) != NULL) {
+ tail = next;
+ }
+ mi_block_set_next(page, tail, page->free);
+ page->free = page->local_free;
+ page->local_free = NULL;
+ page->free_is_zero = false;
+ }
+ }
+
+ mi_assert_internal(!force || page->local_free == NULL);
+}
+
+
+
+/* -----------------------------------------------------------
+ Page fresh and retire
+----------------------------------------------------------- */
+
+// called from segments when reclaiming abandoned pages
+void _mi_page_reclaim(mi_heap_t* heap, mi_page_t* page) {
+ mi_assert_expensive(mi_page_is_valid_init(page));
+
+ mi_assert_internal(mi_page_heap(page) == heap);
+ mi_assert_internal(mi_page_thread_free_flag(page) != MI_NEVER_DELAYED_FREE);
+ #if MI_HUGE_PAGE_ABANDON
+ mi_assert_internal(_mi_page_segment(page)->kind != MI_SEGMENT_HUGE);
+ #endif
+
+ // TODO: push on full queue immediately if it is full?
+ mi_page_queue_t* pq = mi_page_queue(heap, mi_page_block_size(page));
+ mi_page_queue_push(heap, pq, page);
+ _PyMem_mi_page_reclaimed(page);
+ mi_assert_expensive(_mi_page_is_valid(page));
+}
+
+// allocate a fresh page from a segment
+static mi_page_t* mi_page_fresh_alloc(mi_heap_t* heap, mi_page_queue_t* pq, size_t block_size, size_t page_alignment) {
+ #if !MI_HUGE_PAGE_ABANDON
+ mi_assert_internal(pq != NULL);
+ mi_assert_internal(mi_heap_contains_queue(heap, pq));
+ mi_assert_internal(page_alignment > 0 || block_size > MI_MEDIUM_OBJ_SIZE_MAX || block_size == pq->block_size);
+ #endif
+ mi_page_t* page = _mi_segment_page_alloc(heap, block_size, page_alignment, &heap->tld->segments, &heap->tld->os);
+ if (page == NULL) {
+ // this may be out-of-memory, or an abandoned page was reclaimed (and in our queue)
+ return NULL;
+ }
+ mi_assert_internal(page_alignment >0 || block_size > MI_MEDIUM_OBJ_SIZE_MAX || _mi_page_segment(page)->kind != MI_SEGMENT_HUGE);
+ mi_assert_internal(pq!=NULL || page->xblock_size != 0);
+ mi_assert_internal(pq!=NULL || mi_page_block_size(page) >= block_size);
+ // a fresh page was found, initialize it
+ const size_t full_block_size = ((pq == NULL || mi_page_queue_is_huge(pq)) ? mi_page_block_size(page) : block_size); // see also: mi_segment_huge_page_alloc
+ mi_assert_internal(full_block_size >= block_size);
+ mi_page_init(heap, page, full_block_size, heap->tld);
+ mi_heap_stat_increase(heap, pages, 1);
+ if (pq != NULL) { mi_page_queue_push(heap, pq, page); }
+ mi_assert_expensive(_mi_page_is_valid(page));
+ return page;
+}
+
+// Get a fresh page to use
+static mi_page_t* mi_page_fresh(mi_heap_t* heap, mi_page_queue_t* pq) {
+ mi_assert_internal(mi_heap_contains_queue(heap, pq));
+ mi_page_t* page = mi_page_fresh_alloc(heap, pq, pq->block_size, 0);
+ if (page==NULL) return NULL;
+ mi_assert_internal(pq->block_size==mi_page_block_size(page));
+ mi_assert_internal(pq==mi_page_queue(heap, mi_page_block_size(page)));
+ return page;
+}
+
+/* -----------------------------------------------------------
+ Do any delayed frees
+ (put there by other threads if they deallocated in a full page)
+----------------------------------------------------------- */
+void _mi_heap_delayed_free_all(mi_heap_t* heap) {
+ while (!_mi_heap_delayed_free_partial(heap)) {
+ mi_atomic_yield();
+ }
+}
+
+// returns true if all delayed frees were processed
+bool _mi_heap_delayed_free_partial(mi_heap_t* heap) {
+ // take over the list (note: no atomic exchange since it is often NULL)
+ mi_block_t* block = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free);
+ while (block != NULL && !mi_atomic_cas_ptr_weak_acq_rel(mi_block_t, &heap->thread_delayed_free, &block, NULL)) { /* nothing */ };
+ bool all_freed = true;
+
+ // and free them all
+ while(block != NULL) {
+ mi_block_t* next = mi_block_nextx(heap,block, heap->keys);
+ // use internal free instead of regular one to keep stats etc correct
+ if (!_mi_free_delayed_block(block)) {
+ // we might already start delayed freeing while another thread has not yet
+ // reset the delayed_freeing flag; in that case delay it further by reinserting the current block
+ // into the delayed free list
+ all_freed = false;
+ mi_block_t* dfree = mi_atomic_load_ptr_relaxed(mi_block_t, &heap->thread_delayed_free);
+ do {
+ mi_block_set_nextx(heap, block, dfree, heap->keys);
+ } while (!mi_atomic_cas_ptr_weak_release(mi_block_t,&heap->thread_delayed_free, &dfree, block));
+ }
+ block = next;
+ }
+ return all_freed;
+}
+
+/* -----------------------------------------------------------
+ Unfull, abandon, free and retire
+----------------------------------------------------------- */
+
+// Move a page from the full list back to a regular list
+void _mi_page_unfull(mi_page_t* page) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ mi_assert_internal(mi_page_is_in_full(page));
+ if (!mi_page_is_in_full(page)) return;
+
+ mi_heap_t* heap = mi_page_heap(page);
+ mi_page_queue_t* pqfull = &heap->pages[MI_BIN_FULL];
+ mi_page_set_in_full(page, false); // to get the right queue
+ mi_page_queue_t* pq = mi_heap_page_queue_of(heap, page);
+ mi_page_set_in_full(page, true);
+ mi_page_queue_enqueue_from(pq, pqfull, page);
+}
+
+static void mi_page_to_full(mi_page_t* page, mi_page_queue_t* pq) {
+ mi_assert_internal(pq == mi_page_queue_of(page));
+ mi_assert_internal(!mi_page_immediate_available(page));
+ mi_assert_internal(!mi_page_is_in_full(page));
+
+ if (mi_page_is_in_full(page)) return;
+ mi_page_queue_enqueue_from(&mi_page_heap(page)->pages[MI_BIN_FULL], pq, page);
+ _mi_page_free_collect(page,false); // try to collect right away in case another thread freed just before MI_USE_DELAYED_FREE was set
+}
+
+
+// Abandon a page with used blocks at the end of a thread.
+// Note: only call if it is ensured that no references exist from
+// the `page->heap->thread_delayed_free` into this page.
+// Currently only called through `mi_heap_collect_ex` which ensures this.
+void _mi_page_abandon(mi_page_t* page, mi_page_queue_t* pq) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ mi_assert_internal(pq == mi_page_queue_of(page));
+ mi_assert_internal(mi_page_heap(page) != NULL);
+
+ mi_heap_t* pheap = mi_page_heap(page);
+
+#ifdef Py_GIL_DISABLED
+ if (page->qsbr_node.next != NULL) {
+ // remove from QSBR queue, but keep the goal
+ llist_remove(&page->qsbr_node);
+ }
+#endif
+
+ // remove from our page list
+ mi_segments_tld_t* segments_tld = &pheap->tld->segments;
+ mi_page_queue_remove(pq, page);
+
+ // page is no longer associated with our heap
+ mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE);
+ mi_page_set_heap(page, NULL);
+
+#if (MI_DEBUG>1) && !MI_TRACK_ENABLED
+ // check there are no references left..
+ for (mi_block_t* block = (mi_block_t*)pheap->thread_delayed_free; block != NULL; block = mi_block_nextx(pheap, block, pheap->keys)) {
+ mi_assert_internal(_mi_ptr_page(block) != page);
+ }
+#endif
+
+ // and abandon it
+ mi_assert_internal(mi_page_heap(page) == NULL);
+ _mi_segment_page_abandon(page,segments_tld);
+}
+
+
+// Free a page with no more free blocks
+void _mi_page_free(mi_page_t* page, mi_page_queue_t* pq, bool force) {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ mi_assert_internal(pq == mi_page_queue_of(page));
+ mi_assert_internal(mi_page_all_free(page));
+ mi_assert_internal(mi_page_thread_free_flag(page)!=MI_DELAYED_FREEING);
+
+ // no more aligned blocks in here
+ mi_page_set_has_aligned(page, false);
+
+ mi_heap_t* heap = mi_page_heap(page);
+
+#ifdef Py_GIL_DISABLED
+ mi_assert_internal(page->qsbr_goal == 0);
+ mi_assert_internal(page->qsbr_node.next == NULL);
+#endif
+
+ // remove from the page list
+ // (no need to do _mi_heap_delayed_free first as all blocks are already free)
+ mi_segments_tld_t* segments_tld = &heap->tld->segments;
+ mi_page_queue_remove(pq, page);
+
+ // and free it
+ mi_page_set_heap(page,NULL);
+ _mi_segment_page_free(page, force, segments_tld);
+}
+
+// Retire parameters
+#define MI_MAX_RETIRE_SIZE (MI_MEDIUM_OBJ_SIZE_MAX)
+#define MI_RETIRE_CYCLES (16)
+
+// Retire a page with no more used blocks
+// Important to not retire too quickly though as new
+// allocations might coming.
+// Note: called from `mi_free` and benchmarks often
+// trigger this due to freeing everything and then
+// allocating again so careful when changing this.
+void _mi_page_retire(mi_page_t* page) mi_attr_noexcept {
+ mi_assert_internal(page != NULL);
+ mi_assert_expensive(_mi_page_is_valid(page));
+ mi_assert_internal(mi_page_all_free(page));
+
+ mi_page_set_has_aligned(page, false);
+
+ // any previous QSBR goals are no longer valid because we reused the page
+ _PyMem_mi_page_clear_qsbr(page);
+
+ // don't retire too often..
+ // (or we end up retiring and re-allocating most of the time)
+ // NOTE: refine this more: we should not retire if this
+ // is the only page left with free blocks. It is not clear
+ // how to check this efficiently though...
+ // for now, we don't retire if it is the only page left of this size class.
+ mi_page_queue_t* pq = mi_page_queue_of(page);
+ if mi_likely(page->xblock_size <= MI_MAX_RETIRE_SIZE && !mi_page_queue_is_special(pq)) { // not too large && not full or huge queue?
+ if (pq->last==page && pq->first==page) { // the only page in the queue?
+ mi_stat_counter_increase(_mi_stats_main.page_no_retire,1);
+ page->retire_expire = 1 + (page->xblock_size <= MI_SMALL_OBJ_SIZE_MAX ? MI_RETIRE_CYCLES : MI_RETIRE_CYCLES/4);
+ mi_heap_t* heap = mi_page_heap(page);
+ mi_assert_internal(pq >= heap->pages);
+ const size_t index = pq - heap->pages;
+ mi_assert_internal(index < MI_BIN_FULL && index < MI_BIN_HUGE);
+ if (index < heap->page_retired_min) heap->page_retired_min = index;
+ if (index > heap->page_retired_max) heap->page_retired_max = index;
+ mi_assert_internal(mi_page_all_free(page));
+ return; // dont't free after all
+ }
+ }
+ _PyMem_mi_page_maybe_free(page, pq, false);
+}
+
+// free retired pages: we don't need to look at the entire queues
+// since we only retire pages that are at the head position in a queue.
+void _mi_heap_collect_retired(mi_heap_t* heap, bool force) {
+ size_t min = MI_BIN_FULL;
+ size_t max = 0;
+ for(size_t bin = heap->page_retired_min; bin <= heap->page_retired_max; bin++) {
+ mi_page_queue_t* pq = &heap->pages[bin];
+ mi_page_t* page = pq->first;
+ if (page != NULL && page->retire_expire != 0) {
+ if (mi_page_all_free(page)) {
+ page->retire_expire--;
+ if (force || page->retire_expire == 0) {
+#ifdef Py_GIL_DISABLED
+ mi_assert_internal(page->qsbr_goal == 0);
+#endif
+ _PyMem_mi_page_maybe_free(page, pq, force);
+ }
+ else {
+ // keep retired, update min/max
+ if (bin < min) min = bin;
+ if (bin > max) max = bin;
+ }
+ }
+ else {
+ page->retire_expire = 0;
+ }
+ }
+ }
+ heap->page_retired_min = min;
+ heap->page_retired_max = max;
+}
+
+
+/* -----------------------------------------------------------
+ Initialize the initial free list in a page.
+ In secure mode we initialize a randomized list by
+ alternating between slices.
+----------------------------------------------------------- */
+
+#define MI_MAX_SLICE_SHIFT (6) // at most 64 slices
+#define MI_MAX_SLICES (1UL << MI_MAX_SLICE_SHIFT)
+#define MI_MIN_SLICES (2)
+
+static void mi_page_free_list_extend_secure(mi_heap_t* const heap, mi_page_t* const page, const size_t bsize, const size_t extend, mi_stats_t* const stats) {
+ MI_UNUSED(stats);
+ #if (MI_SECURE<=2)
+ mi_assert_internal(page->free == NULL);
+ mi_assert_internal(page->local_free == NULL);
+ #endif
+ mi_assert_internal(page->capacity + extend <= page->reserved);
+ mi_assert_internal(bsize == mi_page_block_size(page));
+ void* const page_area = _mi_page_start(_mi_page_segment(page), page, NULL);
+
+ // initialize a randomized free list
+ // set up `slice_count` slices to alternate between
+ size_t shift = MI_MAX_SLICE_SHIFT;
+ while ((extend >> shift) == 0) {
+ shift--;
+ }
+ const size_t slice_count = (size_t)1U << shift;
+ const size_t slice_extend = extend / slice_count;
+ mi_assert_internal(slice_extend >= 1);
+ mi_block_t* blocks[MI_MAX_SLICES]; // current start of the slice
+ size_t counts[MI_MAX_SLICES]; // available objects in the slice
+ for (size_t i = 0; i < slice_count; i++) {
+ blocks[i] = mi_page_block_at(page, page_area, bsize, page->capacity + i*slice_extend);
+ counts[i] = slice_extend;
+ }
+ counts[slice_count-1] += (extend % slice_count); // final slice holds the modulus too (todo: distribute evenly?)
+
+ // and initialize the free list by randomly threading through them
+ // set up first element
+ const uintptr_t r = _mi_heap_random_next(heap);
+ size_t current = r % slice_count;
+ counts[current]--;
+ mi_block_t* const free_start = blocks[current];
+ // and iterate through the rest; use `random_shuffle` for performance
+ uintptr_t rnd = _mi_random_shuffle(r|1); // ensure not 0
+ for (size_t i = 1; i < extend; i++) {
+ // call random_shuffle only every INTPTR_SIZE rounds
+ const size_t round = i%MI_INTPTR_SIZE;
+ if (round == 0) rnd = _mi_random_shuffle(rnd);
+ // select a random next slice index
+ size_t next = ((rnd >> 8*round) & (slice_count-1));
+ while (counts[next]==0) { // ensure it still has space
+ next++;
+ if (next==slice_count) next = 0;
+ }
+ // and link the current block to it
+ counts[next]--;
+ mi_block_t* const block = blocks[current];
+ blocks[current] = (mi_block_t*)((uint8_t*)block + bsize); // bump to the following block
+ mi_block_set_next(page, block, blocks[next]); // and set next; note: we may have `current == next`
+ current = next;
+ }
+ // prepend to the free list (usually NULL)
+ mi_block_set_next(page, blocks[current], page->free); // end of the list
+ page->free = free_start;
+}
+
+static mi_decl_noinline void mi_page_free_list_extend( mi_page_t* const page, const size_t bsize, const size_t extend, mi_stats_t* const stats)
+{
+ MI_UNUSED(stats);
+ #if (MI_SECURE <= 2)
+ mi_assert_internal(page->free == NULL);
+ mi_assert_internal(page->local_free == NULL);
+ #endif
+ mi_assert_internal(page->capacity + extend <= page->reserved);
+ mi_assert_internal(bsize == mi_page_block_size(page));
+ void* const page_area = _mi_page_start(_mi_page_segment(page), page, NULL );
+
+ mi_block_t* const start = mi_page_block_at(page, page_area, bsize, page->capacity);
+
+ // initialize a sequential free list
+ mi_block_t* const last = mi_page_block_at(page, page_area, bsize, page->capacity + extend - 1);
+ mi_block_t* block = start;
+ while(block <= last) {
+ mi_block_t* next = (mi_block_t*)((uint8_t*)block + bsize);
+ mi_block_set_next(page,block,next);
+ block = next;
+ }
+ // prepend to free list (usually `NULL`)
+ mi_block_set_next(page, last, page->free);
+ page->free = start;
+}
+
+/* -----------------------------------------------------------
+ Page initialize and extend the capacity
+----------------------------------------------------------- */
+
+#define MI_MAX_EXTEND_SIZE (4*1024) // heuristic, one OS page seems to work well.
+#if (MI_SECURE>0)
+#define MI_MIN_EXTEND (8*MI_SECURE) // extend at least by this many
+#else
+#define MI_MIN_EXTEND (4)
+#endif
+
+// Extend the capacity (up to reserved) by initializing a free list
+// We do at most `MI_MAX_EXTEND` to avoid touching too much memory
+// Note: we also experimented with "bump" allocation on the first
+// allocations but this did not speed up any benchmark (due to an
+// extra test in malloc? or cache effects?)
+static void mi_page_extend_free(mi_heap_t* heap, mi_page_t* page, mi_tld_t* tld) {
+ MI_UNUSED(tld);
+ mi_assert_expensive(mi_page_is_valid_init(page));
+ #if (MI_SECURE<=2)
+ mi_assert(page->free == NULL);
+ mi_assert(page->local_free == NULL);
+ if (page->free != NULL) return;
+ #endif
+ if (page->capacity >= page->reserved) return;
+
+ size_t page_size;
+ _mi_page_start(_mi_page_segment(page), page, &page_size);
+ mi_stat_counter_increase(tld->stats.pages_extended, 1);
+
+ // calculate the extend count
+ const size_t bsize = (page->xblock_size < MI_HUGE_BLOCK_SIZE ? page->xblock_size : page_size);
+ size_t extend = page->reserved - page->capacity;
+ mi_assert_internal(extend > 0);
+
+ size_t max_extend = (bsize >= MI_MAX_EXTEND_SIZE ? MI_MIN_EXTEND : MI_MAX_EXTEND_SIZE/(uint32_t)bsize);
+ if (max_extend < MI_MIN_EXTEND) { max_extend = MI_MIN_EXTEND; }
+ mi_assert_internal(max_extend > 0);
+
+ if (extend > max_extend) {
+ // ensure we don't touch memory beyond the page to reduce page commit.
+ // the `lean` benchmark tests this. Going from 1 to 8 increases rss by 50%.
+ extend = max_extend;
+ }
+
+ mi_assert_internal(extend > 0 && extend + page->capacity <= page->reserved);
+ mi_assert_internal(extend < (1UL<<16));
+
+ // and append the extend the free list
+ if (extend < MI_MIN_SLICES || MI_SECURE==0) { //!mi_option_is_enabled(mi_option_secure)) {
+ mi_page_free_list_extend(page, bsize, extend, &tld->stats );
+ }
+ else {
+ mi_page_free_list_extend_secure(heap, page, bsize, extend, &tld->stats);
+ }
+ // enable the new free list
+ page->capacity += (uint16_t)extend;
+ mi_stat_increase(tld->stats.page_committed, extend * bsize);
+ mi_assert_expensive(mi_page_is_valid_init(page));
+}
+
+// Initialize a fresh page
+static void mi_page_init(mi_heap_t* heap, mi_page_t* page, size_t block_size, mi_tld_t* tld) {
+ mi_assert(page != NULL);
+ mi_segment_t* segment = _mi_page_segment(page);
+ mi_assert(segment != NULL);
+ mi_assert_internal(block_size > 0);
+ // set fields
+ mi_page_set_heap(page, heap);
+ page->tag = heap->tag;
+ page->use_qsbr = heap->page_use_qsbr;
+ page->debug_offset = heap->debug_offset;
+ page->xblock_size = (block_size < MI_HUGE_BLOCK_SIZE ? (uint32_t)block_size : MI_HUGE_BLOCK_SIZE); // initialize before _mi_segment_page_start
+ size_t page_size;
+ const void* page_start = _mi_segment_page_start(segment, page, &page_size);
+ MI_UNUSED(page_start);
+ mi_track_mem_noaccess(page_start,page_size);
+ mi_assert_internal(mi_page_block_size(page) <= page_size);
+ mi_assert_internal(page_size <= page->slice_count*MI_SEGMENT_SLICE_SIZE);
+ mi_assert_internal(page_size / block_size < (1L<<16));
+ page->reserved = (uint16_t)(page_size / block_size);
+ mi_assert_internal(page->reserved > 0);
+ #if (MI_PADDING || MI_ENCODE_FREELIST)
+ page->keys[0] = _mi_heap_random_next(heap);
+ page->keys[1] = _mi_heap_random_next(heap);
+ #endif
+ page->free_is_zero = page->is_zero_init;
+ #if MI_DEBUG>2
+ if (page->is_zero_init) {
+ mi_track_mem_defined(page_start, page_size);
+ mi_assert_expensive(mi_mem_is_zero(page_start, page_size));
+ }
+ #endif
+
+ mi_assert_internal(page->is_committed);
+ mi_assert_internal(page->capacity == 0);
+ mi_assert_internal(page->free == NULL);
+ mi_assert_internal(page->used == 0);
+ mi_assert_internal(page->xthread_free == 0);
+ mi_assert_internal(page->next == NULL);
+ mi_assert_internal(page->prev == NULL);
+#ifdef Py_GIL_DISABLED
+ mi_assert_internal(page->qsbr_goal == 0);
+ mi_assert_internal(page->qsbr_node.next == NULL);
+#endif
+ mi_assert_internal(page->retire_expire == 0);
+ mi_assert_internal(!mi_page_has_aligned(page));
+ #if (MI_PADDING || MI_ENCODE_FREELIST)
+ mi_assert_internal(page->keys[0] != 0);
+ mi_assert_internal(page->keys[1] != 0);
+ #endif
+ mi_assert_expensive(mi_page_is_valid_init(page));
+
+ // initialize an initial free list
+ mi_page_extend_free(heap,page,tld);
+ mi_assert(mi_page_immediate_available(page));
+}
+
+
+/* -----------------------------------------------------------
+ Find pages with free blocks
+-------------------------------------------------------------*/
+
+// Find a page with free blocks of `page->block_size`.
+static mi_page_t* mi_page_queue_find_free_ex(mi_heap_t* heap, mi_page_queue_t* pq, bool first_try)
+{
+ // search through the pages in "next fit" order
+ #if MI_STAT
+ size_t count = 0;
+ #endif
+ mi_page_t* page = pq->first;
+ while (page != NULL)
+ {
+ mi_page_t* next = page->next; // remember next
+ #if MI_STAT
+ count++;
+ #endif
+
+ // 0. collect freed blocks by us and other threads
+ _mi_page_free_collect(page, false);
+
+ // 1. if the page contains free blocks, we are done
+ if (mi_page_immediate_available(page)) {
+ break; // pick this one
+ }
+
+ // 2. Try to extend
+ if (page->capacity < page->reserved) {
+ mi_page_extend_free(heap, page, heap->tld);
+ mi_assert_internal(mi_page_immediate_available(page));
+ break;
+ }
+
+ // 3. If the page is completely full, move it to the `mi_pages_full`
+ // queue so we don't visit long-lived pages too often.
+ mi_assert_internal(!mi_page_is_in_full(page) && !mi_page_immediate_available(page));
+ mi_page_to_full(page, pq);
+
+ page = next;
+ } // for each page
+
+ mi_heap_stat_counter_increase(heap, searches, count);
+
+ if (page == NULL) {
+ _PyMem_mi_heap_collect_qsbr(heap); // some pages might be safe to free now
+ _mi_heap_collect_retired(heap, false); // perhaps make a page available?
+ page = mi_page_fresh(heap, pq);
+ if (page == NULL && first_try) {
+ // out-of-memory _or_ an abandoned page with free blocks was reclaimed, try once again
+ page = mi_page_queue_find_free_ex(heap, pq, false);
+ }
+ }
+ else {
+ mi_assert(pq->first == page);
+ page->retire_expire = 0;
+ _PyMem_mi_page_clear_qsbr(page);
+ }
+ mi_assert_internal(page == NULL || mi_page_immediate_available(page));
+ return page;
+}
+
+
+
+// Find a page with free blocks of `size`.
+static inline mi_page_t* mi_find_free_page(mi_heap_t* heap, size_t size) {
+ mi_page_queue_t* pq = mi_page_queue(heap,size);
+ mi_page_t* page = pq->first;
+ if (page != NULL) {
+ #if (MI_SECURE>=3) // in secure mode, we extend half the time to increase randomness
+ if (page->capacity < page->reserved && ((_mi_heap_random_next(heap) & 1) == 1)) {
+ mi_page_extend_free(heap, page, heap->tld);
+ mi_assert_internal(mi_page_immediate_available(page));
+ }
+ else
+ #endif
+ {
+ _mi_page_free_collect(page,false);
+ }
+
+ if (mi_page_immediate_available(page)) {
+ page->retire_expire = 0;
+ _PyMem_mi_page_clear_qsbr(page);
+ return page; // fast path
+ }
+ }
+ return mi_page_queue_find_free_ex(heap, pq, true);
+}
+
+
+/* -----------------------------------------------------------
+ Users can register a deferred free function called
+ when the `free` list is empty. Since the `local_free`
+ is separate this is deterministically called after
+ a certain number of allocations.
+----------------------------------------------------------- */
+
+static mi_deferred_free_fun* volatile deferred_free = NULL;
+static _Atomic(void*) deferred_arg; // = NULL
+
+void _mi_deferred_free(mi_heap_t* heap, bool force) {
+ heap->tld->heartbeat++;
+ if (deferred_free != NULL && !heap->tld->recurse) {
+ heap->tld->recurse = true;
+ deferred_free(force, heap->tld->heartbeat, mi_atomic_load_ptr_relaxed(void,&deferred_arg));
+ heap->tld->recurse = false;
+ }
+}
+
+void mi_register_deferred_free(mi_deferred_free_fun* fn, void* arg) mi_attr_noexcept {
+ deferred_free = fn;
+ mi_atomic_store_ptr_release(void,&deferred_arg, arg);
+}
+
+
+/* -----------------------------------------------------------
+ General allocation
+----------------------------------------------------------- */
+
+// Large and huge page allocation.
+// Huge pages are allocated directly without being in a queue.
+// Because huge pages contain just one block, and the segment contains
+// just that page, we always treat them as abandoned and any thread
+// that frees the block can free the whole page and segment directly.
+// Huge pages are also use if the requested alignment is very large (> MI_ALIGNMENT_MAX).
+static mi_page_t* mi_large_huge_page_alloc(mi_heap_t* heap, size_t size, size_t page_alignment) {
+ size_t block_size = _mi_os_good_alloc_size(size);
+ mi_assert_internal(mi_bin(block_size) == MI_BIN_HUGE || page_alignment > 0);
+ bool is_huge = (block_size > MI_LARGE_OBJ_SIZE_MAX || page_alignment > 0);
+ #if MI_HUGE_PAGE_ABANDON
+ mi_page_queue_t* pq = (is_huge ? NULL : mi_page_queue(heap, block_size));
+ #else
+ mi_page_queue_t* pq = mi_page_queue(heap, is_huge ? MI_HUGE_BLOCK_SIZE : block_size); // not block_size as that can be low if the page_alignment > 0
+ mi_assert_internal(!is_huge || mi_page_queue_is_huge(pq));
+ #endif
+ mi_page_t* page = mi_page_fresh_alloc(heap, pq, block_size, page_alignment);
+ if (page != NULL) {
+ mi_assert_internal(mi_page_immediate_available(page));
+
+ if (is_huge) {
+ mi_assert_internal(_mi_page_segment(page)->kind == MI_SEGMENT_HUGE);
+ mi_assert_internal(_mi_page_segment(page)->used==1);
+ #if MI_HUGE_PAGE_ABANDON
+ mi_assert_internal(_mi_page_segment(page)->thread_id==0); // abandoned, not in the huge queue
+ mi_page_set_heap(page, NULL);
+ #endif
+ }
+ else {
+ mi_assert_internal(_mi_page_segment(page)->kind != MI_SEGMENT_HUGE);
+ }
+
+ const size_t bsize = mi_page_usable_block_size(page); // note: not `mi_page_block_size` to account for padding
+ if (bsize <= MI_LARGE_OBJ_SIZE_MAX) {
+ mi_heap_stat_increase(heap, large, bsize);
+ mi_heap_stat_counter_increase(heap, large_count, 1);
+ }
+ else {
+ mi_heap_stat_increase(heap, huge, bsize);
+ mi_heap_stat_counter_increase(heap, huge_count, 1);
+ }
+ }
+ return page;
+}
+
+
+// Allocate a page
+// Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed.
+static mi_page_t* mi_find_page(mi_heap_t* heap, size_t size, size_t huge_alignment) mi_attr_noexcept {
+ // huge allocation?
+ const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size`
+ if mi_unlikely(req_size > (MI_MEDIUM_OBJ_SIZE_MAX - MI_PADDING_SIZE) || huge_alignment > 0) {
+ if mi_unlikely(req_size > PTRDIFF_MAX) { // we don't allocate more than PTRDIFF_MAX (see <https://sourceware.org/ml/libc-announce/2019/msg00001.html>)
+ _mi_error_message(EOVERFLOW, "allocation request is too large (%zu bytes)\n", req_size);
+ return NULL;
+ }
+ else {
+ _PyMem_mi_heap_collect_qsbr(heap);
+ return mi_large_huge_page_alloc(heap,size,huge_alignment);
+ }
+ }
+ else {
+ // otherwise find a page with free blocks in our size segregated queues
+ #if MI_PADDING
+ mi_assert_internal(size >= MI_PADDING_SIZE);
+ #endif
+ return mi_find_free_page(heap, size);
+ }
+}
+
+// Generic allocation routine if the fast path (`alloc.c:mi_page_malloc`) does not succeed.
+// Note: in debug mode the size includes MI_PADDING_SIZE and might have overflowed.
+// The `huge_alignment` is normally 0 but is set to a multiple of MI_SEGMENT_SIZE for
+// very large requested alignments in which case we use a huge segment.
+void* _mi_malloc_generic(mi_heap_t* heap, size_t size, bool zero, size_t huge_alignment) mi_attr_noexcept
+{
+ mi_assert_internal(heap != NULL);
+
+ // initialize if necessary
+ if mi_unlikely(!mi_heap_is_initialized(heap)) {
+ heap = mi_heap_get_default(); // calls mi_thread_init
+ if mi_unlikely(!mi_heap_is_initialized(heap)) { return NULL; }
+ }
+ mi_assert_internal(mi_heap_is_initialized(heap));
+
+ // call potential deferred free routines
+ _mi_deferred_free(heap, false);
+
+ // free delayed frees from other threads (but skip contended ones)
+ _mi_heap_delayed_free_partial(heap);
+
+ // find (or allocate) a page of the right size
+ mi_page_t* page = mi_find_page(heap, size, huge_alignment);
+ if mi_unlikely(page == NULL) { // first time out of memory, try to collect and retry the allocation once more
+ mi_heap_collect(heap, true /* force */);
+ page = mi_find_page(heap, size, huge_alignment);
+ }
+
+ if mi_unlikely(page == NULL) { // out of memory
+ const size_t req_size = size - MI_PADDING_SIZE; // correct for padding_size in case of an overflow on `size`
+ _mi_error_message(ENOMEM, "unable to allocate memory (%zu bytes)\n", req_size);
+ return NULL;
+ }
+
+ mi_assert_internal(mi_page_immediate_available(page));
+ mi_assert_internal(mi_page_block_size(page) >= size);
+
+ // and try again, this time succeeding! (i.e. this should never recurse through _mi_page_malloc)
+ if mi_unlikely(zero && page->xblock_size == 0) {
+ // note: we cannot call _mi_page_malloc with zeroing for huge blocks; we zero it afterwards in that case.
+ void* p = _mi_page_malloc(heap, page, size, false);
+ mi_assert_internal(p != NULL);
+ _mi_memzero_aligned(p, mi_page_usable_block_size(page));
+ return p;
+ }
+ else {
+ return _mi_page_malloc(heap, page, size, zero);
+ }
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/prim/osx/alloc-override-zone.c b/contrib/tools/python3/Objects/mimalloc/prim/osx/alloc-override-zone.c
new file mode 100644
index 00000000000..0e0a99d9388
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/prim/osx/alloc-override-zone.c
@@ -0,0 +1,458 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2022, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+
+#if defined(MI_MALLOC_OVERRIDE)
+
+#if !defined(__APPLE__)
+#error "this file should only be included on macOS"
+#endif
+
+/* ------------------------------------------------------
+ Override system malloc on macOS
+ This is done through the malloc zone interface.
+ It seems to be most robust in combination with interposing
+ though or otherwise we may get zone errors as there are could
+ be allocations done by the time we take over the
+ zone.
+------------------------------------------------------ */
+
+#include <AvailabilityMacros.h>
+#include <malloc/malloc.h>
+#include <string.h> // memset
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
+// only available from OSX 10.6
+extern malloc_zone_t* malloc_default_purgeable_zone(void) __attribute__((weak_import));
+#endif
+
+/* ------------------------------------------------------
+ malloc zone members
+------------------------------------------------------ */
+
+static size_t zone_size(malloc_zone_t* zone, const void* p) {
+ MI_UNUSED(zone);
+ if (!mi_is_in_heap_region(p)){ return 0; } // not our pointer, bail out
+ return mi_usable_size(p);
+}
+
+static void* zone_malloc(malloc_zone_t* zone, size_t size) {
+ MI_UNUSED(zone);
+ return mi_malloc(size);
+}
+
+static void* zone_calloc(malloc_zone_t* zone, size_t count, size_t size) {
+ MI_UNUSED(zone);
+ return mi_calloc(count, size);
+}
+
+static void* zone_valloc(malloc_zone_t* zone, size_t size) {
+ MI_UNUSED(zone);
+ return mi_malloc_aligned(size, _mi_os_page_size());
+}
+
+static void zone_free(malloc_zone_t* zone, void* p) {
+ MI_UNUSED(zone);
+ mi_cfree(p);
+}
+
+static void* zone_realloc(malloc_zone_t* zone, void* p, size_t newsize) {
+ MI_UNUSED(zone);
+ return mi_realloc(p, newsize);
+}
+
+static void* zone_memalign(malloc_zone_t* zone, size_t alignment, size_t size) {
+ MI_UNUSED(zone);
+ return mi_malloc_aligned(size,alignment);
+}
+
+static void zone_destroy(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+ // todo: ignore for now?
+}
+
+static unsigned zone_batch_malloc(malloc_zone_t* zone, size_t size, void** ps, unsigned count) {
+ size_t i;
+ for (i = 0; i < count; i++) {
+ ps[i] = zone_malloc(zone, size);
+ if (ps[i] == NULL) break;
+ }
+ return i;
+}
+
+static void zone_batch_free(malloc_zone_t* zone, void** ps, unsigned count) {
+ for(size_t i = 0; i < count; i++) {
+ zone_free(zone, ps[i]);
+ ps[i] = NULL;
+ }
+}
+
+static size_t zone_pressure_relief(malloc_zone_t* zone, size_t size) {
+ MI_UNUSED(zone); MI_UNUSED(size);
+ mi_collect(false);
+ return 0;
+}
+
+static void zone_free_definite_size(malloc_zone_t* zone, void* p, size_t size) {
+ MI_UNUSED(size);
+ zone_free(zone,p);
+}
+
+static boolean_t zone_claimed_address(malloc_zone_t* zone, void* p) {
+ MI_UNUSED(zone);
+ return mi_is_in_heap_region(p);
+}
+
+
+/* ------------------------------------------------------
+ Introspection members
+------------------------------------------------------ */
+
+static kern_return_t intro_enumerator(task_t task, void* p,
+ unsigned type_mask, vm_address_t zone_address,
+ memory_reader_t reader,
+ vm_range_recorder_t recorder)
+{
+ // todo: enumerate all memory
+ MI_UNUSED(task); MI_UNUSED(p); MI_UNUSED(type_mask); MI_UNUSED(zone_address);
+ MI_UNUSED(reader); MI_UNUSED(recorder);
+ return KERN_SUCCESS;
+}
+
+static size_t intro_good_size(malloc_zone_t* zone, size_t size) {
+ MI_UNUSED(zone);
+ return mi_good_size(size);
+}
+
+static boolean_t intro_check(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+ return true;
+}
+
+static void intro_print(malloc_zone_t* zone, boolean_t verbose) {
+ MI_UNUSED(zone); MI_UNUSED(verbose);
+ mi_stats_print(NULL);
+}
+
+static void intro_log(malloc_zone_t* zone, void* p) {
+ MI_UNUSED(zone); MI_UNUSED(p);
+ // todo?
+}
+
+static void intro_force_lock(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+ // todo?
+}
+
+static void intro_force_unlock(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+ // todo?
+}
+
+static void intro_statistics(malloc_zone_t* zone, malloc_statistics_t* stats) {
+ MI_UNUSED(zone);
+ // todo...
+ stats->blocks_in_use = 0;
+ stats->size_in_use = 0;
+ stats->max_size_in_use = 0;
+ stats->size_allocated = 0;
+}
+
+static boolean_t intro_zone_locked(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+ return false;
+}
+
+
+/* ------------------------------------------------------
+ At process start, override the default allocator
+------------------------------------------------------ */
+
+#if defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wc99-extensions"
+#endif
+
+static malloc_introspection_t mi_introspect = {
+ .enumerator = &intro_enumerator,
+ .good_size = &intro_good_size,
+ .check = &intro_check,
+ .print = &intro_print,
+ .log = &intro_log,
+ .force_lock = &intro_force_lock,
+ .force_unlock = &intro_force_unlock,
+#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) && !defined(__ppc__)
+ .statistics = &intro_statistics,
+ .zone_locked = &intro_zone_locked,
+#endif
+};
+
+static malloc_zone_t mi_malloc_zone = {
+ // note: even with designators, the order is important for C++ compilation
+ //.reserved1 = NULL,
+ //.reserved2 = NULL,
+ .size = &zone_size,
+ .malloc = &zone_malloc,
+ .calloc = &zone_calloc,
+ .valloc = &zone_valloc,
+ .free = &zone_free,
+ .realloc = &zone_realloc,
+ .destroy = &zone_destroy,
+ .zone_name = "mimalloc",
+ .batch_malloc = &zone_batch_malloc,
+ .batch_free = &zone_batch_free,
+ .introspect = &mi_introspect,
+#if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) && !defined(__ppc__)
+ #if defined(MAC_OS_X_VERSION_10_14) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14)
+ .version = 10,
+ #else
+ .version = 9,
+ #endif
+ // switch to version 9+ on OSX 10.6 to support memalign.
+ .memalign = &zone_memalign,
+ .free_definite_size = &zone_free_definite_size,
+ .pressure_relief = &zone_pressure_relief,
+ #if defined(MAC_OS_X_VERSION_10_14) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_14)
+ .claimed_address = &zone_claimed_address,
+ #endif
+#else
+ .version = 4,
+#endif
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#if defined(MI_OSX_INTERPOSE) && defined(MI_SHARED_LIB_EXPORT)
+
+// ------------------------------------------------------
+// Override malloc_xxx and malloc_zone_xxx api's to use only
+// our mimalloc zone. Since even the loader uses malloc
+// on macOS, this ensures that all allocations go through
+// mimalloc (as all calls are interposed).
+// The main `malloc`, `free`, etc calls are interposed in `alloc-override.c`,
+// Here, we also override macOS specific API's like
+// `malloc_zone_calloc` etc. see <https://github.com/aosm/libmalloc/blob/master/man/malloc_zone_malloc.3>
+// ------------------------------------------------------
+
+static inline malloc_zone_t* mi_get_default_zone(void)
+{
+ static bool init;
+ if mi_unlikely(!init) {
+ init = true;
+ malloc_zone_register(&mi_malloc_zone); // by calling register we avoid a zone error on free (see <http://eatmyrandom.blogspot.com/2010/03/mallocfree-interception-on-mac-os-x.html>)
+ }
+ return &mi_malloc_zone;
+}
+
+mi_decl_externc int malloc_jumpstart(uintptr_t cookie);
+mi_decl_externc void _malloc_fork_prepare(void);
+mi_decl_externc void _malloc_fork_parent(void);
+mi_decl_externc void _malloc_fork_child(void);
+
+
+static malloc_zone_t* mi_malloc_create_zone(vm_size_t size, unsigned flags) {
+ MI_UNUSED(size); MI_UNUSED(flags);
+ return mi_get_default_zone();
+}
+
+static malloc_zone_t* mi_malloc_default_zone (void) {
+ return mi_get_default_zone();
+}
+
+static malloc_zone_t* mi_malloc_default_purgeable_zone(void) {
+ return mi_get_default_zone();
+}
+
+static void mi_malloc_destroy_zone(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+ // nothing.
+}
+
+static kern_return_t mi_malloc_get_all_zones (task_t task, memory_reader_t mr, vm_address_t** addresses, unsigned* count) {
+ MI_UNUSED(task); MI_UNUSED(mr);
+ if (addresses != NULL) *addresses = NULL;
+ if (count != NULL) *count = 0;
+ return KERN_SUCCESS;
+}
+
+static const char* mi_malloc_get_zone_name(malloc_zone_t* zone) {
+ return (zone == NULL ? mi_malloc_zone.zone_name : zone->zone_name);
+}
+
+static void mi_malloc_set_zone_name(malloc_zone_t* zone, const char* name) {
+ MI_UNUSED(zone); MI_UNUSED(name);
+}
+
+static int mi_malloc_jumpstart(uintptr_t cookie) {
+ MI_UNUSED(cookie);
+ return 1; // or 0 for no error?
+}
+
+static void mi__malloc_fork_prepare(void) {
+ // nothing
+}
+static void mi__malloc_fork_parent(void) {
+ // nothing
+}
+static void mi__malloc_fork_child(void) {
+ // nothing
+}
+
+static void mi_malloc_printf(const char* fmt, ...) {
+ MI_UNUSED(fmt);
+}
+
+static bool zone_check(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+ return true;
+}
+
+static malloc_zone_t* zone_from_ptr(const void* p) {
+ MI_UNUSED(p);
+ return mi_get_default_zone();
+}
+
+static void zone_log(malloc_zone_t* zone, void* p) {
+ MI_UNUSED(zone); MI_UNUSED(p);
+}
+
+static void zone_print(malloc_zone_t* zone, bool b) {
+ MI_UNUSED(zone); MI_UNUSED(b);
+}
+
+static void zone_print_ptr_info(void* p) {
+ MI_UNUSED(p);
+}
+
+static void zone_register(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+}
+
+static void zone_unregister(malloc_zone_t* zone) {
+ MI_UNUSED(zone);
+}
+
+// use interposing so `DYLD_INSERT_LIBRARIES` works without `DYLD_FORCE_FLAT_NAMESPACE=1`
+// See: <https://books.google.com/books?id=K8vUkpOXhN4C&pg=PA73>
+struct mi_interpose_s {
+ const void* replacement;
+ const void* target;
+};
+#define MI_INTERPOSE_FUN(oldfun,newfun) { (const void*)&newfun, (const void*)&oldfun }
+#define MI_INTERPOSE_MI(fun) MI_INTERPOSE_FUN(fun,mi_##fun)
+#define MI_INTERPOSE_ZONE(fun) MI_INTERPOSE_FUN(malloc_##fun,fun)
+__attribute__((used)) static const struct mi_interpose_s _mi_zone_interposes[] __attribute__((section("__DATA, __interpose"))) =
+{
+
+ MI_INTERPOSE_MI(malloc_create_zone),
+ MI_INTERPOSE_MI(malloc_default_purgeable_zone),
+ MI_INTERPOSE_MI(malloc_default_zone),
+ MI_INTERPOSE_MI(malloc_destroy_zone),
+ MI_INTERPOSE_MI(malloc_get_all_zones),
+ MI_INTERPOSE_MI(malloc_get_zone_name),
+ MI_INTERPOSE_MI(malloc_jumpstart),
+ MI_INTERPOSE_MI(malloc_printf),
+ MI_INTERPOSE_MI(malloc_set_zone_name),
+ MI_INTERPOSE_MI(_malloc_fork_child),
+ MI_INTERPOSE_MI(_malloc_fork_parent),
+ MI_INTERPOSE_MI(_malloc_fork_prepare),
+
+ MI_INTERPOSE_ZONE(zone_batch_free),
+ MI_INTERPOSE_ZONE(zone_batch_malloc),
+ MI_INTERPOSE_ZONE(zone_calloc),
+ MI_INTERPOSE_ZONE(zone_check),
+ MI_INTERPOSE_ZONE(zone_free),
+ MI_INTERPOSE_ZONE(zone_from_ptr),
+ MI_INTERPOSE_ZONE(zone_log),
+ MI_INTERPOSE_ZONE(zone_malloc),
+ MI_INTERPOSE_ZONE(zone_memalign),
+ MI_INTERPOSE_ZONE(zone_print),
+ MI_INTERPOSE_ZONE(zone_print_ptr_info),
+ MI_INTERPOSE_ZONE(zone_realloc),
+ MI_INTERPOSE_ZONE(zone_register),
+ MI_INTERPOSE_ZONE(zone_unregister),
+ MI_INTERPOSE_ZONE(zone_valloc)
+};
+
+
+#else
+
+// ------------------------------------------------------
+// hook into the zone api's without interposing
+// This is the official way of adding an allocator but
+// it seems less robust than using interpose.
+// ------------------------------------------------------
+
+static inline malloc_zone_t* mi_get_default_zone(void)
+{
+ // The first returned zone is the real default
+ malloc_zone_t** zones = NULL;
+ unsigned count = 0;
+ kern_return_t ret = malloc_get_all_zones(0, NULL, (vm_address_t**)&zones, &count);
+ if (ret == KERN_SUCCESS && count > 0) {
+ return zones[0];
+ }
+ else {
+ // fallback
+ return malloc_default_zone();
+ }
+}
+
+#if defined(__clang__)
+__attribute__((constructor(0)))
+#else
+__attribute__((constructor)) // seems not supported by g++-11 on the M1
+#endif
+static void _mi_macos_override_malloc(void) {
+ malloc_zone_t* purgeable_zone = NULL;
+
+ #if defined(MAC_OS_X_VERSION_10_6) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6)
+ // force the purgeable zone to exist to avoid strange bugs
+ if (malloc_default_purgeable_zone) {
+ purgeable_zone = malloc_default_purgeable_zone();
+ }
+ #endif
+
+ // Register our zone.
+ // thomcc: I think this is still needed to put us in the zone list.
+ malloc_zone_register(&mi_malloc_zone);
+ // Unregister the default zone, this makes our zone the new default
+ // as that was the last registered.
+ malloc_zone_t *default_zone = mi_get_default_zone();
+ // thomcc: Unsure if the next test is *always* false or just false in the
+ // cases I've tried. I'm also unsure if the code inside is needed. at all
+ if (default_zone != &mi_malloc_zone) {
+ malloc_zone_unregister(default_zone);
+
+ // Reregister the default zone so free and realloc in that zone keep working.
+ malloc_zone_register(default_zone);
+ }
+
+ // Unregister, and re-register the purgeable_zone to avoid bugs if it occurs
+ // earlier than the default zone.
+ if (purgeable_zone != NULL) {
+ malloc_zone_unregister(purgeable_zone);
+ malloc_zone_register(purgeable_zone);
+ }
+
+}
+#endif // MI_OSX_INTERPOSE
+
+#endif // MI_MALLOC_OVERRIDE
diff --git a/contrib/tools/python3/Objects/mimalloc/prim/osx/prim.c b/contrib/tools/python3/Objects/mimalloc/prim/osx/prim.c
new file mode 100644
index 00000000000..8a2f4e8aa47
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/prim/osx/prim.c
@@ -0,0 +1,9 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2023, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+// We use the unix/prim.c with the mmap API on macOSX
+#include "../unix/prim.c"
diff --git a/contrib/tools/python3/Objects/mimalloc/prim/prim.c b/contrib/tools/python3/Objects/mimalloc/prim/prim.c
new file mode 100644
index 00000000000..9a597d8eb60
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/prim/prim.c
@@ -0,0 +1,24 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2023, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+// Select the implementation of the primitives
+// depending on the OS.
+
+#if defined(_WIN32)
+#include "windows/prim.c" // VirtualAlloc (Windows)
+
+#elif defined(__APPLE__)
+#include "osx/prim.c" // macOSX (actually defers to mmap in unix/prim.c)
+
+#elif defined(__wasi__)
+#define MI_USE_SBRK
+#include "wasi/prim.c" // memory-grow or sbrk (Wasm)
+
+#else
+#include "unix/prim.c" // mmap() (Linux, macOSX, BSD, Illumnos, Haiku, DragonFly, etc.)
+
+#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/prim/unix/prim.c b/contrib/tools/python3/Objects/mimalloc/prim/unix/prim.c
new file mode 100644
index 00000000000..60fe601be9b
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/prim/unix/prim.c
@@ -0,0 +1,860 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2023, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+// This file is included in `src/prim/prim.c`
+
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE // ensure mmap flags and syscall are defined
+#endif
+
+#if defined(__sun)
+// illumos provides new mman.h api when any of these are defined
+// otherwise the old api based on caddr_t which predates the void pointers one.
+// stock solaris provides only the former, chose to atomically to discard those
+// flags only here rather than project wide tough.
+#undef _XOPEN_SOURCE
+#undef _POSIX_C_SOURCE
+#endif
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+#include "mimalloc/prim.h"
+
+#include <sys/mman.h> // mmap
+#include <unistd.h> // sysconf
+#include <fcntl.h> // open, close, read, access
+
+#if defined(__linux__)
+ #include <features.h>
+ #include <fcntl.h>
+ #if defined(__GLIBC__)
+ #include <linux/mman.h> // linux mmap flags
+ #else
+ #include <sys/mman.h>
+ #endif
+#elif defined(__APPLE__)
+ #include <TargetConditionals.h>
+ #if !TARGET_IOS_IPHONE && !TARGET_IOS_SIMULATOR
+ #include <mach/vm_statistics.h>
+ #endif
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
+ #include <sys/param.h>
+ #if __FreeBSD_version >= 1200000
+ #include <sys/cpuset.h>
+ #error #include <sys/domainset.h>
+ #endif
+ #include <sys/sysctl.h>
+#endif
+
+#if !defined(__HAIKU__) && !defined(__APPLE__) && !defined(__CYGWIN__) && !defined(_AIX) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(__NetBSD__)
+ #define MI_HAS_SYSCALL_H
+ #include <sys/syscall.h>
+#endif
+
+//------------------------------------------------------------------------------------
+// Use syscalls for some primitives to allow for libraries that override open/read/close etc.
+// and do allocation themselves; using syscalls prevents recursion when mimalloc is
+// still initializing (issue #713)
+//------------------------------------------------------------------------------------
+
+#if defined(MI_HAS_SYSCALL_H) && defined(SYS_open) && defined(SYS_close) && defined(SYS_read) && defined(SYS_access)
+
+static int mi_prim_open(const char* fpath, int open_flags) {
+ return syscall(SYS_open,fpath,open_flags,0);
+}
+static ssize_t mi_prim_read(int fd, void* buf, size_t bufsize) {
+ return syscall(SYS_read,fd,buf,bufsize);
+}
+static int mi_prim_close(int fd) {
+ return syscall(SYS_close,fd);
+}
+static int mi_prim_access(const char *fpath, int mode) {
+ return syscall(SYS_access,fpath,mode);
+}
+
+#elif !defined(__APPLE__) && !defined(_AIX) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__sun) && !defined(__NetBSD__) // avoid unused warnings
+
+static int mi_prim_open(const char* fpath, int open_flags) {
+ return open(fpath,open_flags);
+}
+static ssize_t mi_prim_read(int fd, void* buf, size_t bufsize) {
+ return read(fd,buf,bufsize);
+}
+static int mi_prim_close(int fd) {
+ return close(fd);
+}
+static int mi_prim_access(const char *fpath, int mode) {
+ return access(fpath,mode);
+}
+
+#endif
+
+
+
+//---------------------------------------------
+// init
+//---------------------------------------------
+
+static bool unix_detect_overcommit(void) {
+ bool os_overcommit = true;
+#if defined(__linux__)
+ int fd = mi_prim_open("/proc/sys/vm/overcommit_memory", O_RDONLY);
+ if (fd >= 0) {
+ char buf[32] = {0};
+ ssize_t nread = mi_prim_read(fd, &buf, sizeof(buf));
+ mi_prim_close(fd);
+ // <https://www.kernel.org/doc/Documentation/vm/overcommit-accounting>
+ // 0: heuristic overcommit, 1: always overcommit, 2: never overcommit (ignore NORESERVE)
+ if (nread >= 1) {
+ os_overcommit = (buf[0] == '0' || buf[0] == '1');
+ }
+ }
+#elif defined(__FreeBSD__)
+ int val = 0;
+ size_t olen = sizeof(val);
+ if (sysctlbyname("vm.overcommit", &val, &olen, NULL, 0) == 0) {
+ os_overcommit = (val != 0);
+ }
+#else
+ // default: overcommit is true
+#endif
+ return os_overcommit;
+}
+
+void _mi_prim_mem_init( mi_os_mem_config_t* config ) {
+ long psize = sysconf(_SC_PAGESIZE);
+ if (psize > 0) {
+ config->page_size = (size_t)psize;
+ config->alloc_granularity = (size_t)psize;
+ }
+ config->large_page_size = 2*MI_MiB; // TODO: can we query the OS for this?
+ config->has_overcommit = unix_detect_overcommit();
+ config->must_free_whole = false; // mmap can free in parts
+ config->has_virtual_reserve = true; // todo: check if this true for NetBSD? (for anonymous mmap with PROT_NONE)
+}
+
+
+//---------------------------------------------
+// free
+//---------------------------------------------
+
+int _mi_prim_free(void* addr, size_t size ) {
+ bool err = (munmap(addr, size) == -1);
+ return (err ? errno : 0);
+}
+
+
+//---------------------------------------------
+// mmap
+//---------------------------------------------
+
+static int unix_madvise(void* addr, size_t size, int advice) {
+ #if defined(__sun)
+ return madvise((caddr_t)addr, size, advice); // Solaris needs cast (issue #520)
+ #else
+ return madvise(addr, size, advice);
+ #endif
+}
+
+static void* unix_mmap_prim(void* addr, size_t size, size_t try_alignment, int protect_flags, int flags, int fd) {
+ MI_UNUSED(try_alignment);
+ void* p = NULL;
+ #if defined(MAP_ALIGNED) // BSD
+ if (addr == NULL && try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0) {
+ size_t n = mi_bsr(try_alignment);
+ if (((size_t)1 << n) == try_alignment && n >= 12 && n <= 30) { // alignment is a power of 2 and 4096 <= alignment <= 1GiB
+ p = mmap(addr, size, protect_flags, flags | MAP_ALIGNED(n), fd, 0);
+ if (p==MAP_FAILED || !_mi_is_aligned(p,try_alignment)) {
+ int err = errno;
+ _mi_verbose_message("unable to directly request aligned OS memory (error: %d (0x%x), size: 0x%zx bytes, alignment: 0x%zx, hint address: %p)\n", err, err, size, try_alignment, addr);
+ }
+ if (p!=MAP_FAILED) return p;
+ // fall back to regular mmap
+ }
+ }
+ #elif defined(MAP_ALIGN) // Solaris
+ if (addr == NULL && try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0) {
+ p = mmap((void*)try_alignment, size, protect_flags, flags | MAP_ALIGN, fd, 0); // addr parameter is the required alignment
+ if (p!=MAP_FAILED) return p;
+ // fall back to regular mmap
+ }
+ #endif
+ #if (MI_INTPTR_SIZE >= 8) && !defined(MAP_ALIGNED)
+ // on 64-bit systems, use the virtual address area after 2TiB for 4MiB aligned allocations
+ if (addr == NULL) {
+ void* hint = _mi_os_get_aligned_hint(try_alignment, size);
+ if (hint != NULL) {
+ p = mmap(hint, size, protect_flags, flags, fd, 0);
+ if (p==MAP_FAILED || !_mi_is_aligned(p,try_alignment)) {
+ #if MI_TRACK_ENABLED // asan sometimes does not instrument errno correctly?
+ int err = 0;
+ #else
+ int err = errno;
+ #endif
+ _mi_verbose_message("unable to directly request hinted aligned OS memory (error: %d (0x%x), size: 0x%zx bytes, alignment: 0x%zx, hint address: %p)\n", err, err, size, try_alignment, hint);
+ }
+ if (p!=MAP_FAILED) return p;
+ // fall back to regular mmap
+ }
+ }
+ #endif
+ // regular mmap
+ p = mmap(addr, size, protect_flags, flags, fd, 0);
+ if (p!=MAP_FAILED) return p;
+ // failed to allocate
+ return NULL;
+}
+
+static int unix_mmap_fd(void) {
+ #if defined(VM_MAKE_TAG)
+ // macOS: tracking anonymous page with a specific ID. (All up to 98 are taken officially but LLVM sanitizers had taken 99)
+ int os_tag = (int)mi_option_get(mi_option_os_tag);
+ if (os_tag < 100 || os_tag > 255) { os_tag = 100; }
+ return VM_MAKE_TAG(os_tag);
+ #else
+ return -1;
+ #endif
+}
+
+static void* unix_mmap(void* addr, size_t size, size_t try_alignment, int protect_flags, bool large_only, bool allow_large, bool* is_large) {
+ #if !defined(MAP_ANONYMOUS)
+ #define MAP_ANONYMOUS MAP_ANON
+ #endif
+ #if !defined(MAP_NORESERVE)
+ #define MAP_NORESERVE 0
+ #endif
+ void* p = NULL;
+ const int fd = unix_mmap_fd();
+ int flags = MAP_PRIVATE | MAP_ANONYMOUS;
+ if (_mi_os_has_overcommit()) {
+ flags |= MAP_NORESERVE;
+ }
+ #if defined(PROT_MAX)
+ protect_flags |= PROT_MAX(PROT_READ | PROT_WRITE); // BSD
+ #endif
+ // huge page allocation
+ if ((large_only || _mi_os_use_large_page(size, try_alignment)) && allow_large) {
+ static _Atomic(size_t) large_page_try_ok; // = 0;
+ size_t try_ok = mi_atomic_load_acquire(&large_page_try_ok);
+ if (!large_only && try_ok > 0) {
+ // If the OS is not configured for large OS pages, or the user does not have
+ // enough permission, the `mmap` will always fail (but it might also fail for other reasons).
+ // Therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times
+ // to avoid too many failing calls to mmap.
+ mi_atomic_cas_strong_acq_rel(&large_page_try_ok, &try_ok, try_ok - 1);
+ }
+ else {
+ int lflags = flags & ~MAP_NORESERVE; // using NORESERVE on huge pages seems to fail on Linux
+ int lfd = fd;
+ #ifdef MAP_ALIGNED_SUPER
+ lflags |= MAP_ALIGNED_SUPER;
+ #endif
+ #ifdef MAP_HUGETLB
+ lflags |= MAP_HUGETLB;
+ #endif
+ #ifdef MAP_HUGE_1GB
+ static bool mi_huge_pages_available = true;
+ if ((size % MI_GiB) == 0 && mi_huge_pages_available) {
+ lflags |= MAP_HUGE_1GB;
+ }
+ else
+ #endif
+ {
+ #ifdef MAP_HUGE_2MB
+ lflags |= MAP_HUGE_2MB;
+ #endif
+ }
+ #ifdef VM_FLAGS_SUPERPAGE_SIZE_2MB
+ lfd |= VM_FLAGS_SUPERPAGE_SIZE_2MB;
+ #endif
+ if (large_only || lflags != flags) {
+ // try large OS page allocation
+ *is_large = true;
+ p = unix_mmap_prim(addr, size, try_alignment, protect_flags, lflags, lfd);
+ #ifdef MAP_HUGE_1GB
+ if (p == NULL && (lflags & MAP_HUGE_1GB) != 0) {
+ mi_huge_pages_available = false; // don't try huge 1GiB pages again
+ _mi_warning_message("unable to allocate huge (1GiB) page, trying large (2MiB) pages instead (errno: %i)\n", errno);
+ lflags = ((lflags & ~MAP_HUGE_1GB) | MAP_HUGE_2MB);
+ p = unix_mmap_prim(addr, size, try_alignment, protect_flags, lflags, lfd);
+ }
+ #endif
+ if (large_only) return p;
+ if (p == NULL) {
+ mi_atomic_store_release(&large_page_try_ok, (size_t)8); // on error, don't try again for the next N allocations
+ }
+ }
+ }
+ }
+ // regular allocation
+ if (p == NULL) {
+ *is_large = false;
+ p = unix_mmap_prim(addr, size, try_alignment, protect_flags, flags, fd);
+ if (p != NULL) {
+ #if defined(MADV_HUGEPAGE)
+ // Many Linux systems don't allow MAP_HUGETLB but they support instead
+ // transparent huge pages (THP). Generally, it is not required to call `madvise` with MADV_HUGE
+ // though since properly aligned allocations will already use large pages if available
+ // in that case -- in particular for our large regions (in `memory.c`).
+ // However, some systems only allow THP if called with explicit `madvise`, so
+ // when large OS pages are enabled for mimalloc, we call `madvise` anyways.
+ if (allow_large && _mi_os_use_large_page(size, try_alignment)) {
+ if (unix_madvise(p, size, MADV_HUGEPAGE) == 0) {
+ *is_large = true; // possibly
+ };
+ }
+ #elif defined(__sun)
+ if (allow_large && _mi_os_use_large_page(size, try_alignment)) {
+ struct memcntl_mha cmd = {0};
+ cmd.mha_pagesize = 2*MI_MiB;
+ cmd.mha_cmd = MHA_MAPSIZE_VA;
+ if (memcntl((caddr_t)p, size, MC_HAT_ADVISE, (caddr_t)&cmd, 0, 0) == 0) {
+ *is_large = true;
+ }
+ }
+ #endif
+ }
+ }
+ return p;
+}
+
+// Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned.
+int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) {
+ mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
+ mi_assert_internal(commit || !allow_large);
+ mi_assert_internal(try_alignment > 0);
+
+ *is_zero = true;
+ int protect_flags = (commit ? (PROT_WRITE | PROT_READ) : PROT_NONE);
+ *addr = unix_mmap(NULL, size, try_alignment, protect_flags, false, allow_large, is_large);
+ return (*addr != NULL ? 0 : errno);
+}
+
+
+//---------------------------------------------
+// Commit/Reset
+//---------------------------------------------
+
+static void unix_mprotect_hint(int err) {
+ #if defined(__linux__) && (MI_SECURE>=2) // guard page around every mimalloc page
+ if (err == ENOMEM) {
+ _mi_warning_message("The next warning may be caused by a low memory map limit.\n"
+ " On Linux this is controlled by the vm.max_map_count -- maybe increase it?\n"
+ " For example: sudo sysctl -w vm.max_map_count=262144\n");
+ }
+ #else
+ MI_UNUSED(err);
+ #endif
+}
+
+int _mi_prim_commit(void* start, size_t size, bool* is_zero) {
+ // commit: ensure we can access the area
+ // note: we may think that *is_zero can be true since the memory
+ // was either from mmap PROT_NONE, or from decommit MADV_DONTNEED, but
+ // we sometimes call commit on a range with still partially committed
+ // memory and `mprotect` does not zero the range.
+ *is_zero = false;
+ int err = mprotect(start, size, (PROT_READ | PROT_WRITE));
+ if (err != 0) {
+ err = errno;
+ unix_mprotect_hint(err);
+ }
+ return err;
+}
+
+int _mi_prim_decommit(void* start, size_t size, bool* needs_recommit) {
+ int err = 0;
+ // decommit: use MADV_DONTNEED as it decreases rss immediately (unlike MADV_FREE)
+ err = unix_madvise(start, size, MADV_DONTNEED);
+ #if !MI_DEBUG && !MI_SECURE
+ *needs_recommit = false;
+ #else
+ *needs_recommit = true;
+ mprotect(start, size, PROT_NONE);
+ #endif
+ /*
+ // decommit: use mmap with MAP_FIXED and PROT_NONE to discard the existing memory (and reduce rss)
+ *needs_recommit = true;
+ const int fd = unix_mmap_fd();
+ void* p = mmap(start, size, PROT_NONE, (MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE), fd, 0);
+ if (p != start) { err = errno; }
+ */
+ return err;
+}
+
+int _mi_prim_reset(void* start, size_t size) {
+ // We try to use `MADV_FREE` as that is the fastest. A drawback though is that it
+ // will not reduce the `rss` stats in tools like `top` even though the memory is available
+ // to other processes. With the default `MIMALLOC_PURGE_DECOMMITS=1` we ensure that by
+ // default `MADV_DONTNEED` is used though.
+ #if defined(MADV_FREE)
+ static _Atomic(size_t) advice = MI_ATOMIC_VAR_INIT(MADV_FREE);
+ int oadvice = (int)mi_atomic_load_relaxed(&advice);
+ int err;
+ while ((err = unix_madvise(start, size, oadvice)) != 0 && errno == EAGAIN) { errno = 0; };
+ if (err != 0 && errno == EINVAL && oadvice == MADV_FREE) {
+ // if MADV_FREE is not supported, fall back to MADV_DONTNEED from now on
+ mi_atomic_store_release(&advice, (size_t)MADV_DONTNEED);
+ err = unix_madvise(start, size, MADV_DONTNEED);
+ }
+ #else
+ int err = unix_madvise(start, size, MADV_DONTNEED);
+ #endif
+ return err;
+}
+
+int _mi_prim_protect(void* start, size_t size, bool protect) {
+ int err = mprotect(start, size, protect ? PROT_NONE : (PROT_READ | PROT_WRITE));
+ if (err != 0) { err = errno; }
+ unix_mprotect_hint(err);
+ return err;
+}
+
+
+
+//---------------------------------------------
+// Huge page allocation
+//---------------------------------------------
+
+#if (MI_INTPTR_SIZE >= 8) && !defined(__HAIKU__) && !defined(__CYGWIN__)
+
+#ifndef MPOL_PREFERRED
+#define MPOL_PREFERRED 1
+#endif
+
+#if defined(MI_HAS_SYSCALL_H) && defined(SYS_mbind)
+static long mi_prim_mbind(void* start, unsigned long len, unsigned long mode, const unsigned long* nmask, unsigned long maxnode, unsigned flags) {
+ return syscall(SYS_mbind, start, len, mode, nmask, maxnode, flags);
+}
+#else
+static long mi_prim_mbind(void* start, unsigned long len, unsigned long mode, const unsigned long* nmask, unsigned long maxnode, unsigned flags) {
+ MI_UNUSED(start); MI_UNUSED(len); MI_UNUSED(mode); MI_UNUSED(nmask); MI_UNUSED(maxnode); MI_UNUSED(flags);
+ return 0;
+}
+#endif
+
+int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bool* is_zero, void** addr) {
+ bool is_large = true;
+ *is_zero = true;
+ *addr = unix_mmap(hint_addr, size, MI_SEGMENT_SIZE, PROT_READ | PROT_WRITE, true, true, &is_large);
+ if (*addr != NULL && numa_node >= 0 && numa_node < 8*MI_INTPTR_SIZE) { // at most 64 nodes
+ unsigned long numa_mask = (1UL << numa_node);
+ // TODO: does `mbind` work correctly for huge OS pages? should we
+ // use `set_mempolicy` before calling mmap instead?
+ // see: <https://lkml.org/lkml/2017/2/9/875>
+ long err = mi_prim_mbind(*addr, size, MPOL_PREFERRED, &numa_mask, 8*MI_INTPTR_SIZE, 0);
+ if (err != 0) {
+ err = errno;
+ _mi_warning_message("failed to bind huge (1GiB) pages to numa node %d (error: %d (0x%x))\n", numa_node, err, err);
+ }
+ }
+ return (*addr != NULL ? 0 : errno);
+}
+
+#else
+
+int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bool* is_zero, void** addr) {
+ MI_UNUSED(hint_addr); MI_UNUSED(size); MI_UNUSED(numa_node);
+ *is_zero = false;
+ *addr = NULL;
+ return ENOMEM;
+}
+
+#endif
+
+//---------------------------------------------
+// NUMA nodes
+//---------------------------------------------
+
+#if defined(__linux__)
+
+#include <stdio.h> // snprintf
+
+size_t _mi_prim_numa_node(void) {
+ #if defined(MI_HAS_SYSCALL_H) && defined(SYS_getcpu)
+ unsigned long node = 0;
+ unsigned long ncpu = 0;
+ long err = syscall(SYS_getcpu, &ncpu, &node, NULL);
+ if (err != 0) return 0;
+ return node;
+ #else
+ return 0;
+ #endif
+}
+
+size_t _mi_prim_numa_node_count(void) {
+ char buf[128];
+ unsigned node = 0;
+ for(node = 0; node < 256; node++) {
+ // enumerate node entries -- todo: it there a more efficient way to do this? (but ensure there is no allocation)
+ snprintf(buf, 127, "/sys/devices/system/node/node%u", node + 1);
+ if (mi_prim_access(buf,R_OK) != 0) break;
+ }
+ return (node+1);
+}
+
+#elif defined(__FreeBSD__) && __FreeBSD_version >= 1200000
+
+size_t _mi_prim_numa_node(void) {
+ domainset_t dom;
+ size_t node;
+ int policy;
+ if (cpuset_getdomain(CPU_LEVEL_CPUSET, CPU_WHICH_PID, -1, sizeof(dom), &dom, &policy) == -1) return 0ul;
+ for (node = 0; node < MAXMEMDOM; node++) {
+ if (DOMAINSET_ISSET(node, &dom)) return node;
+ }
+ return 0ul;
+}
+
+size_t _mi_prim_numa_node_count(void) {
+ size_t ndomains = 0;
+ size_t len = sizeof(ndomains);
+ if (sysctlbyname("vm.ndomains", &ndomains, &len, NULL, 0) == -1) return 0ul;
+ return ndomains;
+}
+
+#elif defined(__DragonFly__)
+
+size_t _mi_prim_numa_node(void) {
+ // TODO: DragonFly does not seem to provide any userland means to get this information.
+ return 0ul;
+}
+
+size_t _mi_prim_numa_node_count(void) {
+ size_t ncpus = 0, nvirtcoresperphys = 0;
+ size_t len = sizeof(size_t);
+ if (sysctlbyname("hw.ncpu", &ncpus, &len, NULL, 0) == -1) return 0ul;
+ if (sysctlbyname("hw.cpu_topology_ht_ids", &nvirtcoresperphys, &len, NULL, 0) == -1) return 0ul;
+ return nvirtcoresperphys * ncpus;
+}
+
+#else
+
+size_t _mi_prim_numa_node(void) {
+ return 0;
+}
+
+size_t _mi_prim_numa_node_count(void) {
+ return 1;
+}
+
+#endif
+
+// ----------------------------------------------------------------
+// Clock
+// ----------------------------------------------------------------
+
+#include <time.h>
+
+#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC)
+
+mi_msecs_t _mi_prim_clock_now(void) {
+ struct timespec t;
+ #ifdef CLOCK_MONOTONIC
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ #else
+ clock_gettime(CLOCK_REALTIME, &t);
+ #endif
+ return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000);
+}
+
+#else
+
+// low resolution timer
+mi_msecs_t _mi_prim_clock_now(void) {
+ #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0)
+ return (mi_msecs_t)clock();
+ #elif (CLOCKS_PER_SEC < 1000)
+ return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC);
+ #else
+ return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000);
+ #endif
+}
+
+#endif
+
+
+
+
+//----------------------------------------------------------------
+// Process info
+//----------------------------------------------------------------
+
+#if defined(__unix__) || defined(__unix) || defined(unix) || defined(__APPLE__) || defined(__HAIKU__)
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/resource.h>
+
+#if defined(__APPLE__)
+#include <mach/mach.h>
+#endif
+
+#if defined(__HAIKU__)
+#error #include <kernel/OS.h>
+#endif
+
+static mi_msecs_t timeval_secs(const struct timeval* tv) {
+ return ((mi_msecs_t)tv->tv_sec * 1000L) + ((mi_msecs_t)tv->tv_usec / 1000L);
+}
+
+void _mi_prim_process_info(mi_process_info_t* pinfo)
+{
+ struct rusage rusage;
+ getrusage(RUSAGE_SELF, &rusage);
+ pinfo->utime = timeval_secs(&rusage.ru_utime);
+ pinfo->stime = timeval_secs(&rusage.ru_stime);
+#if !defined(__HAIKU__)
+ pinfo->page_faults = rusage.ru_majflt;
+#endif
+#if defined(__HAIKU__)
+ // Haiku does not have (yet?) a way to
+ // get these stats per process
+ thread_info tid;
+ area_info mem;
+ ssize_t c;
+ get_thread_info(find_thread(0), &tid);
+ while (get_next_area_info(tid.team, &c, &mem) == B_OK) {
+ pinfo->peak_rss += mem.ram_size;
+ }
+ pinfo->page_faults = 0;
+#elif defined(__APPLE__)
+ pinfo->peak_rss = rusage.ru_maxrss; // macos reports in bytes
+ #ifdef MACH_TASK_BASIC_INFO
+ struct mach_task_basic_info info;
+ mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
+ if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) == KERN_SUCCESS) {
+ pinfo->current_rss = (size_t)info.resident_size;
+ }
+ #else
+ struct task_basic_info info;
+ mach_msg_type_number_t infoCount = TASK_BASIC_INFO_COUNT;
+ if (task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &infoCount) == KERN_SUCCESS) {
+ pinfo->current_rss = (size_t)info.resident_size;
+ }
+ #endif
+#else
+ pinfo->peak_rss = rusage.ru_maxrss * 1024; // Linux/BSD report in KiB
+#endif
+ // use defaults for commit
+}
+
+#else
+
+#ifndef __wasi__
+// WebAssembly instances are not processes
+#pragma message("define a way to get process info")
+#endif
+
+void _mi_prim_process_info(mi_process_info_t* pinfo)
+{
+ // use defaults
+ MI_UNUSED(pinfo);
+}
+
+#endif
+
+
+//----------------------------------------------------------------
+// Output
+//----------------------------------------------------------------
+
+void _mi_prim_out_stderr( const char* msg ) {
+ fputs(msg,stderr);
+}
+
+
+//----------------------------------------------------------------
+// Environment
+//----------------------------------------------------------------
+
+#if !defined(MI_USE_ENVIRON) || (MI_USE_ENVIRON!=0)
+// On Posix systemsr use `environ` to access environment variables
+// even before the C runtime is initialized.
+#if defined(__APPLE__) && defined(__has_include) && __has_include(<crt_externs.h>)
+#include <crt_externs.h>
+static char** mi_get_environ(void) {
+ return (*_NSGetEnviron());
+}
+#else
+extern char** environ;
+static char** mi_get_environ(void) {
+ return environ;
+}
+#endif
+bool _mi_prim_getenv(const char* name, char* result, size_t result_size) {
+ if (name==NULL) return false;
+ const size_t len = _mi_strlen(name);
+ if (len == 0) return false;
+ char** env = mi_get_environ();
+ if (env == NULL) return false;
+ // compare up to 10000 entries
+ for (int i = 0; i < 10000 && env[i] != NULL; i++) {
+ const char* s = env[i];
+ if (_mi_strnicmp(name, s, len) == 0 && s[len] == '=') { // case insensitive
+ // found it
+ _mi_strlcpy(result, s + len + 1, result_size);
+ return true;
+ }
+ }
+ return false;
+}
+#else
+// fallback: use standard C `getenv` but this cannot be used while initializing the C runtime
+bool _mi_prim_getenv(const char* name, char* result, size_t result_size) {
+ // cannot call getenv() when still initializing the C runtime.
+ if (_mi_preloading()) return false;
+ const char* s = getenv(name);
+ if (s == NULL) {
+ // we check the upper case name too.
+ char buf[64+1];
+ size_t len = _mi_strnlen(name,sizeof(buf)-1);
+ for (size_t i = 0; i < len; i++) {
+ buf[i] = _mi_toupper(name[i]);
+ }
+ buf[len] = 0;
+ s = getenv(buf);
+ }
+ if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false;
+ _mi_strlcpy(result, s, result_size);
+ return true;
+}
+#endif // !MI_USE_ENVIRON
+
+
+//----------------------------------------------------------------
+// Random
+//----------------------------------------------------------------
+
+#if defined(__APPLE__)
+
+#include <AvailabilityMacros.h>
+#if defined(MAC_OS_X_VERSION_10_10) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_10
+#include <CommonCrypto/CommonCryptoError.h>
+#include <CommonCrypto/CommonRandom.h>
+#endif
+bool _mi_prim_random_buf(void* buf, size_t buf_len) {
+ #if defined(MAC_OS_X_VERSION_10_15) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_15
+ // We prefere CCRandomGenerateBytes as it returns an error code while arc4random_buf
+ // may fail silently on macOS. See PR #390, and <https://opensource.apple.com/source/Libc/Libc-1439.40.11/gen/FreeBSD/arc4random.c.auto.html>
+ return (CCRandomGenerateBytes(buf, buf_len) == kCCSuccess);
+ #else
+ // fall back on older macOS
+ arc4random_buf(buf, buf_len);
+ return true;
+ #endif
+}
+
+#elif defined(__ANDROID__) || defined(__DragonFly__) || \
+ defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || \
+ defined(__sun)
+
+#include <stdlib.h>
+bool _mi_prim_random_buf(void* buf, size_t buf_len) {
+ arc4random_buf(buf, buf_len);
+ return true;
+}
+
+#elif defined(__linux__) || defined(__HAIKU__)
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
+bool _mi_prim_random_buf(void* buf, size_t buf_len) {
+ // Modern Linux provides `getrandom` but different distributions either use `sys/random.h` or `linux/random.h`
+ // and for the latter the actual `getrandom` call is not always defined.
+ // (see <https://stackoverflow.com/questions/45237324/why-doesnt-getrandom-compile>)
+ // We therefore use a syscall directly and fall back dynamically to /dev/urandom when needed.
+ #if defined(MI_HAS_SYSCALL_H) && defined(SYS_getrandom)
+ #ifndef GRND_NONBLOCK
+ #define GRND_NONBLOCK (1)
+ #endif
+ static _Atomic(uintptr_t) no_getrandom; // = 0
+ if (mi_atomic_load_acquire(&no_getrandom)==0) {
+ ssize_t ret = syscall(SYS_getrandom, buf, buf_len, GRND_NONBLOCK);
+ if (ret >= 0) return (buf_len == (size_t)ret);
+ if (errno != ENOSYS) return false;
+ mi_atomic_store_release(&no_getrandom, (uintptr_t)1); // don't call again, and fall back to /dev/urandom
+ }
+ #endif
+ int flags = O_RDONLY;
+ #if defined(O_CLOEXEC)
+ flags |= O_CLOEXEC;
+ #endif
+ int fd = mi_prim_open("/dev/urandom", flags);
+ if (fd < 0) return false;
+ size_t count = 0;
+ while(count < buf_len) {
+ ssize_t ret = mi_prim_read(fd, (char*)buf + count, buf_len - count);
+ if (ret<=0) {
+ if (errno!=EAGAIN && errno!=EINTR) break;
+ }
+ else {
+ count += ret;
+ }
+ }
+ mi_prim_close(fd);
+ return (count==buf_len);
+}
+
+#else
+
+bool _mi_prim_random_buf(void* buf, size_t buf_len) {
+ return false;
+}
+
+#endif
+
+
+//----------------------------------------------------------------
+// Thread init/done
+//----------------------------------------------------------------
+
+#if defined(MI_USE_PTHREADS)
+
+// use pthread local storage keys to detect thread ending
+// (and used with MI_TLS_PTHREADS for the default heap)
+pthread_key_t _mi_heap_default_key = (pthread_key_t)(-1);
+
+static void mi_pthread_done(void* value) {
+ if (value!=NULL) {
+ _mi_thread_done((mi_heap_t*)value);
+ }
+}
+
+void _mi_prim_thread_init_auto_done(void) {
+ mi_assert_internal(_mi_heap_default_key == (pthread_key_t)(-1));
+ pthread_key_create(&_mi_heap_default_key, &mi_pthread_done);
+}
+
+void _mi_prim_thread_done_auto_done(void) {
+ // nothing to do
+}
+
+void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
+ if (_mi_heap_default_key != (pthread_key_t)(-1)) { // can happen during recursive invocation on freeBSD
+ pthread_setspecific(_mi_heap_default_key, heap);
+ }
+}
+
+#else
+
+void _mi_prim_thread_init_auto_done(void) {
+ // nothing
+}
+
+void _mi_prim_thread_done_auto_done(void) {
+ // nothing
+}
+
+void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
+ MI_UNUSED(heap);
+}
+
+#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/prim/wasi/prim.c b/contrib/tools/python3/Objects/mimalloc/prim/wasi/prim.c
new file mode 100644
index 00000000000..640dac0493f
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/prim/wasi/prim.c
@@ -0,0 +1,276 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2023, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+// This file is included in `src/prim/prim.c`
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+#include "mimalloc/prim.h"
+#include <unistd.h> // sbrk()
+
+//---------------------------------------------
+// Initialize
+//---------------------------------------------
+
+void _mi_prim_mem_init( mi_os_mem_config_t* config ) {
+ config->page_size = 64*MI_KiB; // WebAssembly has a fixed page size: 64KiB
+ config->alloc_granularity = 16;
+ config->has_overcommit = false;
+ config->must_free_whole = true;
+ config->has_virtual_reserve = false;
+}
+
+//---------------------------------------------
+// Free
+//---------------------------------------------
+
+int _mi_prim_free(void* addr, size_t size ) {
+ MI_UNUSED(addr); MI_UNUSED(size);
+ // wasi heap cannot be shrunk
+ return 0;
+}
+
+
+//---------------------------------------------
+// Allocation: sbrk or memory_grow
+//---------------------------------------------
+
+#if defined(MI_USE_SBRK)
+ static void* mi_memory_grow( size_t size ) {
+ void* p = sbrk(size);
+ if (p == (void*)(-1)) return NULL;
+ #if !defined(__wasi__) // on wasi this is always zero initialized already (?)
+ memset(p,0,size);
+ #endif
+ return p;
+ }
+#elif defined(__wasi__)
+ static void* mi_memory_grow( size_t size ) {
+ size_t base = (size > 0 ? __builtin_wasm_memory_grow(0,_mi_divide_up(size, _mi_os_page_size()))
+ : __builtin_wasm_memory_size(0));
+ if (base == SIZE_MAX) return NULL;
+ return (void*)(base * _mi_os_page_size());
+ }
+#endif
+
+#if defined(MI_USE_PTHREADS)
+static pthread_mutex_t mi_heap_grow_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+static void* mi_prim_mem_grow(size_t size, size_t try_alignment) {
+ void* p = NULL;
+ if (try_alignment <= 1) {
+ // `sbrk` is not thread safe in general so try to protect it (we could skip this on WASM but leave it in for now)
+ #if defined(MI_USE_PTHREADS)
+ pthread_mutex_lock(&mi_heap_grow_mutex);
+ #endif
+ p = mi_memory_grow(size);
+ #if defined(MI_USE_PTHREADS)
+ pthread_mutex_unlock(&mi_heap_grow_mutex);
+ #endif
+ }
+ else {
+ void* base = NULL;
+ size_t alloc_size = 0;
+ // to allocate aligned use a lock to try to avoid thread interaction
+ // between getting the current size and actual allocation
+ // (also, `sbrk` is not thread safe in general)
+ #if defined(MI_USE_PTHREADS)
+ pthread_mutex_lock(&mi_heap_grow_mutex);
+ #endif
+ {
+ void* current = mi_memory_grow(0); // get current size
+ if (current != NULL) {
+ void* aligned_current = mi_align_up_ptr(current, try_alignment); // and align from there to minimize wasted space
+ alloc_size = _mi_align_up( ((uint8_t*)aligned_current - (uint8_t*)current) + size, _mi_os_page_size());
+ base = mi_memory_grow(alloc_size);
+ }
+ }
+ #if defined(MI_USE_PTHREADS)
+ pthread_mutex_unlock(&mi_heap_grow_mutex);
+ #endif
+ if (base != NULL) {
+ p = mi_align_up_ptr(base, try_alignment);
+ if ((uint8_t*)p + size > (uint8_t*)base + alloc_size) {
+ // another thread used wasm_memory_grow/sbrk in-between and we do not have enough
+ // space after alignment. Give up (and waste the space as we cannot shrink :-( )
+ // (in `mi_os_mem_alloc_aligned` this will fall back to overallocation to align)
+ p = NULL;
+ }
+ }
+ }
+ /*
+ if (p == NULL) {
+ _mi_warning_message("unable to allocate sbrk/wasm_memory_grow OS memory (%zu bytes, %zu alignment)\n", size, try_alignment);
+ errno = ENOMEM;
+ return NULL;
+ }
+ */
+ mi_assert_internal( p == NULL || try_alignment == 0 || (uintptr_t)p % try_alignment == 0 );
+ return p;
+}
+
+// Note: the `try_alignment` is just a hint and the returned pointer is not guaranteed to be aligned.
+int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) {
+ MI_UNUSED(allow_large); MI_UNUSED(commit);
+ *is_large = false;
+ *is_zero = false;
+ *addr = mi_prim_mem_grow(size, try_alignment);
+ return (*addr != NULL ? 0 : ENOMEM);
+}
+
+
+//---------------------------------------------
+// Commit/Reset/Protect
+//---------------------------------------------
+
+int _mi_prim_commit(void* addr, size_t size, bool* is_zero) {
+ MI_UNUSED(addr); MI_UNUSED(size);
+ *is_zero = false;
+ return 0;
+}
+
+int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit) {
+ MI_UNUSED(addr); MI_UNUSED(size);
+ *needs_recommit = false;
+ return 0;
+}
+
+int _mi_prim_reset(void* addr, size_t size) {
+ MI_UNUSED(addr); MI_UNUSED(size);
+ return 0;
+}
+
+int _mi_prim_protect(void* addr, size_t size, bool protect) {
+ MI_UNUSED(addr); MI_UNUSED(size); MI_UNUSED(protect);
+ return 0;
+}
+
+
+//---------------------------------------------
+// Huge pages and NUMA nodes
+//---------------------------------------------
+
+int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bool* is_zero, void** addr) {
+ MI_UNUSED(hint_addr); MI_UNUSED(size); MI_UNUSED(numa_node);
+ *is_zero = true;
+ *addr = NULL;
+ return ENOSYS;
+}
+
+size_t _mi_prim_numa_node(void) {
+ return 0;
+}
+
+size_t _mi_prim_numa_node_count(void) {
+ return 1;
+}
+
+
+//----------------------------------------------------------------
+// Clock
+//----------------------------------------------------------------
+
+#include <time.h>
+
+#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC)
+
+mi_msecs_t _mi_prim_clock_now(void) {
+ struct timespec t;
+ #ifdef CLOCK_MONOTONIC
+ clock_gettime(CLOCK_MONOTONIC, &t);
+ #else
+ clock_gettime(CLOCK_REALTIME, &t);
+ #endif
+ return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000);
+}
+
+#else
+
+// low resolution timer
+mi_msecs_t _mi_prim_clock_now(void) {
+ #if !defined(CLOCKS_PER_SEC) || (CLOCKS_PER_SEC == 1000) || (CLOCKS_PER_SEC == 0)
+ return (mi_msecs_t)clock();
+ #elif (CLOCKS_PER_SEC < 1000)
+ return (mi_msecs_t)clock() * (1000 / (mi_msecs_t)CLOCKS_PER_SEC);
+ #else
+ return (mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000);
+ #endif
+}
+
+#endif
+
+
+//----------------------------------------------------------------
+// Process info
+//----------------------------------------------------------------
+
+void _mi_prim_process_info(mi_process_info_t* pinfo)
+{
+ // use defaults
+ MI_UNUSED(pinfo);
+}
+
+
+//----------------------------------------------------------------
+// Output
+//----------------------------------------------------------------
+
+void _mi_prim_out_stderr( const char* msg ) {
+ fputs(msg,stderr);
+}
+
+
+//----------------------------------------------------------------
+// Environment
+//----------------------------------------------------------------
+
+bool _mi_prim_getenv(const char* name, char* result, size_t result_size) {
+ // cannot call getenv() when still initializing the C runtime.
+ if (_mi_preloading()) return false;
+ const char* s = getenv(name);
+ if (s == NULL) {
+ // we check the upper case name too.
+ char buf[64+1];
+ size_t len = _mi_strnlen(name,sizeof(buf)-1);
+ for (size_t i = 0; i < len; i++) {
+ buf[i] = _mi_toupper(name[i]);
+ }
+ buf[len] = 0;
+ s = getenv(buf);
+ }
+ if (s == NULL || _mi_strnlen(s,result_size) >= result_size) return false;
+ _mi_strlcpy(result, s, result_size);
+ return true;
+}
+
+
+//----------------------------------------------------------------
+// Random
+//----------------------------------------------------------------
+
+bool _mi_prim_random_buf(void* buf, size_t buf_len) {
+ return false;
+}
+
+
+//----------------------------------------------------------------
+// Thread init/done
+//----------------------------------------------------------------
+
+void _mi_prim_thread_init_auto_done(void) {
+ // nothing
+}
+
+void _mi_prim_thread_done_auto_done(void) {
+ // nothing
+}
+
+void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
+ MI_UNUSED(heap);
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/prim/windows/etw.h b/contrib/tools/python3/Objects/mimalloc/prim/windows/etw.h
new file mode 100644
index 00000000000..e9ec35fc9ed
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/prim/windows/etw.h
@@ -0,0 +1,905 @@
+//**********************************************************************`
+//* This is an include file generated by Message Compiler. *`
+//* *`
+//* Copyright (c) Microsoft Corporation. All Rights Reserved. *`
+//**********************************************************************`
+#pragma once
+
+//*****************************************************************************
+//
+// Notes on the ETW event code generated by MC:
+//
+// - Structures and arrays of structures are treated as an opaque binary blob.
+// The caller is responsible for packing the data for the structure into a
+// single region of memory, with no padding between values. The macro will
+// have an extra parameter for the length of the blob.
+// - Arrays of nul-terminated strings must be packed by the caller into a
+// single binary blob containing the correct number of strings, with a nul
+// after each string. The size of the blob is specified in characters, and
+// includes the final nul.
+// - Arrays of SID are treated as a single binary blob. The caller is
+// responsible for packing the SID values into a single region of memory with
+// no padding.
+// - The length attribute on the data element in the manifest is significant
+// for values with intype win:UnicodeString, win:AnsiString, or win:Binary.
+// The length attribute must be specified for win:Binary, and is optional for
+// win:UnicodeString and win:AnsiString (if no length is given, the strings
+// are assumed to be nul-terminated). For win:UnicodeString, the length is
+// measured in characters, not bytes.
+// - For an array of win:UnicodeString, win:AnsiString, or win:Binary, the
+// length attribute applies to every value in the array, so every value in
+// the array must have the same length. The values in the array are provided
+// to the macro via a single pointer -- the caller is responsible for packing
+// all of the values into a single region of memory with no padding between
+// values.
+// - Values of type win:CountedUnicodeString, win:CountedAnsiString, and
+// win:CountedBinary can be generated and collected on Vista or later.
+// However, they may not decode properly without the Windows 10 2018 Fall
+// Update.
+// - Arrays of type win:CountedUnicodeString, win:CountedAnsiString, and
+// win:CountedBinary must be packed by the caller into a single region of
+// memory. The format for each item is a UINT16 byte-count followed by that
+// many bytes of data. When providing the array to the generated macro, you
+// must provide the total size of the packed array data, including the UINT16
+// sizes for each item. In the case of win:CountedUnicodeString, the data
+// size is specified in WCHAR (16-bit) units. In the case of
+// win:CountedAnsiString and win:CountedBinary, the data size is specified in
+// bytes.
+//
+//*****************************************************************************
+
+#include <wmistr.h>
+#include <evntrace.h>
+#include <evntprov.h>
+
+#ifndef ETW_INLINE
+ #ifdef _ETW_KM_
+ // In kernel mode, save stack space by never inlining templates.
+ #define ETW_INLINE DECLSPEC_NOINLINE __inline
+ #else
+ // In user mode, save code size by inlining templates as appropriate.
+ #define ETW_INLINE __inline
+ #endif
+#endif // ETW_INLINE
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+//
+// MCGEN_DISABLE_PROVIDER_CODE_GENERATION macro:
+// Define this macro to have the compiler skip the generated functions in this
+// header.
+//
+#ifndef MCGEN_DISABLE_PROVIDER_CODE_GENERATION
+
+//
+// MCGEN_USE_KERNEL_MODE_APIS macro:
+// Controls whether the generated code uses kernel-mode or user-mode APIs.
+// - Set to 0 to use Windows user-mode APIs such as EventRegister.
+// - Set to 1 to use Windows kernel-mode APIs such as EtwRegister.
+// Default is based on whether the _ETW_KM_ macro is defined (i.e. by wdm.h).
+// Note that the APIs can also be overridden directly, e.g. by setting the
+// MCGEN_EVENTWRITETRANSFER or MCGEN_EVENTREGISTER macros.
+//
+#ifndef MCGEN_USE_KERNEL_MODE_APIS
+ #ifdef _ETW_KM_
+ #define MCGEN_USE_KERNEL_MODE_APIS 1
+ #else
+ #define MCGEN_USE_KERNEL_MODE_APIS 0
+ #endif
+#endif // MCGEN_USE_KERNEL_MODE_APIS
+
+//
+// MCGEN_HAVE_EVENTSETINFORMATION macro:
+// Controls how McGenEventSetInformation uses the EventSetInformation API.
+// - Set to 0 to disable the use of EventSetInformation
+// (McGenEventSetInformation will always return an error).
+// - Set to 1 to directly invoke MCGEN_EVENTSETINFORMATION.
+// - Set to 2 to to locate EventSetInformation at runtime via GetProcAddress
+// (user-mode) or MmGetSystemRoutineAddress (kernel-mode).
+// Default is determined as follows:
+// - If MCGEN_EVENTSETINFORMATION has been customized, set to 1
+// (i.e. use MCGEN_EVENTSETINFORMATION).
+// - Else if the target OS version has EventSetInformation, set to 1
+// (i.e. use MCGEN_EVENTSETINFORMATION).
+// - Else set to 2 (i.e. try to dynamically locate EventSetInformation).
+// Note that an McGenEventSetInformation function will only be generated if one
+// or more provider in a manifest has provider traits.
+//
+#ifndef MCGEN_HAVE_EVENTSETINFORMATION
+ #ifdef MCGEN_EVENTSETINFORMATION // if MCGEN_EVENTSETINFORMATION has been customized,
+ #define MCGEN_HAVE_EVENTSETINFORMATION 1 // directly invoke MCGEN_EVENTSETINFORMATION(...).
+ #elif MCGEN_USE_KERNEL_MODE_APIS // else if using kernel-mode APIs,
+ #if NTDDI_VERSION >= 0x06040000 // if target OS is Windows 10 or later,
+ #define MCGEN_HAVE_EVENTSETINFORMATION 1 // directly invoke MCGEN_EVENTSETINFORMATION(...).
+ #else // else
+ #define MCGEN_HAVE_EVENTSETINFORMATION 2 // find "EtwSetInformation" via MmGetSystemRoutineAddress.
+ #endif // else (using user-mode APIs)
+ #else // if target OS and SDK is Windows 8 or later,
+ #if WINVER >= 0x0602 && defined(EVENT_FILTER_TYPE_SCHEMATIZED)
+ #define MCGEN_HAVE_EVENTSETINFORMATION 1 // directly invoke MCGEN_EVENTSETINFORMATION(...).
+ #else // else
+ #define MCGEN_HAVE_EVENTSETINFORMATION 2 // find "EventSetInformation" via GetModuleHandleExW/GetProcAddress.
+ #endif
+ #endif
+#endif // MCGEN_HAVE_EVENTSETINFORMATION
+
+//
+// MCGEN Override Macros
+//
+// The following override macros may be defined before including this header
+// to control the APIs used by this header:
+//
+// - MCGEN_EVENTREGISTER
+// - MCGEN_EVENTUNREGISTER
+// - MCGEN_EVENTSETINFORMATION
+// - MCGEN_EVENTWRITETRANSFER
+//
+// If the macro is undefined, the MC implementation will default to the
+// corresponding ETW APIs. For example, if the MCGEN_EVENTREGISTER macro is
+// undefined, the EventRegister[MyProviderName] macro will use EventRegister
+// in user mode and will use EtwRegister in kernel mode.
+//
+// To prevent issues from conflicting definitions of these macros, the value
+// of the override macro will be used as a suffix in certain internal function
+// names. Because of this, the override macros must follow certain rules:
+//
+// - The macro must be defined before any MC-generated header is included and
+// must not be undefined or redefined after any MC-generated header is
+// included. Different translation units (i.e. different .c or .cpp files)
+// may set the macros to different values, but within a translation unit
+// (within a single .c or .cpp file), the macro must be set once and not
+// changed.
+// - The override must be an object-like macro, not a function-like macro
+// (i.e. the override macro must not have a parameter list).
+// - The override macro's value must be a simple identifier, i.e. must be
+// something that starts with a letter or '_' and contains only letters,
+// numbers, and '_' characters.
+// - If the override macro's value is the name of a second object-like macro,
+// the second object-like macro must follow the same rules. (The override
+// macro's value can also be the name of a function-like macro, in which
+// case the function-like macro does not need to follow the same rules.)
+//
+// For example, the following will cause compile errors:
+//
+// #define MCGEN_EVENTWRITETRANSFER MyNamespace::MyClass::MyFunction // Value has non-identifier characters (colon).
+// #define MCGEN_EVENTWRITETRANSFER GetEventWriteFunctionPointer(7) // Value has non-identifier characters (parentheses).
+// #define MCGEN_EVENTWRITETRANSFER(h,e,a,r,c,d) EventWrite(h,e,c,d) // Override is defined as a function-like macro.
+// #define MY_OBJECT_LIKE_MACRO MyNamespace::MyClass::MyEventWriteFunction
+// #define MCGEN_EVENTWRITETRANSFER MY_OBJECT_LIKE_MACRO // Evaluates to something with non-identifier characters (colon).
+//
+// The following would be ok:
+//
+// #define MCGEN_EVENTWRITETRANSFER MyEventWriteFunction1 // OK, suffix will be "MyEventWriteFunction1".
+// #define MY_OBJECT_LIKE_MACRO MyEventWriteFunction2
+// #define MCGEN_EVENTWRITETRANSFER MY_OBJECT_LIKE_MACRO // OK, suffix will be "MyEventWriteFunction2".
+// #define MY_FUNCTION_LIKE_MACRO(h,e,a,r,c,d) MyNamespace::MyClass::MyEventWriteFunction3(h,e,c,d)
+// #define MCGEN_EVENTWRITETRANSFER MY_FUNCTION_LIKE_MACRO // OK, suffix will be "MY_FUNCTION_LIKE_MACRO".
+//
+#ifndef MCGEN_EVENTREGISTER
+ #if MCGEN_USE_KERNEL_MODE_APIS
+ #define MCGEN_EVENTREGISTER EtwRegister
+ #else
+ #define MCGEN_EVENTREGISTER EventRegister
+ #endif
+#endif // MCGEN_EVENTREGISTER
+#ifndef MCGEN_EVENTUNREGISTER
+ #if MCGEN_USE_KERNEL_MODE_APIS
+ #define MCGEN_EVENTUNREGISTER EtwUnregister
+ #else
+ #define MCGEN_EVENTUNREGISTER EventUnregister
+ #endif
+#endif // MCGEN_EVENTUNREGISTER
+#ifndef MCGEN_EVENTSETINFORMATION
+ #if MCGEN_USE_KERNEL_MODE_APIS
+ #define MCGEN_EVENTSETINFORMATION EtwSetInformation
+ #else
+ #define MCGEN_EVENTSETINFORMATION EventSetInformation
+ #endif
+#endif // MCGEN_EVENTSETINFORMATION
+#ifndef MCGEN_EVENTWRITETRANSFER
+ #if MCGEN_USE_KERNEL_MODE_APIS
+ #define MCGEN_EVENTWRITETRANSFER EtwWriteTransfer
+ #else
+ #define MCGEN_EVENTWRITETRANSFER EventWriteTransfer
+ #endif
+#endif // MCGEN_EVENTWRITETRANSFER
+
+//
+// MCGEN_EVENT_ENABLED macro:
+// Override to control how the EventWrite[EventName] macros determine whether
+// an event is enabled. The default behavior is for EventWrite[EventName] to
+// use the EventEnabled[EventName] macros.
+//
+#ifndef MCGEN_EVENT_ENABLED
+#define MCGEN_EVENT_ENABLED(EventName) EventEnabled##EventName()
+#endif
+
+//
+// MCGEN_EVENT_ENABLED_FORCONTEXT macro:
+// Override to control how the EventWrite[EventName]_ForContext macros
+// determine whether an event is enabled. The default behavior is for
+// EventWrite[EventName]_ForContext to use the
+// EventEnabled[EventName]_ForContext macros.
+//
+#ifndef MCGEN_EVENT_ENABLED_FORCONTEXT
+#define MCGEN_EVENT_ENABLED_FORCONTEXT(pContext, EventName) EventEnabled##EventName##_ForContext(pContext)
+#endif
+
+//
+// MCGEN_ENABLE_CHECK macro:
+// Determines whether the specified event would be considered as enabled
+// based on the state of the specified context. Slightly faster than calling
+// McGenEventEnabled directly.
+//
+#ifndef MCGEN_ENABLE_CHECK
+#define MCGEN_ENABLE_CHECK(Context, Descriptor) (Context.IsEnabled && McGenEventEnabled(&Context, &Descriptor))
+#endif
+
+#if !defined(MCGEN_TRACE_CONTEXT_DEF)
+#define MCGEN_TRACE_CONTEXT_DEF
+// This structure is for use by MC-generated code and should not be used directly.
+typedef struct _MCGEN_TRACE_CONTEXT
+{
+ TRACEHANDLE RegistrationHandle;
+ TRACEHANDLE Logger; // Used as pointer to provider traits.
+ ULONGLONG MatchAnyKeyword;
+ ULONGLONG MatchAllKeyword;
+ ULONG Flags;
+ ULONG IsEnabled;
+ UCHAR Level;
+ UCHAR Reserve;
+ USHORT EnableBitsCount;
+ PULONG EnableBitMask;
+ const ULONGLONG* EnableKeyWords;
+ const UCHAR* EnableLevel;
+} MCGEN_TRACE_CONTEXT, *PMCGEN_TRACE_CONTEXT;
+#endif // MCGEN_TRACE_CONTEXT_DEF
+
+#if !defined(MCGEN_LEVEL_KEYWORD_ENABLED_DEF)
+#define MCGEN_LEVEL_KEYWORD_ENABLED_DEF
+//
+// Determines whether an event with a given Level and Keyword would be
+// considered as enabled based on the state of the specified context.
+// Note that you may want to use MCGEN_ENABLE_CHECK instead of calling this
+// function directly.
+//
+FORCEINLINE
+BOOLEAN
+McGenLevelKeywordEnabled(
+ _In_ PMCGEN_TRACE_CONTEXT EnableInfo,
+ _In_ UCHAR Level,
+ _In_ ULONGLONG Keyword
+ )
+{
+ //
+ // Check if the event Level is lower than the level at which
+ // the channel is enabled.
+ // If the event Level is 0 or the channel is enabled at level 0,
+ // all levels are enabled.
+ //
+
+ if ((Level <= EnableInfo->Level) || // This also covers the case of Level == 0.
+ (EnableInfo->Level == 0)) {
+
+ //
+ // Check if Keyword is enabled
+ //
+
+ if ((Keyword == (ULONGLONG)0) ||
+ ((Keyword & EnableInfo->MatchAnyKeyword) &&
+ ((Keyword & EnableInfo->MatchAllKeyword) == EnableInfo->MatchAllKeyword))) {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+#endif // MCGEN_LEVEL_KEYWORD_ENABLED_DEF
+
+#if !defined(MCGEN_EVENT_ENABLED_DEF)
+#define MCGEN_EVENT_ENABLED_DEF
+//
+// Determines whether the specified event would be considered as enabled based
+// on the state of the specified context. Note that you may want to use
+// MCGEN_ENABLE_CHECK instead of calling this function directly.
+//
+FORCEINLINE
+BOOLEAN
+McGenEventEnabled(
+ _In_ PMCGEN_TRACE_CONTEXT EnableInfo,
+ _In_ PCEVENT_DESCRIPTOR EventDescriptor
+ )
+{
+ return McGenLevelKeywordEnabled(EnableInfo, EventDescriptor->Level, EventDescriptor->Keyword);
+}
+#endif // MCGEN_EVENT_ENABLED_DEF
+
+#if !defined(MCGEN_CONTROL_CALLBACK)
+#define MCGEN_CONTROL_CALLBACK
+
+// This function is for use by MC-generated code and should not be used directly.
+DECLSPEC_NOINLINE __inline
+VOID
+__stdcall
+McGenControlCallbackV2(
+ _In_ LPCGUID SourceId,
+ _In_ ULONG ControlCode,
+ _In_ UCHAR Level,
+ _In_ ULONGLONG MatchAnyKeyword,
+ _In_ ULONGLONG MatchAllKeyword,
+ _In_opt_ PEVENT_FILTER_DESCRIPTOR FilterData,
+ _Inout_opt_ PVOID CallbackContext
+ )
+/*++
+
+Routine Description:
+
+ This is the notification callback for Windows Vista and later.
+
+Arguments:
+
+ SourceId - The GUID that identifies the session that enabled the provider.
+
+ ControlCode - The parameter indicates whether the provider
+ is being enabled or disabled.
+
+ Level - The level at which the event is enabled.
+
+ MatchAnyKeyword - The bitmask of keywords that the provider uses to
+ determine the category of events that it writes.
+
+ MatchAllKeyword - This bitmask additionally restricts the category
+ of events that the provider writes.
+
+ FilterData - The provider-defined data.
+
+ CallbackContext - The context of the callback that is defined when the provider
+ called EtwRegister to register itself.
+
+Remarks:
+
+ ETW calls this function to notify provider of enable/disable
+
+--*/
+{
+ PMCGEN_TRACE_CONTEXT Ctx = (PMCGEN_TRACE_CONTEXT)CallbackContext;
+ ULONG Ix;
+#ifndef MCGEN_PRIVATE_ENABLE_CALLBACK_V2
+ UNREFERENCED_PARAMETER(SourceId);
+ UNREFERENCED_PARAMETER(FilterData);
+#endif
+
+ if (Ctx == NULL) {
+ return;
+ }
+
+ switch (ControlCode) {
+
+ case EVENT_CONTROL_CODE_ENABLE_PROVIDER:
+ Ctx->Level = Level;
+ Ctx->MatchAnyKeyword = MatchAnyKeyword;
+ Ctx->MatchAllKeyword = MatchAllKeyword;
+ Ctx->IsEnabled = EVENT_CONTROL_CODE_ENABLE_PROVIDER;
+
+ for (Ix = 0; Ix < Ctx->EnableBitsCount; Ix += 1) {
+ if (McGenLevelKeywordEnabled(Ctx, Ctx->EnableLevel[Ix], Ctx->EnableKeyWords[Ix]) != FALSE) {
+ Ctx->EnableBitMask[Ix >> 5] |= (1 << (Ix % 32));
+ } else {
+ Ctx->EnableBitMask[Ix >> 5] &= ~(1 << (Ix % 32));
+ }
+ }
+ break;
+
+ case EVENT_CONTROL_CODE_DISABLE_PROVIDER:
+ Ctx->IsEnabled = EVENT_CONTROL_CODE_DISABLE_PROVIDER;
+ Ctx->Level = 0;
+ Ctx->MatchAnyKeyword = 0;
+ Ctx->MatchAllKeyword = 0;
+ if (Ctx->EnableBitsCount > 0) {
+#pragma warning(suppress: 26451) // Arithmetic overflow cannot occur, no matter the value of EnableBitCount
+ RtlZeroMemory(Ctx->EnableBitMask, (((Ctx->EnableBitsCount - 1) / 32) + 1) * sizeof(ULONG));
+ }
+ break;
+
+ default:
+ break;
+ }
+
+#ifdef MCGEN_PRIVATE_ENABLE_CALLBACK_V2
+ //
+ // Call user defined callback
+ //
+ MCGEN_PRIVATE_ENABLE_CALLBACK_V2(
+ SourceId,
+ ControlCode,
+ Level,
+ MatchAnyKeyword,
+ MatchAllKeyword,
+ FilterData,
+ CallbackContext
+ );
+#endif // MCGEN_PRIVATE_ENABLE_CALLBACK_V2
+
+ return;
+}
+
+#endif // MCGEN_CONTROL_CALLBACK
+
+#ifndef _mcgen_PENABLECALLBACK
+ #if MCGEN_USE_KERNEL_MODE_APIS
+ #define _mcgen_PENABLECALLBACK PETWENABLECALLBACK
+ #else
+ #define _mcgen_PENABLECALLBACK PENABLECALLBACK
+ #endif
+#endif // _mcgen_PENABLECALLBACK
+
+#if !defined(_mcgen_PASTE2)
+// This macro is for use by MC-generated code and should not be used directly.
+#define _mcgen_PASTE2(a, b) _mcgen_PASTE2_imp(a, b)
+#define _mcgen_PASTE2_imp(a, b) a##b
+#endif // _mcgen_PASTE2
+
+#if !defined(_mcgen_PASTE3)
+// This macro is for use by MC-generated code and should not be used directly.
+#define _mcgen_PASTE3(a, b, c) _mcgen_PASTE3_imp(a, b, c)
+#define _mcgen_PASTE3_imp(a, b, c) a##b##_##c
+#endif // _mcgen_PASTE3
+
+//
+// Macro validation
+//
+
+// Validate MCGEN_EVENTREGISTER:
+
+// Trigger an error if MCGEN_EVENTREGISTER is not an unqualified (simple) identifier:
+struct _mcgen_PASTE2(MCGEN_EVENTREGISTER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTREGISTER);
+
+// Trigger an error if MCGEN_EVENTREGISTER is redefined:
+typedef struct _mcgen_PASTE2(MCGEN_EVENTREGISTER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTREGISTER)
+ MCGEN_EVENTREGISTER_must_not_be_redefined_between_headers;
+
+// Trigger an error if MCGEN_EVENTREGISTER is defined as a function-like macro:
+typedef void MCGEN_EVENTREGISTER_must_not_be_a_functionLike_macro_MCGEN_EVENTREGISTER;
+typedef int _mcgen_PASTE2(MCGEN_EVENTREGISTER_must_not_be_a_functionLike_macro_, MCGEN_EVENTREGISTER);
+
+// Validate MCGEN_EVENTUNREGISTER:
+
+// Trigger an error if MCGEN_EVENTUNREGISTER is not an unqualified (simple) identifier:
+struct _mcgen_PASTE2(MCGEN_EVENTUNREGISTER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTUNREGISTER);
+
+// Trigger an error if MCGEN_EVENTUNREGISTER is redefined:
+typedef struct _mcgen_PASTE2(MCGEN_EVENTUNREGISTER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTUNREGISTER)
+ MCGEN_EVENTUNREGISTER_must_not_be_redefined_between_headers;
+
+// Trigger an error if MCGEN_EVENTUNREGISTER is defined as a function-like macro:
+typedef void MCGEN_EVENTUNREGISTER_must_not_be_a_functionLike_macro_MCGEN_EVENTUNREGISTER;
+typedef int _mcgen_PASTE2(MCGEN_EVENTUNREGISTER_must_not_be_a_functionLike_macro_, MCGEN_EVENTUNREGISTER);
+
+// Validate MCGEN_EVENTSETINFORMATION:
+
+// Trigger an error if MCGEN_EVENTSETINFORMATION is not an unqualified (simple) identifier:
+struct _mcgen_PASTE2(MCGEN_EVENTSETINFORMATION_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTSETINFORMATION);
+
+// Trigger an error if MCGEN_EVENTSETINFORMATION is redefined:
+typedef struct _mcgen_PASTE2(MCGEN_EVENTSETINFORMATION_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTSETINFORMATION)
+ MCGEN_EVENTSETINFORMATION_must_not_be_redefined_between_headers;
+
+// Trigger an error if MCGEN_EVENTSETINFORMATION is defined as a function-like macro:
+typedef void MCGEN_EVENTSETINFORMATION_must_not_be_a_functionLike_macro_MCGEN_EVENTSETINFORMATION;
+typedef int _mcgen_PASTE2(MCGEN_EVENTSETINFORMATION_must_not_be_a_functionLike_macro_, MCGEN_EVENTSETINFORMATION);
+
+// Validate MCGEN_EVENTWRITETRANSFER:
+
+// Trigger an error if MCGEN_EVENTWRITETRANSFER is not an unqualified (simple) identifier:
+struct _mcgen_PASTE2(MCGEN_EVENTWRITETRANSFER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTWRITETRANSFER);
+
+// Trigger an error if MCGEN_EVENTWRITETRANSFER is redefined:
+typedef struct _mcgen_PASTE2(MCGEN_EVENTWRITETRANSFER_definition_must_be_an_unqualified_identifier_, MCGEN_EVENTWRITETRANSFER)
+ MCGEN_EVENTWRITETRANSFER_must_not_be_redefined_between_headers;;
+
+// Trigger an error if MCGEN_EVENTWRITETRANSFER is defined as a function-like macro:
+typedef void MCGEN_EVENTWRITETRANSFER_must_not_be_a_functionLike_macro_MCGEN_EVENTWRITETRANSFER;
+typedef int _mcgen_PASTE2(MCGEN_EVENTWRITETRANSFER_must_not_be_a_functionLike_macro_, MCGEN_EVENTWRITETRANSFER);
+
+#ifndef McGenEventWrite_def
+#define McGenEventWrite_def
+
+// This macro is for use by MC-generated code and should not be used directly.
+#define McGenEventWrite _mcgen_PASTE2(McGenEventWrite_, MCGEN_EVENTWRITETRANSFER)
+
+// This function is for use by MC-generated code and should not be used directly.
+DECLSPEC_NOINLINE __inline
+ULONG __stdcall
+McGenEventWrite(
+ _In_ PMCGEN_TRACE_CONTEXT Context,
+ _In_ PCEVENT_DESCRIPTOR Descriptor,
+ _In_opt_ LPCGUID ActivityId,
+ _In_range_(1, 128) ULONG EventDataCount,
+ _Pre_cap_(EventDataCount) EVENT_DATA_DESCRIPTOR* EventData
+ )
+{
+ const USHORT UNALIGNED* Traits;
+
+ // Some customized MCGEN_EVENTWRITETRANSFER macros might ignore ActivityId.
+ UNREFERENCED_PARAMETER(ActivityId);
+
+ Traits = (const USHORT UNALIGNED*)(UINT_PTR)Context->Logger;
+
+ if (Traits == NULL) {
+ EventData[0].Ptr = 0;
+ EventData[0].Size = 0;
+ EventData[0].Reserved = 0;
+ } else {
+ EventData[0].Ptr = (ULONG_PTR)Traits;
+ EventData[0].Size = *Traits;
+ EventData[0].Reserved = 2; // EVENT_DATA_DESCRIPTOR_TYPE_PROVIDER_METADATA
+ }
+
+ return MCGEN_EVENTWRITETRANSFER(
+ Context->RegistrationHandle,
+ Descriptor,
+ ActivityId,
+ NULL,
+ EventDataCount,
+ EventData);
+}
+#endif // McGenEventWrite_def
+
+#if !defined(McGenEventRegisterUnregister)
+#define McGenEventRegisterUnregister
+
+// This macro is for use by MC-generated code and should not be used directly.
+#define McGenEventRegister _mcgen_PASTE2(McGenEventRegister_, MCGEN_EVENTREGISTER)
+
+#pragma warning(push)
+#pragma warning(disable:6103)
+// This function is for use by MC-generated code and should not be used directly.
+DECLSPEC_NOINLINE __inline
+ULONG __stdcall
+McGenEventRegister(
+ _In_ LPCGUID ProviderId,
+ _In_opt_ _mcgen_PENABLECALLBACK EnableCallback,
+ _In_opt_ PVOID CallbackContext,
+ _Inout_ PREGHANDLE RegHandle
+ )
+/*++
+
+Routine Description:
+
+ This function registers the provider with ETW.
+
+Arguments:
+
+ ProviderId - Provider ID to register with ETW.
+
+ EnableCallback - Callback to be used.
+
+ CallbackContext - Context for the callback.
+
+ RegHandle - Pointer to registration handle.
+
+Remarks:
+
+ Should not be called if the provider is already registered (i.e. should not
+ be called if *RegHandle != 0). Repeatedly registering a provider is a bug
+ and may indicate a race condition. However, for compatibility with previous
+ behavior, this function will return SUCCESS in this case.
+
+--*/
+{
+ ULONG Error;
+
+ if (*RegHandle != 0)
+ {
+ Error = 0; // ERROR_SUCCESS
+ }
+ else
+ {
+ Error = MCGEN_EVENTREGISTER(ProviderId, EnableCallback, CallbackContext, RegHandle);
+ }
+
+ return Error;
+}
+#pragma warning(pop)
+
+// This macro is for use by MC-generated code and should not be used directly.
+#define McGenEventUnregister _mcgen_PASTE2(McGenEventUnregister_, MCGEN_EVENTUNREGISTER)
+
+// This function is for use by MC-generated code and should not be used directly.
+DECLSPEC_NOINLINE __inline
+ULONG __stdcall
+McGenEventUnregister(_Inout_ PREGHANDLE RegHandle)
+/*++
+
+Routine Description:
+
+ Unregister from ETW and set *RegHandle = 0.
+
+Arguments:
+
+ RegHandle - the pointer to the provider registration handle
+
+Remarks:
+
+ If provider has not been registered (i.e. if *RegHandle == 0),
+ return SUCCESS. It is safe to call McGenEventUnregister even if the
+ call to McGenEventRegister returned an error.
+
+--*/
+{
+ ULONG Error;
+
+ if(*RegHandle == 0)
+ {
+ Error = 0; // ERROR_SUCCESS
+ }
+ else
+ {
+ Error = MCGEN_EVENTUNREGISTER(*RegHandle);
+ *RegHandle = (REGHANDLE)0;
+ }
+
+ return Error;
+}
+
+#endif // McGenEventRegisterUnregister
+
+#ifndef _mcgen_EVENT_BIT_SET
+ #if defined(_M_IX86) || defined(_M_X64)
+ // This macro is for use by MC-generated code and should not be used directly.
+ #define _mcgen_EVENT_BIT_SET(EnableBits, BitPosition) ((((const unsigned char*)EnableBits)[BitPosition >> 3] & (1u << (BitPosition & 7))) != 0)
+ #else // CPU type
+ // This macro is for use by MC-generated code and should not be used directly.
+ #define _mcgen_EVENT_BIT_SET(EnableBits, BitPosition) ((EnableBits[BitPosition >> 5] & (1u << (BitPosition & 31))) != 0)
+ #endif // CPU type
+#endif // _mcgen_EVENT_BIT_SET
+
+#endif // MCGEN_DISABLE_PROVIDER_CODE_GENERATION
+
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+// Provider "microsoft-windows-mimalloc" event count 2
+//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+// Provider GUID = 138f4dbb-ee04-4899-aa0a-572ad4475779
+EXTERN_C __declspec(selectany) const GUID ETW_MI_Provider = {0x138f4dbb, 0xee04, 0x4899, {0xaa, 0x0a, 0x57, 0x2a, 0xd4, 0x47, 0x57, 0x79}};
+
+#ifndef ETW_MI_Provider_Traits
+#define ETW_MI_Provider_Traits NULL
+#endif // ETW_MI_Provider_Traits
+
+//
+// Event Descriptors
+//
+EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR ETW_MI_ALLOC = {0x64, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0};
+#define ETW_MI_ALLOC_value 0x64
+EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR ETW_MI_FREE = {0x65, 0x1, 0x0, 0x4, 0x0, 0x0, 0x0};
+#define ETW_MI_FREE_value 0x65
+
+//
+// MCGEN_DISABLE_PROVIDER_CODE_GENERATION macro:
+// Define this macro to have the compiler skip the generated functions in this
+// header.
+//
+#ifndef MCGEN_DISABLE_PROVIDER_CODE_GENERATION
+
+//
+// Event Enablement Bits
+// These variables are for use by MC-generated code and should not be used directly.
+//
+EXTERN_C __declspec(selectany) DECLSPEC_CACHEALIGN ULONG microsoft_windows_mimallocEnableBits[1];
+EXTERN_C __declspec(selectany) const ULONGLONG microsoft_windows_mimallocKeywords[1] = {0x0};
+EXTERN_C __declspec(selectany) const unsigned char microsoft_windows_mimallocLevels[1] = {4};
+
+//
+// Provider context
+//
+EXTERN_C __declspec(selectany) MCGEN_TRACE_CONTEXT ETW_MI_Provider_Context = {0, (ULONG_PTR)ETW_MI_Provider_Traits, 0, 0, 0, 0, 0, 0, 1, microsoft_windows_mimallocEnableBits, microsoft_windows_mimallocKeywords, microsoft_windows_mimallocLevels};
+
+//
+// Provider REGHANDLE
+//
+#define microsoft_windows_mimallocHandle (ETW_MI_Provider_Context.RegistrationHandle)
+
+//
+// This macro is set to 0, indicating that the EventWrite[Name] macros do not
+// have an Activity parameter. This is controlled by the -km and -um options.
+//
+#define ETW_MI_Provider_EventWriteActivity 0
+
+//
+// Register with ETW using the control GUID specified in the manifest.
+// Invoke this macro during module initialization (i.e. program startup,
+// DLL process attach, or driver load) to initialize the provider.
+// Note that if this function returns an error, the error means that
+// will not work, but no action needs to be taken -- even if EventRegister
+// returns an error, it is generally safe to use EventWrite and
+// EventUnregister macros (they will be no-ops if EventRegister failed).
+//
+#ifndef EventRegistermicrosoft_windows_mimalloc
+#define EventRegistermicrosoft_windows_mimalloc() McGenEventRegister(&ETW_MI_Provider, McGenControlCallbackV2, &ETW_MI_Provider_Context, &microsoft_windows_mimallocHandle)
+#endif
+
+//
+// Register with ETW using a specific control GUID (i.e. a GUID other than what
+// is specified in the manifest). Advanced scenarios only.
+//
+#ifndef EventRegisterByGuidmicrosoft_windows_mimalloc
+#define EventRegisterByGuidmicrosoft_windows_mimalloc(Guid) McGenEventRegister(&(Guid), McGenControlCallbackV2, &ETW_MI_Provider_Context, &microsoft_windows_mimallocHandle)
+#endif
+
+//
+// Unregister with ETW and close the provider.
+// Invoke this macro during module shutdown (i.e. program exit, DLL process
+// detach, or driver unload) to unregister the provider.
+// Note that you MUST call EventUnregister before DLL or driver unload
+// (not optional): failure to unregister a provider before DLL or driver unload
+// will result in crashes.
+//
+#ifndef EventUnregistermicrosoft_windows_mimalloc
+#define EventUnregistermicrosoft_windows_mimalloc() McGenEventUnregister(&microsoft_windows_mimallocHandle)
+#endif
+
+//
+// MCGEN_ENABLE_FORCONTEXT_CODE_GENERATION macro:
+// Define this macro to enable support for caller-allocated provider context.
+//
+#ifdef MCGEN_ENABLE_FORCONTEXT_CODE_GENERATION
+
+//
+// Advanced scenarios: Caller-allocated provider context.
+// Use when multiple differently-configured provider handles are needed,
+// e.g. for container-aware drivers, one context per container.
+//
+// Usage:
+//
+// - Caller enables the feature before including this header, e.g.
+// #define MCGEN_ENABLE_FORCONTEXT_CODE_GENERATION 1
+// - Caller allocates memory, e.g. pContext = malloc(sizeof(McGenContext_microsoft_windows_mimalloc));
+// - Caller registers the provider, e.g. EventRegistermicrosoft_windows_mimalloc_ForContext(pContext);
+// - Caller writes events, e.g. EventWriteMyEvent_ForContext(pContext, ...);
+// - Caller unregisters, e.g. EventUnregistermicrosoft_windows_mimalloc_ForContext(pContext);
+// - Caller frees memory, e.g. free(pContext);
+//
+
+typedef struct tagMcGenContext_microsoft_windows_mimalloc {
+ // The fields of this structure are subject to change and should
+ // not be accessed directly. To access the provider's REGHANDLE,
+ // use microsoft_windows_mimallocHandle_ForContext(pContext).
+ MCGEN_TRACE_CONTEXT Context;
+ ULONG EnableBits[1];
+} McGenContext_microsoft_windows_mimalloc;
+
+#define EventRegistermicrosoft_windows_mimalloc_ForContext(pContext) _mcgen_PASTE2(_mcgen_RegisterForContext_microsoft_windows_mimalloc_, MCGEN_EVENTREGISTER)(&ETW_MI_Provider, pContext)
+#define EventRegisterByGuidmicrosoft_windows_mimalloc_ForContext(Guid, pContext) _mcgen_PASTE2(_mcgen_RegisterForContext_microsoft_windows_mimalloc_, MCGEN_EVENTREGISTER)(&(Guid), pContext)
+#define EventUnregistermicrosoft_windows_mimalloc_ForContext(pContext) McGenEventUnregister(&(pContext)->Context.RegistrationHandle)
+
+//
+// Provider REGHANDLE for caller-allocated context.
+//
+#define microsoft_windows_mimallocHandle_ForContext(pContext) ((pContext)->Context.RegistrationHandle)
+
+// This function is for use by MC-generated code and should not be used directly.
+// Initialize and register the caller-allocated context.
+__inline
+ULONG __stdcall
+_mcgen_PASTE2(_mcgen_RegisterForContext_microsoft_windows_mimalloc_, MCGEN_EVENTREGISTER)(
+ _In_ LPCGUID pProviderId,
+ _Out_ McGenContext_microsoft_windows_mimalloc* pContext)
+{
+ RtlZeroMemory(pContext, sizeof(*pContext));
+ pContext->Context.Logger = (ULONG_PTR)ETW_MI_Provider_Traits;
+ pContext->Context.EnableBitsCount = 1;
+ pContext->Context.EnableBitMask = pContext->EnableBits;
+ pContext->Context.EnableKeyWords = microsoft_windows_mimallocKeywords;
+ pContext->Context.EnableLevel = microsoft_windows_mimallocLevels;
+ return McGenEventRegister(
+ pProviderId,
+ McGenControlCallbackV2,
+ &pContext->Context,
+ &pContext->Context.RegistrationHandle);
+}
+
+// This function is for use by MC-generated code and should not be used directly.
+// Trigger a compile error if called with the wrong parameter type.
+FORCEINLINE
+_Ret_ McGenContext_microsoft_windows_mimalloc*
+_mcgen_CheckContextType_microsoft_windows_mimalloc(_In_ McGenContext_microsoft_windows_mimalloc* pContext)
+{
+ return pContext;
+}
+
+#endif // MCGEN_ENABLE_FORCONTEXT_CODE_GENERATION
+
+//
+// Enablement check macro for event "ETW_MI_ALLOC"
+//
+#define EventEnabledETW_MI_ALLOC() _mcgen_EVENT_BIT_SET(microsoft_windows_mimallocEnableBits, 0)
+#define EventEnabledETW_MI_ALLOC_ForContext(pContext) _mcgen_EVENT_BIT_SET(_mcgen_CheckContextType_microsoft_windows_mimalloc(pContext)->EnableBits, 0)
+
+//
+// Event write macros for event "ETW_MI_ALLOC"
+//
+#define EventWriteETW_MI_ALLOC(Address, Size) \
+ MCGEN_EVENT_ENABLED(ETW_MI_ALLOC) \
+ ? _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC(&ETW_MI_Provider_Context, &ETW_MI_ALLOC, Address, Size) : 0
+#define EventWriteETW_MI_ALLOC_AssumeEnabled(Address, Size) \
+ _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC(&ETW_MI_Provider_Context, &ETW_MI_ALLOC, Address, Size)
+#define EventWriteETW_MI_ALLOC_ForContext(pContext, Address, Size) \
+ MCGEN_EVENT_ENABLED_FORCONTEXT(pContext, ETW_MI_ALLOC) \
+ ? _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC(&(pContext)->Context, &ETW_MI_ALLOC, Address, Size) : 0
+#define EventWriteETW_MI_ALLOC_ForContextAssumeEnabled(pContext, Address, Size) \
+ _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC(&_mcgen_CheckContextType_microsoft_windows_mimalloc(pContext)->Context, &ETW_MI_ALLOC, Address, Size)
+
+// This macro is for use by MC-generated code and should not be used directly.
+#define _mcgen_TEMPLATE_FOR_ETW_MI_ALLOC _mcgen_PASTE2(McTemplateU0xx_, MCGEN_EVENTWRITETRANSFER)
+
+//
+// Enablement check macro for event "ETW_MI_FREE"
+//
+#define EventEnabledETW_MI_FREE() _mcgen_EVENT_BIT_SET(microsoft_windows_mimallocEnableBits, 0)
+#define EventEnabledETW_MI_FREE_ForContext(pContext) _mcgen_EVENT_BIT_SET(_mcgen_CheckContextType_microsoft_windows_mimalloc(pContext)->EnableBits, 0)
+
+//
+// Event write macros for event "ETW_MI_FREE"
+//
+#define EventWriteETW_MI_FREE(Address, Size) \
+ MCGEN_EVENT_ENABLED(ETW_MI_FREE) \
+ ? _mcgen_TEMPLATE_FOR_ETW_MI_FREE(&ETW_MI_Provider_Context, &ETW_MI_FREE, Address, Size) : 0
+#define EventWriteETW_MI_FREE_AssumeEnabled(Address, Size) \
+ _mcgen_TEMPLATE_FOR_ETW_MI_FREE(&ETW_MI_Provider_Context, &ETW_MI_FREE, Address, Size)
+#define EventWriteETW_MI_FREE_ForContext(pContext, Address, Size) \
+ MCGEN_EVENT_ENABLED_FORCONTEXT(pContext, ETW_MI_FREE) \
+ ? _mcgen_TEMPLATE_FOR_ETW_MI_FREE(&(pContext)->Context, &ETW_MI_FREE, Address, Size) : 0
+#define EventWriteETW_MI_FREE_ForContextAssumeEnabled(pContext, Address, Size) \
+ _mcgen_TEMPLATE_FOR_ETW_MI_FREE(&_mcgen_CheckContextType_microsoft_windows_mimalloc(pContext)->Context, &ETW_MI_FREE, Address, Size)
+
+// This macro is for use by MC-generated code and should not be used directly.
+#define _mcgen_TEMPLATE_FOR_ETW_MI_FREE _mcgen_PASTE2(McTemplateU0xx_, MCGEN_EVENTWRITETRANSFER)
+
+#endif // MCGEN_DISABLE_PROVIDER_CODE_GENERATION
+
+//
+// MCGEN_DISABLE_PROVIDER_CODE_GENERATION macro:
+// Define this macro to have the compiler skip the generated functions in this
+// header.
+//
+#ifndef MCGEN_DISABLE_PROVIDER_CODE_GENERATION
+
+//
+// Template Functions
+//
+
+//
+// Function for template "ETW_CUSTOM_HEAP_ALLOC_DATA" (and possibly others).
+// This function is for use by MC-generated code and should not be used directly.
+//
+#ifndef McTemplateU0xx_def
+#define McTemplateU0xx_def
+ETW_INLINE
+ULONG
+_mcgen_PASTE2(McTemplateU0xx_, MCGEN_EVENTWRITETRANSFER)(
+ _In_ PMCGEN_TRACE_CONTEXT Context,
+ _In_ PCEVENT_DESCRIPTOR Descriptor,
+ _In_ const unsigned __int64 _Arg0,
+ _In_ const unsigned __int64 _Arg1
+ )
+{
+#define McTemplateU0xx_ARGCOUNT 2
+
+ EVENT_DATA_DESCRIPTOR EventData[McTemplateU0xx_ARGCOUNT + 1];
+
+ EventDataDescCreate(&EventData[1],&_Arg0, sizeof(const unsigned __int64) );
+
+ EventDataDescCreate(&EventData[2],&_Arg1, sizeof(const unsigned __int64) );
+
+ return McGenEventWrite(Context, Descriptor, NULL, McTemplateU0xx_ARGCOUNT + 1, EventData);
+}
+#endif // McTemplateU0xx_def
+
+#endif // MCGEN_DISABLE_PROVIDER_CODE_GENERATION
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/prim/windows/prim.c b/contrib/tools/python3/Objects/mimalloc/prim/windows/prim.c
new file mode 100644
index 00000000000..a038277ad21
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/prim/windows/prim.c
@@ -0,0 +1,622 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2023, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+// This file is included in `src/prim/prim.c`
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+#include "mimalloc/prim.h"
+#include <stdio.h> // fputs, stderr
+
+
+//---------------------------------------------
+// Dynamically bind Windows API points for portability
+//---------------------------------------------
+
+// We use VirtualAlloc2 for aligned allocation, but it is only supported on Windows 10 and Windows Server 2016.
+// So, we need to look it up dynamically to run on older systems. (use __stdcall for 32-bit compatibility)
+// NtAllocateVirtualAllocEx is used for huge OS page allocation (1GiB)
+// We define a minimal MEM_EXTENDED_PARAMETER ourselves in order to be able to compile with older SDK's.
+typedef enum MI_MEM_EXTENDED_PARAMETER_TYPE_E {
+ MiMemExtendedParameterInvalidType = 0,
+ MiMemExtendedParameterAddressRequirements,
+ MiMemExtendedParameterNumaNode,
+ MiMemExtendedParameterPartitionHandle,
+ MiMemExtendedParameterUserPhysicalHandle,
+ MiMemExtendedParameterAttributeFlags,
+ MiMemExtendedParameterMax
+} MI_MEM_EXTENDED_PARAMETER_TYPE;
+
+typedef struct DECLSPEC_ALIGN(8) MI_MEM_EXTENDED_PARAMETER_S {
+ struct { DWORD64 Type : 8; DWORD64 Reserved : 56; } Type;
+ union { DWORD64 ULong64; PVOID Pointer; SIZE_T Size; HANDLE Handle; DWORD ULong; } Arg;
+} MI_MEM_EXTENDED_PARAMETER;
+
+typedef struct MI_MEM_ADDRESS_REQUIREMENTS_S {
+ PVOID LowestStartingAddress;
+ PVOID HighestEndingAddress;
+ SIZE_T Alignment;
+} MI_MEM_ADDRESS_REQUIREMENTS;
+
+#define MI_MEM_EXTENDED_PARAMETER_NONPAGED_HUGE 0x00000010
+
+#include <winternl.h>
+typedef PVOID (__stdcall *PVirtualAlloc2)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MI_MEM_EXTENDED_PARAMETER*, ULONG);
+typedef NTSTATUS (__stdcall *PNtAllocateVirtualMemoryEx)(HANDLE, PVOID*, SIZE_T*, ULONG, ULONG, MI_MEM_EXTENDED_PARAMETER*, ULONG);
+static PVirtualAlloc2 pVirtualAlloc2 = NULL;
+static PNtAllocateVirtualMemoryEx pNtAllocateVirtualMemoryEx = NULL;
+
+// Similarly, GetNumaProcesorNodeEx is only supported since Windows 7
+typedef struct MI_PROCESSOR_NUMBER_S { WORD Group; BYTE Number; BYTE Reserved; } MI_PROCESSOR_NUMBER;
+
+typedef VOID (__stdcall *PGetCurrentProcessorNumberEx)(MI_PROCESSOR_NUMBER* ProcNumber);
+typedef BOOL (__stdcall *PGetNumaProcessorNodeEx)(MI_PROCESSOR_NUMBER* Processor, PUSHORT NodeNumber);
+typedef BOOL (__stdcall* PGetNumaNodeProcessorMaskEx)(USHORT Node, PGROUP_AFFINITY ProcessorMask);
+typedef BOOL (__stdcall *PGetNumaProcessorNode)(UCHAR Processor, PUCHAR NodeNumber);
+static PGetCurrentProcessorNumberEx pGetCurrentProcessorNumberEx = NULL;
+static PGetNumaProcessorNodeEx pGetNumaProcessorNodeEx = NULL;
+static PGetNumaNodeProcessorMaskEx pGetNumaNodeProcessorMaskEx = NULL;
+static PGetNumaProcessorNode pGetNumaProcessorNode = NULL;
+
+//---------------------------------------------
+// Enable large page support dynamically (if possible)
+//---------------------------------------------
+
+static bool win_enable_large_os_pages(size_t* large_page_size)
+{
+ static bool large_initialized = false;
+ if (large_initialized) return (_mi_os_large_page_size() > 0);
+ large_initialized = true;
+
+ // Try to see if large OS pages are supported
+ // To use large pages on Windows, we first need access permission
+ // Set "Lock pages in memory" permission in the group policy editor
+ // <https://devblogs.microsoft.com/oldnewthing/20110128-00/?p=11643>
+ unsigned long err = 0;
+ HANDLE token = NULL;
+ BOOL ok = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token);
+ if (ok) {
+ TOKEN_PRIVILEGES tp;
+ ok = LookupPrivilegeValue(NULL, TEXT("SeLockMemoryPrivilege"), &tp.Privileges[0].Luid);
+ if (ok) {
+ tp.PrivilegeCount = 1;
+ tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ ok = AdjustTokenPrivileges(token, FALSE, &tp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
+ if (ok) {
+ err = GetLastError();
+ ok = (err == ERROR_SUCCESS);
+ if (ok && large_page_size != NULL) {
+ *large_page_size = GetLargePageMinimum();
+ }
+ }
+ }
+ CloseHandle(token);
+ }
+ if (!ok) {
+ if (err == 0) err = GetLastError();
+ _mi_warning_message("cannot enable large OS page support, error %lu\n", err);
+ }
+ return (ok!=0);
+}
+
+
+//---------------------------------------------
+// Initialize
+//---------------------------------------------
+
+void _mi_prim_mem_init( mi_os_mem_config_t* config )
+{
+ config->has_overcommit = false;
+ config->must_free_whole = true;
+ config->has_virtual_reserve = true;
+ // get the page size
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ if (si.dwPageSize > 0) { config->page_size = si.dwPageSize; }
+ if (si.dwAllocationGranularity > 0) { config->alloc_granularity = si.dwAllocationGranularity; }
+ // get the VirtualAlloc2 function
+ HINSTANCE hDll;
+ hDll = LoadLibrary(TEXT("kernelbase.dll"));
+ if (hDll != NULL) {
+ // use VirtualAlloc2FromApp if possible as it is available to Windows store apps
+ pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2FromApp");
+ if (pVirtualAlloc2==NULL) pVirtualAlloc2 = (PVirtualAlloc2)(void (*)(void))GetProcAddress(hDll, "VirtualAlloc2");
+ FreeLibrary(hDll);
+ }
+ // NtAllocateVirtualMemoryEx is used for huge page allocation
+ hDll = LoadLibrary(TEXT("ntdll.dll"));
+ if (hDll != NULL) {
+ pNtAllocateVirtualMemoryEx = (PNtAllocateVirtualMemoryEx)(void (*)(void))GetProcAddress(hDll, "NtAllocateVirtualMemoryEx");
+ FreeLibrary(hDll);
+ }
+ // Try to use Win7+ numa API
+ hDll = LoadLibrary(TEXT("kernel32.dll"));
+ if (hDll != NULL) {
+ pGetCurrentProcessorNumberEx = (PGetCurrentProcessorNumberEx)(void (*)(void))GetProcAddress(hDll, "GetCurrentProcessorNumberEx");
+ pGetNumaProcessorNodeEx = (PGetNumaProcessorNodeEx)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNodeEx");
+ pGetNumaNodeProcessorMaskEx = (PGetNumaNodeProcessorMaskEx)(void (*)(void))GetProcAddress(hDll, "GetNumaNodeProcessorMaskEx");
+ pGetNumaProcessorNode = (PGetNumaProcessorNode)(void (*)(void))GetProcAddress(hDll, "GetNumaProcessorNode");
+ FreeLibrary(hDll);
+ }
+ if (mi_option_is_enabled(mi_option_allow_large_os_pages) || mi_option_is_enabled(mi_option_reserve_huge_os_pages)) {
+ win_enable_large_os_pages(&config->large_page_size);
+ }
+}
+
+
+//---------------------------------------------
+// Free
+//---------------------------------------------
+
+int _mi_prim_free(void* addr, size_t size ) {
+ MI_UNUSED(size);
+ DWORD errcode = 0;
+ bool err = (VirtualFree(addr, 0, MEM_RELEASE) == 0);
+ if (err) { errcode = GetLastError(); }
+ if (errcode == ERROR_INVALID_ADDRESS) {
+ // In mi_os_mem_alloc_aligned the fallback path may have returned a pointer inside
+ // the memory region returned by VirtualAlloc; in that case we need to free using
+ // the start of the region.
+ MEMORY_BASIC_INFORMATION info = { 0 };
+ VirtualQuery(addr, &info, sizeof(info));
+ if (info.AllocationBase < addr && ((uint8_t*)addr - (uint8_t*)info.AllocationBase) < (ptrdiff_t)MI_SEGMENT_SIZE) {
+ errcode = 0;
+ err = (VirtualFree(info.AllocationBase, 0, MEM_RELEASE) == 0);
+ if (err) { errcode = GetLastError(); }
+ }
+ }
+ return (int)errcode;
+}
+
+
+//---------------------------------------------
+// VirtualAlloc
+//---------------------------------------------
+
+static void* win_virtual_alloc_prim(void* addr, size_t size, size_t try_alignment, DWORD flags) {
+ #if (MI_INTPTR_SIZE >= 8)
+ // on 64-bit systems, try to use the virtual address area after 2TiB for 4MiB aligned allocations
+ if (addr == NULL) {
+ void* hint = _mi_os_get_aligned_hint(try_alignment,size);
+ if (hint != NULL) {
+ void* p = VirtualAlloc(hint, size, flags, PAGE_READWRITE);
+ if (p != NULL) return p;
+ _mi_verbose_message("warning: unable to allocate hinted aligned OS memory (%zu bytes, error code: 0x%x, address: %p, alignment: %zu, flags: 0x%x)\n", size, GetLastError(), hint, try_alignment, flags);
+ // fall through on error
+ }
+ }
+ #endif
+ // on modern Windows try use VirtualAlloc2 for aligned allocation
+ if (try_alignment > 1 && (try_alignment % _mi_os_page_size()) == 0 && pVirtualAlloc2 != NULL) {
+ MI_MEM_ADDRESS_REQUIREMENTS reqs = { 0, 0, 0 };
+ reqs.Alignment = try_alignment;
+ MI_MEM_EXTENDED_PARAMETER param = { {0, 0}, {0} };
+ param.Type.Type = MiMemExtendedParameterAddressRequirements;
+ param.Arg.Pointer = &reqs;
+ void* p = (*pVirtualAlloc2)(GetCurrentProcess(), addr, size, flags, PAGE_READWRITE, &param, 1);
+ if (p != NULL) return p;
+ _mi_warning_message("unable to allocate aligned OS memory (%zu bytes, error code: 0x%x, address: %p, alignment: %zu, flags: 0x%x)\n", size, GetLastError(), addr, try_alignment, flags);
+ // fall through on error
+ }
+ // last resort
+ return VirtualAlloc(addr, size, flags, PAGE_READWRITE);
+}
+
+static void* win_virtual_alloc(void* addr, size_t size, size_t try_alignment, DWORD flags, bool large_only, bool allow_large, bool* is_large) {
+ mi_assert_internal(!(large_only && !allow_large));
+ static _Atomic(size_t) large_page_try_ok; // = 0;
+ void* p = NULL;
+ // Try to allocate large OS pages (2MiB) if allowed or required.
+ if ((large_only || _mi_os_use_large_page(size, try_alignment))
+ && allow_large && (flags&MEM_COMMIT)!=0 && (flags&MEM_RESERVE)!=0) {
+ size_t try_ok = mi_atomic_load_acquire(&large_page_try_ok);
+ if (!large_only && try_ok > 0) {
+ // if a large page allocation fails, it seems the calls to VirtualAlloc get very expensive.
+ // therefore, once a large page allocation failed, we don't try again for `large_page_try_ok` times.
+ mi_atomic_cas_strong_acq_rel(&large_page_try_ok, &try_ok, try_ok - 1);
+ }
+ else {
+ // large OS pages must always reserve and commit.
+ *is_large = true;
+ p = win_virtual_alloc_prim(addr, size, try_alignment, flags | MEM_LARGE_PAGES);
+ if (large_only) return p;
+ // fall back to non-large page allocation on error (`p == NULL`).
+ if (p == NULL) {
+ mi_atomic_store_release(&large_page_try_ok,10UL); // on error, don't try again for the next N allocations
+ }
+ }
+ }
+ // Fall back to regular page allocation
+ if (p == NULL) {
+ *is_large = ((flags&MEM_LARGE_PAGES) != 0);
+ p = win_virtual_alloc_prim(addr, size, try_alignment, flags);
+ }
+ //if (p == NULL) { _mi_warning_message("unable to allocate OS memory (%zu bytes, error code: 0x%x, address: %p, alignment: %zu, flags: 0x%x, large only: %d, allow large: %d)\n", size, GetLastError(), addr, try_alignment, flags, large_only, allow_large); }
+ return p;
+}
+
+int _mi_prim_alloc(size_t size, size_t try_alignment, bool commit, bool allow_large, bool* is_large, bool* is_zero, void** addr) {
+ mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0);
+ mi_assert_internal(commit || !allow_large);
+ mi_assert_internal(try_alignment > 0);
+ *is_zero = true;
+ int flags = MEM_RESERVE;
+ if (commit) { flags |= MEM_COMMIT; }
+ *addr = win_virtual_alloc(NULL, size, try_alignment, flags, false, allow_large, is_large);
+ return (*addr != NULL ? 0 : (int)GetLastError());
+}
+
+
+//---------------------------------------------
+// Commit/Reset/Protect
+//---------------------------------------------
+#ifdef _MSC_VER
+#pragma warning(disable:6250) // suppress warning calling VirtualFree without MEM_RELEASE (for decommit)
+#endif
+
+int _mi_prim_commit(void* addr, size_t size, bool* is_zero) {
+ *is_zero = false;
+ /*
+ // zero'ing only happens on an initial commit... but checking upfront seems expensive..
+ _MEMORY_BASIC_INFORMATION meminfo; _mi_memzero_var(meminfo);
+ if (VirtualQuery(addr, &meminfo, size) > 0) {
+ if ((meminfo.State & MEM_COMMIT) == 0) {
+ *is_zero = true;
+ }
+ }
+ */
+ // commit
+ void* p = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE);
+ if (p == NULL) return (int)GetLastError();
+ return 0;
+}
+
+int _mi_prim_decommit(void* addr, size_t size, bool* needs_recommit) {
+ BOOL ok = VirtualFree(addr, size, MEM_DECOMMIT);
+ *needs_recommit = true; // for safety, assume always decommitted even in the case of an error.
+ return (ok ? 0 : (int)GetLastError());
+}
+
+int _mi_prim_reset(void* addr, size_t size) {
+ void* p = VirtualAlloc(addr, size, MEM_RESET, PAGE_READWRITE);
+ mi_assert_internal(p == addr);
+ #if 0
+ if (p != NULL) {
+ VirtualUnlock(addr,size); // VirtualUnlock after MEM_RESET removes the memory directly from the working set
+ }
+ #endif
+ return (p != NULL ? 0 : (int)GetLastError());
+}
+
+int _mi_prim_protect(void* addr, size_t size, bool protect) {
+ DWORD oldprotect = 0;
+ BOOL ok = VirtualProtect(addr, size, protect ? PAGE_NOACCESS : PAGE_READWRITE, &oldprotect);
+ return (ok ? 0 : (int)GetLastError());
+}
+
+
+//---------------------------------------------
+// Huge page allocation
+//---------------------------------------------
+
+static void* _mi_prim_alloc_huge_os_pagesx(void* hint_addr, size_t size, int numa_node)
+{
+ const DWORD flags = MEM_LARGE_PAGES | MEM_COMMIT | MEM_RESERVE;
+
+ win_enable_large_os_pages(NULL);
+
+ MI_MEM_EXTENDED_PARAMETER params[3] = { {{0,0},{0}},{{0,0},{0}},{{0,0},{0}} };
+ // on modern Windows try use NtAllocateVirtualMemoryEx for 1GiB huge pages
+ static bool mi_huge_pages_available = true;
+ if (pNtAllocateVirtualMemoryEx != NULL && mi_huge_pages_available) {
+ params[0].Type.Type = MiMemExtendedParameterAttributeFlags;
+ params[0].Arg.ULong64 = MI_MEM_EXTENDED_PARAMETER_NONPAGED_HUGE;
+ ULONG param_count = 1;
+ if (numa_node >= 0) {
+ param_count++;
+ params[1].Type.Type = MiMemExtendedParameterNumaNode;
+ params[1].Arg.ULong = (unsigned)numa_node;
+ }
+ SIZE_T psize = size;
+ void* base = hint_addr;
+ NTSTATUS err = (*pNtAllocateVirtualMemoryEx)(GetCurrentProcess(), &base, &psize, flags, PAGE_READWRITE, params, param_count);
+ if (err == 0 && base != NULL) {
+ return base;
+ }
+ else {
+ // fall back to regular large pages
+ mi_huge_pages_available = false; // don't try further huge pages
+ _mi_warning_message("unable to allocate using huge (1GiB) pages, trying large (2MiB) pages instead (status 0x%lx)\n", err);
+ }
+ }
+ // on modern Windows try use VirtualAlloc2 for numa aware large OS page allocation
+ if (pVirtualAlloc2 != NULL && numa_node >= 0) {
+ params[0].Type.Type = MiMemExtendedParameterNumaNode;
+ params[0].Arg.ULong = (unsigned)numa_node;
+ return (*pVirtualAlloc2)(GetCurrentProcess(), hint_addr, size, flags, PAGE_READWRITE, params, 1);
+ }
+
+ // otherwise use regular virtual alloc on older windows
+ return VirtualAlloc(hint_addr, size, flags, PAGE_READWRITE);
+}
+
+int _mi_prim_alloc_huge_os_pages(void* hint_addr, size_t size, int numa_node, bool* is_zero, void** addr) {
+ *is_zero = true;
+ *addr = _mi_prim_alloc_huge_os_pagesx(hint_addr,size,numa_node);
+ return (*addr != NULL ? 0 : (int)GetLastError());
+}
+
+
+//---------------------------------------------
+// Numa nodes
+//---------------------------------------------
+
+size_t _mi_prim_numa_node(void) {
+ USHORT numa_node = 0;
+ if (pGetCurrentProcessorNumberEx != NULL && pGetNumaProcessorNodeEx != NULL) {
+ // Extended API is supported
+ MI_PROCESSOR_NUMBER pnum;
+ (*pGetCurrentProcessorNumberEx)(&pnum);
+ USHORT nnode = 0;
+ BOOL ok = (*pGetNumaProcessorNodeEx)(&pnum, &nnode);
+ if (ok) { numa_node = nnode; }
+ }
+ else if (pGetNumaProcessorNode != NULL) {
+ // Vista or earlier, use older API that is limited to 64 processors. Issue #277
+ DWORD pnum = GetCurrentProcessorNumber();
+ UCHAR nnode = 0;
+ BOOL ok = pGetNumaProcessorNode((UCHAR)pnum, &nnode);
+ if (ok) { numa_node = nnode; }
+ }
+ return numa_node;
+}
+
+size_t _mi_prim_numa_node_count(void) {
+ ULONG numa_max = 0;
+ GetNumaHighestNodeNumber(&numa_max);
+ // find the highest node number that has actual processors assigned to it. Issue #282
+ while(numa_max > 0) {
+ if (pGetNumaNodeProcessorMaskEx != NULL) {
+ // Extended API is supported
+ GROUP_AFFINITY affinity;
+ if ((*pGetNumaNodeProcessorMaskEx)((USHORT)numa_max, &affinity)) {
+ if (affinity.Mask != 0) break; // found the maximum non-empty node
+ }
+ }
+ else {
+ // Vista or earlier, use older API that is limited to 64 processors.
+ ULONGLONG mask;
+ if (GetNumaNodeProcessorMask((UCHAR)numa_max, &mask)) {
+ if (mask != 0) break; // found the maximum non-empty node
+ };
+ }
+ // max node was invalid or had no processor assigned, try again
+ numa_max--;
+ }
+ return ((size_t)numa_max + 1);
+}
+
+
+//----------------------------------------------------------------
+// Clock
+//----------------------------------------------------------------
+
+static mi_msecs_t mi_to_msecs(LARGE_INTEGER t) {
+ static LARGE_INTEGER mfreq; // = 0
+ if (mfreq.QuadPart == 0LL) {
+ LARGE_INTEGER f;
+ QueryPerformanceFrequency(&f);
+ mfreq.QuadPart = f.QuadPart/1000LL;
+ if (mfreq.QuadPart == 0) mfreq.QuadPart = 1;
+ }
+ return (mi_msecs_t)(t.QuadPart / mfreq.QuadPart);
+}
+
+mi_msecs_t _mi_prim_clock_now(void) {
+ LARGE_INTEGER t;
+ QueryPerformanceCounter(&t);
+ return mi_to_msecs(t);
+}
+
+
+//----------------------------------------------------------------
+// Process Info
+//----------------------------------------------------------------
+
+#include <windows.h>
+#include <psapi.h>
+
+static mi_msecs_t filetime_msecs(const FILETIME* ftime) {
+ ULARGE_INTEGER i;
+ i.LowPart = ftime->dwLowDateTime;
+ i.HighPart = ftime->dwHighDateTime;
+ mi_msecs_t msecs = (i.QuadPart / 10000); // FILETIME is in 100 nano seconds
+ return msecs;
+}
+
+typedef BOOL (WINAPI *PGetProcessMemoryInfo)(HANDLE, PPROCESS_MEMORY_COUNTERS, DWORD);
+static PGetProcessMemoryInfo pGetProcessMemoryInfo = NULL;
+
+void _mi_prim_process_info(mi_process_info_t* pinfo)
+{
+ FILETIME ct;
+ FILETIME ut;
+ FILETIME st;
+ FILETIME et;
+ GetProcessTimes(GetCurrentProcess(), &ct, &et, &st, &ut);
+ pinfo->utime = filetime_msecs(&ut);
+ pinfo->stime = filetime_msecs(&st);
+
+ // load psapi on demand
+ if (pGetProcessMemoryInfo == NULL) {
+ HINSTANCE hDll = LoadLibrary(TEXT("psapi.dll"));
+ if (hDll != NULL) {
+ pGetProcessMemoryInfo = (PGetProcessMemoryInfo)(void (*)(void))GetProcAddress(hDll, "GetProcessMemoryInfo");
+ }
+ }
+
+ // get process info
+ PROCESS_MEMORY_COUNTERS info;
+ memset(&info, 0, sizeof(info));
+ if (pGetProcessMemoryInfo != NULL) {
+ pGetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
+ }
+ pinfo->current_rss = (size_t)info.WorkingSetSize;
+ pinfo->peak_rss = (size_t)info.PeakWorkingSetSize;
+ pinfo->current_commit = (size_t)info.PagefileUsage;
+ pinfo->peak_commit = (size_t)info.PeakPagefileUsage;
+ pinfo->page_faults = (size_t)info.PageFaultCount;
+}
+
+//----------------------------------------------------------------
+// Output
+//----------------------------------------------------------------
+
+void _mi_prim_out_stderr( const char* msg )
+{
+ // on windows with redirection, the C runtime cannot handle locale dependent output
+ // after the main thread closes so we use direct console output.
+ if (!_mi_preloading()) {
+ // _cputs(msg); // _cputs cannot be used at is aborts if it fails to lock the console
+ static HANDLE hcon = INVALID_HANDLE_VALUE;
+ static bool hconIsConsole;
+ if (hcon == INVALID_HANDLE_VALUE) {
+ CONSOLE_SCREEN_BUFFER_INFO sbi;
+ hcon = GetStdHandle(STD_ERROR_HANDLE);
+ hconIsConsole = ((hcon != INVALID_HANDLE_VALUE) && GetConsoleScreenBufferInfo(hcon, &sbi));
+ }
+ const size_t len = _mi_strlen(msg);
+ if (len > 0 && len < UINT32_MAX) {
+ DWORD written = 0;
+ if (hconIsConsole) {
+ WriteConsoleA(hcon, msg, (DWORD)len, &written, NULL);
+ }
+ else if (hcon != INVALID_HANDLE_VALUE) {
+ // use direct write if stderr was redirected
+ WriteFile(hcon, msg, (DWORD)len, &written, NULL);
+ }
+ else {
+ // finally fall back to fputs after all
+ fputs(msg, stderr);
+ }
+ }
+ }
+}
+
+
+//----------------------------------------------------------------
+// Environment
+//----------------------------------------------------------------
+
+// On Windows use GetEnvironmentVariable instead of getenv to work
+// reliably even when this is invoked before the C runtime is initialized.
+// i.e. when `_mi_preloading() == true`.
+// Note: on windows, environment names are not case sensitive.
+bool _mi_prim_getenv(const char* name, char* result, size_t result_size) {
+ result[0] = 0;
+ size_t len = GetEnvironmentVariableA(name, result, (DWORD)result_size);
+ return (len > 0 && len < result_size);
+}
+
+
+
+//----------------------------------------------------------------
+// Random
+//----------------------------------------------------------------
+
+#if defined(MI_USE_RTLGENRANDOM) // || defined(__cplusplus)
+// We prefer to use BCryptGenRandom instead of (the unofficial) RtlGenRandom but when using
+// dynamic overriding, we observed it can raise an exception when compiled with C++, and
+// sometimes deadlocks when also running under the VS debugger.
+// In contrast, issue #623 implies that on Windows Server 2019 we need to use BCryptGenRandom.
+// To be continued..
+#pragma comment (lib,"advapi32.lib")
+#define RtlGenRandom SystemFunction036
+mi_decl_externc BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
+
+bool _mi_prim_random_buf(void* buf, size_t buf_len) {
+ return (RtlGenRandom(buf, (ULONG)buf_len) != 0);
+}
+
+#else
+
+#ifndef BCRYPT_USE_SYSTEM_PREFERRED_RNG
+#define BCRYPT_USE_SYSTEM_PREFERRED_RNG 0x00000002
+#endif
+
+typedef LONG (NTAPI *PBCryptGenRandom)(HANDLE, PUCHAR, ULONG, ULONG);
+static PBCryptGenRandom pBCryptGenRandom = NULL;
+
+bool _mi_prim_random_buf(void* buf, size_t buf_len) {
+ if (pBCryptGenRandom == NULL) {
+ HINSTANCE hDll = LoadLibrary(TEXT("bcrypt.dll"));
+ if (hDll != NULL) {
+ pBCryptGenRandom = (PBCryptGenRandom)(void (*)(void))GetProcAddress(hDll, "BCryptGenRandom");
+ }
+ if (pBCryptGenRandom == NULL) return false;
+ }
+ return (pBCryptGenRandom(NULL, (PUCHAR)buf, (ULONG)buf_len, BCRYPT_USE_SYSTEM_PREFERRED_RNG) >= 0);
+}
+
+#endif // MI_USE_RTLGENRANDOM
+
+//----------------------------------------------------------------
+// Thread init/done
+//----------------------------------------------------------------
+
+#if !defined(MI_SHARED_LIB)
+
+// use thread local storage keys to detect thread ending
+#include <fibersapi.h>
+#if (_WIN32_WINNT < 0x600) // before Windows Vista
+WINBASEAPI DWORD WINAPI FlsAlloc( _In_opt_ PFLS_CALLBACK_FUNCTION lpCallback );
+WINBASEAPI PVOID WINAPI FlsGetValue( _In_ DWORD dwFlsIndex );
+WINBASEAPI BOOL WINAPI FlsSetValue( _In_ DWORD dwFlsIndex, _In_opt_ PVOID lpFlsData );
+WINBASEAPI BOOL WINAPI FlsFree(_In_ DWORD dwFlsIndex);
+#endif
+
+static DWORD mi_fls_key = (DWORD)(-1);
+
+static void NTAPI mi_fls_done(PVOID value) {
+ mi_heap_t* heap = (mi_heap_t*)value;
+ if (heap != NULL) {
+ _mi_thread_done(heap);
+ FlsSetValue(mi_fls_key, NULL); // prevent recursion as _mi_thread_done may set it back to the main heap, issue #672
+ }
+}
+
+void _mi_prim_thread_init_auto_done(void) {
+ mi_fls_key = FlsAlloc(&mi_fls_done);
+}
+
+void _mi_prim_thread_done_auto_done(void) {
+ // call thread-done on all threads (except the main thread) to prevent
+ // dangling callback pointer if statically linked with a DLL; Issue #208
+ FlsFree(mi_fls_key);
+}
+
+void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
+ mi_assert_internal(mi_fls_key != (DWORD)(-1));
+ FlsSetValue(mi_fls_key, heap);
+}
+
+#else
+
+// Dll; nothing to do as in that case thread_done is handled through the DLL_THREAD_DETACH event.
+
+void _mi_prim_thread_init_auto_done(void) {
+}
+
+void _mi_prim_thread_done_auto_done(void) {
+}
+
+void _mi_prim_thread_associate_default_heap(mi_heap_t* heap) {
+ MI_UNUSED(heap);
+}
+
+#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/random.c b/contrib/tools/python3/Objects/mimalloc/random.c
new file mode 100644
index 00000000000..35a0633a800
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/random.c
@@ -0,0 +1,254 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019-2021, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/prim.h" // _mi_prim_random_buf
+#include <string.h> // memset
+
+/* ----------------------------------------------------------------------------
+We use our own PRNG to keep predictable performance of random number generation
+and to avoid implementations that use a lock. We only use the OS provided
+random source to initialize the initial seeds. Since we do not need ultimate
+performance but we do rely on the security (for secret cookies in secure mode)
+we use a cryptographically secure generator (chacha20).
+-----------------------------------------------------------------------------*/
+
+#define MI_CHACHA_ROUNDS (20) // perhaps use 12 for better performance?
+
+
+/* ----------------------------------------------------------------------------
+Chacha20 implementation as the original algorithm with a 64-bit nonce
+and counter: https://en.wikipedia.org/wiki/Salsa20
+The input matrix has sixteen 32-bit values:
+Position 0 to 3: constant key
+Position 4 to 11: the key
+Position 12 to 13: the counter.
+Position 14 to 15: the nonce.
+
+The implementation uses regular C code which compiles very well on modern compilers.
+(gcc x64 has no register spills, and clang 6+ uses SSE instructions)
+-----------------------------------------------------------------------------*/
+
+static inline uint32_t rotl(uint32_t x, uint32_t shift) {
+ return (x << shift) | (x >> (32 - shift));
+}
+
+static inline void qround(uint32_t x[16], size_t a, size_t b, size_t c, size_t d) {
+ x[a] += x[b]; x[d] = rotl(x[d] ^ x[a], 16);
+ x[c] += x[d]; x[b] = rotl(x[b] ^ x[c], 12);
+ x[a] += x[b]; x[d] = rotl(x[d] ^ x[a], 8);
+ x[c] += x[d]; x[b] = rotl(x[b] ^ x[c], 7);
+}
+
+static void chacha_block(mi_random_ctx_t* ctx)
+{
+ // scramble into `x`
+ uint32_t x[16];
+ for (size_t i = 0; i < 16; i++) {
+ x[i] = ctx->input[i];
+ }
+ for (size_t i = 0; i < MI_CHACHA_ROUNDS; i += 2) {
+ qround(x, 0, 4, 8, 12);
+ qround(x, 1, 5, 9, 13);
+ qround(x, 2, 6, 10, 14);
+ qround(x, 3, 7, 11, 15);
+ qround(x, 0, 5, 10, 15);
+ qround(x, 1, 6, 11, 12);
+ qround(x, 2, 7, 8, 13);
+ qround(x, 3, 4, 9, 14);
+ }
+
+ // add scrambled data to the initial state
+ for (size_t i = 0; i < 16; i++) {
+ ctx->output[i] = x[i] + ctx->input[i];
+ }
+ ctx->output_available = 16;
+
+ // increment the counter for the next round
+ ctx->input[12] += 1;
+ if (ctx->input[12] == 0) {
+ ctx->input[13] += 1;
+ if (ctx->input[13] == 0) { // and keep increasing into the nonce
+ ctx->input[14] += 1;
+ }
+ }
+}
+
+static uint32_t chacha_next32(mi_random_ctx_t* ctx) {
+ if (ctx->output_available <= 0) {
+ chacha_block(ctx);
+ ctx->output_available = 16; // (assign again to suppress static analysis warning)
+ }
+ const uint32_t x = ctx->output[16 - ctx->output_available];
+ ctx->output[16 - ctx->output_available] = 0; // reset once the data is handed out
+ ctx->output_available--;
+ return x;
+}
+
+static inline uint32_t read32(const uint8_t* p, size_t idx32) {
+ const size_t i = 4*idx32;
+ return ((uint32_t)p[i+0] | (uint32_t)p[i+1] << 8 | (uint32_t)p[i+2] << 16 | (uint32_t)p[i+3] << 24);
+}
+
+static void chacha_init(mi_random_ctx_t* ctx, const uint8_t key[32], uint64_t nonce)
+{
+ // since we only use chacha for randomness (and not encryption) we
+ // do not _need_ to read 32-bit values as little endian but we do anyways
+ // just for being compatible :-)
+ memset(ctx, 0, sizeof(*ctx));
+ for (size_t i = 0; i < 4; i++) {
+ const uint8_t* sigma = (uint8_t*)"expand 32-byte k";
+ ctx->input[i] = read32(sigma,i);
+ }
+ for (size_t i = 0; i < 8; i++) {
+ ctx->input[i + 4] = read32(key,i);
+ }
+ ctx->input[12] = 0;
+ ctx->input[13] = 0;
+ ctx->input[14] = (uint32_t)nonce;
+ ctx->input[15] = (uint32_t)(nonce >> 32);
+}
+
+static void chacha_split(mi_random_ctx_t* ctx, uint64_t nonce, mi_random_ctx_t* ctx_new) {
+ memset(ctx_new, 0, sizeof(*ctx_new));
+ _mi_memcpy(ctx_new->input, ctx->input, sizeof(ctx_new->input));
+ ctx_new->input[12] = 0;
+ ctx_new->input[13] = 0;
+ ctx_new->input[14] = (uint32_t)nonce;
+ ctx_new->input[15] = (uint32_t)(nonce >> 32);
+ mi_assert_internal(ctx->input[14] != ctx_new->input[14] || ctx->input[15] != ctx_new->input[15]); // do not reuse nonces!
+ chacha_block(ctx_new);
+}
+
+
+/* ----------------------------------------------------------------------------
+Random interface
+-----------------------------------------------------------------------------*/
+
+#if MI_DEBUG>1
+static bool mi_random_is_initialized(mi_random_ctx_t* ctx) {
+ return (ctx != NULL && ctx->input[0] != 0);
+}
+#endif
+
+void _mi_random_split(mi_random_ctx_t* ctx, mi_random_ctx_t* ctx_new) {
+ mi_assert_internal(mi_random_is_initialized(ctx));
+ mi_assert_internal(ctx != ctx_new);
+ chacha_split(ctx, (uintptr_t)ctx_new /*nonce*/, ctx_new);
+}
+
+uintptr_t _mi_random_next(mi_random_ctx_t* ctx) {
+ mi_assert_internal(mi_random_is_initialized(ctx));
+ #if MI_INTPTR_SIZE <= 4
+ return chacha_next32(ctx);
+ #elif MI_INTPTR_SIZE == 8
+ return (((uintptr_t)chacha_next32(ctx) << 32) | chacha_next32(ctx));
+ #else
+ # error "define mi_random_next for this platform"
+ #endif
+}
+
+
+/* ----------------------------------------------------------------------------
+To initialize a fresh random context.
+If we cannot get good randomness, we fall back to weak randomness based on a timer and ASLR.
+-----------------------------------------------------------------------------*/
+
+uintptr_t _mi_os_random_weak(uintptr_t extra_seed) {
+ uintptr_t x = (uintptr_t)&_mi_os_random_weak ^ extra_seed; // ASLR makes the address random
+ x ^= _mi_prim_clock_now();
+ // and do a few randomization steps
+ uintptr_t max = ((x ^ (x >> 17)) & 0x0F) + 1;
+ for (uintptr_t i = 0; i < max; i++) {
+ x = _mi_random_shuffle(x);
+ }
+ mi_assert_internal(x != 0);
+ return x;
+}
+
+static void mi_random_init_ex(mi_random_ctx_t* ctx, bool use_weak) {
+ uint8_t key[32] = {0};
+ if (use_weak || !_mi_prim_random_buf(key, sizeof(key))) {
+ // if we fail to get random data from the OS, we fall back to a
+ // weak random source based on the current time
+ #if !defined(__wasi__)
+ if (!use_weak) { _mi_warning_message("unable to use secure randomness\n"); }
+ #endif
+ uintptr_t x = _mi_os_random_weak(0);
+ for (size_t i = 0; i < 8; i++) { // key is eight 32-bit words.
+ x = _mi_random_shuffle(x);
+ ((uint32_t*)key)[i] = (uint32_t)x;
+ }
+ ctx->weak = true;
+ }
+ else {
+ ctx->weak = false;
+ }
+ chacha_init(ctx, key, (uintptr_t)ctx /*nonce*/ );
+}
+
+void _mi_random_init(mi_random_ctx_t* ctx) {
+ mi_random_init_ex(ctx, false);
+}
+
+void _mi_random_init_weak(mi_random_ctx_t * ctx) {
+ mi_random_init_ex(ctx, true);
+}
+
+void _mi_random_reinit_if_weak(mi_random_ctx_t * ctx) {
+ if (ctx->weak) {
+ _mi_random_init(ctx);
+ }
+}
+
+/* --------------------------------------------------------
+test vectors from <https://tools.ietf.org/html/rfc8439>
+----------------------------------------------------------- */
+/*
+static bool array_equals(uint32_t* x, uint32_t* y, size_t n) {
+ for (size_t i = 0; i < n; i++) {
+ if (x[i] != y[i]) return false;
+ }
+ return true;
+}
+static void chacha_test(void)
+{
+ uint32_t x[4] = { 0x11111111, 0x01020304, 0x9b8d6f43, 0x01234567 };
+ uint32_t x_out[4] = { 0xea2a92f4, 0xcb1cf8ce, 0x4581472e, 0x5881c4bb };
+ qround(x, 0, 1, 2, 3);
+ mi_assert_internal(array_equals(x, x_out, 4));
+
+ uint32_t y[16] = {
+ 0x879531e0, 0xc5ecf37d, 0x516461b1, 0xc9a62f8a,
+ 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0x2a5f714c,
+ 0x53372767, 0xb00a5631, 0x974c541a, 0x359e9963,
+ 0x5c971061, 0x3d631689, 0x2098d9d6, 0x91dbd320 };
+ uint32_t y_out[16] = {
+ 0x879531e0, 0xc5ecf37d, 0xbdb886dc, 0xc9a62f8a,
+ 0x44c20ef3, 0x3390af7f, 0xd9fc690b, 0xcfacafd2,
+ 0xe46bea80, 0xb00a5631, 0x974c541a, 0x359e9963,
+ 0x5c971061, 0xccc07c79, 0x2098d9d6, 0x91dbd320 };
+ qround(y, 2, 7, 8, 13);
+ mi_assert_internal(array_equals(y, y_out, 16));
+
+ mi_random_ctx_t r = {
+ { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574,
+ 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c,
+ 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c,
+ 0x00000001, 0x09000000, 0x4a000000, 0x00000000 },
+ {0},
+ 0
+ };
+ uint32_t r_out[16] = {
+ 0xe4e7f110, 0x15593bd1, 0x1fdd0f50, 0xc47120a3,
+ 0xc7f4d1c7, 0x0368c033, 0x9aaa2204, 0x4e6cd4c3,
+ 0x466482d2, 0x09aa9f07, 0x05d7c214, 0xa2028bd9,
+ 0xd19c12b5, 0xb94e16de, 0xe883d0cb, 0x4e3c50a2 };
+ chacha_block(&r);
+ mi_assert_internal(array_equals(r.output, r_out, 16));
+}
+*/
diff --git a/contrib/tools/python3/Objects/mimalloc/segment-map.c b/contrib/tools/python3/Objects/mimalloc/segment-map.c
new file mode 100644
index 00000000000..5141f2e1b1d
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/segment-map.c
@@ -0,0 +1,153 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2019-2023, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+
+/* -----------------------------------------------------------
+ The following functions are to reliably find the segment or
+ block that encompasses any pointer p (or NULL if it is not
+ in any of our segments).
+ We maintain a bitmap of all memory with 1 bit per MI_SEGMENT_SIZE (64MiB)
+ set to 1 if it contains the segment meta data.
+----------------------------------------------------------- */
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+
+#if (MI_INTPTR_SIZE==8)
+#define MI_MAX_ADDRESS ((size_t)40 << 40) // 40TB (to include huge page areas)
+#else
+#define MI_MAX_ADDRESS ((size_t)2 << 30) // 2Gb
+#endif
+
+#define MI_SEGMENT_MAP_BITS (MI_MAX_ADDRESS / MI_SEGMENT_SIZE)
+#define MI_SEGMENT_MAP_SIZE (MI_SEGMENT_MAP_BITS / 8)
+#define MI_SEGMENT_MAP_WSIZE (MI_SEGMENT_MAP_SIZE / MI_INTPTR_SIZE)
+
+static _Atomic(uintptr_t) mi_segment_map[MI_SEGMENT_MAP_WSIZE + 1]; // 2KiB per TB with 64MiB segments
+
+static size_t mi_segment_map_index_of(const mi_segment_t* segment, size_t* bitidx) {
+ mi_assert_internal(_mi_ptr_segment(segment + 1) == segment); // is it aligned on MI_SEGMENT_SIZE?
+ if ((uintptr_t)segment >= MI_MAX_ADDRESS) {
+ *bitidx = 0;
+ return MI_SEGMENT_MAP_WSIZE;
+ }
+ else {
+ const uintptr_t segindex = ((uintptr_t)segment) / MI_SEGMENT_SIZE;
+ *bitidx = segindex % MI_INTPTR_BITS;
+ const size_t mapindex = segindex / MI_INTPTR_BITS;
+ mi_assert_internal(mapindex < MI_SEGMENT_MAP_WSIZE);
+ return mapindex;
+ }
+}
+
+void _mi_segment_map_allocated_at(const mi_segment_t* segment) {
+ size_t bitidx;
+ size_t index = mi_segment_map_index_of(segment, &bitidx);
+ mi_assert_internal(index <= MI_SEGMENT_MAP_WSIZE);
+ if (index==MI_SEGMENT_MAP_WSIZE) return;
+ uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]);
+ uintptr_t newmask;
+ do {
+ newmask = (mask | ((uintptr_t)1 << bitidx));
+ } while (!mi_atomic_cas_weak_release(&mi_segment_map[index], &mask, newmask));
+}
+
+void _mi_segment_map_freed_at(const mi_segment_t* segment) {
+ size_t bitidx;
+ size_t index = mi_segment_map_index_of(segment, &bitidx);
+ mi_assert_internal(index <= MI_SEGMENT_MAP_WSIZE);
+ if (index == MI_SEGMENT_MAP_WSIZE) return;
+ uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]);
+ uintptr_t newmask;
+ do {
+ newmask = (mask & ~((uintptr_t)1 << bitidx));
+ } while (!mi_atomic_cas_weak_release(&mi_segment_map[index], &mask, newmask));
+}
+
+// Determine the segment belonging to a pointer or NULL if it is not in a valid segment.
+static mi_segment_t* _mi_segment_of(const void* p) {
+ if (p == NULL) return NULL;
+ mi_segment_t* segment = _mi_ptr_segment(p);
+ mi_assert_internal(segment != NULL);
+ size_t bitidx;
+ size_t index = mi_segment_map_index_of(segment, &bitidx);
+ // fast path: for any pointer to valid small/medium/large object or first MI_SEGMENT_SIZE in huge
+ const uintptr_t mask = mi_atomic_load_relaxed(&mi_segment_map[index]);
+ if mi_likely((mask & ((uintptr_t)1 << bitidx)) != 0) {
+ return segment; // yes, allocated by us
+ }
+ if (index==MI_SEGMENT_MAP_WSIZE) return NULL;
+
+ // TODO: maintain max/min allocated range for efficiency for more efficient rejection of invalid pointers?
+
+ // search downwards for the first segment in case it is an interior pointer
+ // could be slow but searches in MI_INTPTR_SIZE * MI_SEGMENT_SIZE (512MiB) steps through
+ // valid huge objects
+ // note: we could maintain a lowest index to speed up the path for invalid pointers?
+ size_t lobitidx;
+ size_t loindex;
+ uintptr_t lobits = mask & (((uintptr_t)1 << bitidx) - 1);
+ if (lobits != 0) {
+ loindex = index;
+ lobitidx = mi_bsr(lobits); // lobits != 0
+ }
+ else if (index == 0) {
+ return NULL;
+ }
+ else {
+ mi_assert_internal(index > 0);
+ uintptr_t lomask = mask;
+ loindex = index;
+ do {
+ loindex--;
+ lomask = mi_atomic_load_relaxed(&mi_segment_map[loindex]);
+ } while (lomask != 0 && loindex > 0);
+ if (lomask == 0) return NULL;
+ lobitidx = mi_bsr(lomask); // lomask != 0
+ }
+ mi_assert_internal(loindex < MI_SEGMENT_MAP_WSIZE);
+ // take difference as the addresses could be larger than the MAX_ADDRESS space.
+ size_t diff = (((index - loindex) * (8*MI_INTPTR_SIZE)) + bitidx - lobitidx) * MI_SEGMENT_SIZE;
+ segment = (mi_segment_t*)((uint8_t*)segment - diff);
+
+ if (segment == NULL) return NULL;
+ mi_assert_internal((void*)segment < p);
+ bool cookie_ok = (_mi_ptr_cookie(segment) == segment->cookie);
+ mi_assert_internal(cookie_ok);
+ if mi_unlikely(!cookie_ok) return NULL;
+ if (((uint8_t*)segment + mi_segment_size(segment)) <= (uint8_t*)p) return NULL; // outside the range
+ mi_assert_internal(p >= (void*)segment && (uint8_t*)p < (uint8_t*)segment + mi_segment_size(segment));
+ return segment;
+}
+
+// Is this a valid pointer in our heap?
+static bool mi_is_valid_pointer(const void* p) {
+ return ((_mi_segment_of(p) != NULL) || (_mi_arena_contains(p)));
+}
+
+mi_decl_nodiscard mi_decl_export bool mi_is_in_heap_region(const void* p) mi_attr_noexcept {
+ return mi_is_valid_pointer(p);
+}
+
+/*
+// Return the full segment range belonging to a pointer
+static void* mi_segment_range_of(const void* p, size_t* size) {
+ mi_segment_t* segment = _mi_segment_of(p);
+ if (segment == NULL) {
+ if (size != NULL) *size = 0;
+ return NULL;
+ }
+ else {
+ if (size != NULL) *size = segment->segment_size;
+ return segment;
+ }
+ mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld));
+ mi_assert_internal(page == NULL || (mi_segment_page_size(_mi_page_segment(page)) - (MI_SECURE == 0 ? 0 : _mi_os_page_size())) >= block_size);
+ mi_reset_delayed(tld);
+ mi_assert_internal(page == NULL || mi_page_not_in_queue(page, tld));
+ return page;
+}
+*/
diff --git a/contrib/tools/python3/Objects/mimalloc/segment.c b/contrib/tools/python3/Objects/mimalloc/segment.c
new file mode 100644
index 00000000000..0b4d3abc07a
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/segment.c
@@ -0,0 +1,1682 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+
+#include <string.h> // memset
+#include <stdio.h>
+
+#define MI_PAGE_HUGE_ALIGN (256*1024)
+
+static void mi_segment_try_purge(mi_segment_t* segment, bool force, mi_stats_t* stats);
+
+
+// -------------------------------------------------------------------
+// commit mask
+// -------------------------------------------------------------------
+
+static bool mi_commit_mask_all_set(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm) {
+ for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) {
+ if ((commit->mask[i] & cm->mask[i]) != cm->mask[i]) return false;
+ }
+ return true;
+}
+
+static bool mi_commit_mask_any_set(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm) {
+ for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) {
+ if ((commit->mask[i] & cm->mask[i]) != 0) return true;
+ }
+ return false;
+}
+
+static void mi_commit_mask_create_intersect(const mi_commit_mask_t* commit, const mi_commit_mask_t* cm, mi_commit_mask_t* res) {
+ for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) {
+ res->mask[i] = (commit->mask[i] & cm->mask[i]);
+ }
+}
+
+static void mi_commit_mask_clear(mi_commit_mask_t* res, const mi_commit_mask_t* cm) {
+ for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) {
+ res->mask[i] &= ~(cm->mask[i]);
+ }
+}
+
+static void mi_commit_mask_set(mi_commit_mask_t* res, const mi_commit_mask_t* cm) {
+ for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) {
+ res->mask[i] |= cm->mask[i];
+ }
+}
+
+static void mi_commit_mask_create(size_t bitidx, size_t bitcount, mi_commit_mask_t* cm) {
+ mi_assert_internal(bitidx < MI_COMMIT_MASK_BITS);
+ mi_assert_internal((bitidx + bitcount) <= MI_COMMIT_MASK_BITS);
+ if (bitcount == MI_COMMIT_MASK_BITS) {
+ mi_assert_internal(bitidx==0);
+ mi_commit_mask_create_full(cm);
+ }
+ else if (bitcount == 0) {
+ mi_commit_mask_create_empty(cm);
+ }
+ else {
+ mi_commit_mask_create_empty(cm);
+ size_t i = bitidx / MI_COMMIT_MASK_FIELD_BITS;
+ size_t ofs = bitidx % MI_COMMIT_MASK_FIELD_BITS;
+ while (bitcount > 0) {
+ mi_assert_internal(i < MI_COMMIT_MASK_FIELD_COUNT);
+ size_t avail = MI_COMMIT_MASK_FIELD_BITS - ofs;
+ size_t count = (bitcount > avail ? avail : bitcount);
+ size_t mask = (count >= MI_COMMIT_MASK_FIELD_BITS ? ~((size_t)0) : (((size_t)1 << count) - 1) << ofs);
+ cm->mask[i] = mask;
+ bitcount -= count;
+ ofs = 0;
+ i++;
+ }
+ }
+}
+
+size_t _mi_commit_mask_committed_size(const mi_commit_mask_t* cm, size_t total) {
+ mi_assert_internal((total%MI_COMMIT_MASK_BITS)==0);
+ size_t count = 0;
+ for (size_t i = 0; i < MI_COMMIT_MASK_FIELD_COUNT; i++) {
+ size_t mask = cm->mask[i];
+ if (~mask == 0) {
+ count += MI_COMMIT_MASK_FIELD_BITS;
+ }
+ else {
+ for (; mask != 0; mask >>= 1) { // todo: use popcount
+ if ((mask&1)!=0) count++;
+ }
+ }
+ }
+ // we use total since for huge segments each commit bit may represent a larger size
+ return ((total / MI_COMMIT_MASK_BITS) * count);
+}
+
+
+size_t _mi_commit_mask_next_run(const mi_commit_mask_t* cm, size_t* idx) {
+ size_t i = (*idx) / MI_COMMIT_MASK_FIELD_BITS;
+ size_t ofs = (*idx) % MI_COMMIT_MASK_FIELD_BITS;
+ size_t mask = 0;
+ // find first ones
+ while (i < MI_COMMIT_MASK_FIELD_COUNT) {
+ mask = cm->mask[i];
+ mask >>= ofs;
+ if (mask != 0) {
+ while ((mask&1) == 0) {
+ mask >>= 1;
+ ofs++;
+ }
+ break;
+ }
+ i++;
+ ofs = 0;
+ }
+ if (i >= MI_COMMIT_MASK_FIELD_COUNT) {
+ // not found
+ *idx = MI_COMMIT_MASK_BITS;
+ return 0;
+ }
+ else {
+ // found, count ones
+ size_t count = 0;
+ *idx = (i*MI_COMMIT_MASK_FIELD_BITS) + ofs;
+ do {
+ mi_assert_internal(ofs < MI_COMMIT_MASK_FIELD_BITS && (mask&1) == 1);
+ do {
+ count++;
+ mask >>= 1;
+ } while ((mask&1) == 1);
+ if ((((*idx + count) % MI_COMMIT_MASK_FIELD_BITS) == 0)) {
+ i++;
+ if (i >= MI_COMMIT_MASK_FIELD_COUNT) break;
+ mask = cm->mask[i];
+ ofs = 0;
+ }
+ } while ((mask&1) == 1);
+ mi_assert_internal(count > 0);
+ return count;
+ }
+}
+
+
+/* --------------------------------------------------------------------------------
+ Segment allocation
+
+ If a thread ends, it "abandons" pages with used blocks
+ and there is an abandoned segment list whose segments can
+ be reclaimed by still running threads, much like work-stealing.
+-------------------------------------------------------------------------------- */
+
+
+/* -----------------------------------------------------------
+ Slices
+----------------------------------------------------------- */
+
+
+static const mi_slice_t* mi_segment_slices_end(const mi_segment_t* segment) {
+ return &segment->slices[segment->slice_entries];
+}
+
+static uint8_t* mi_slice_start(const mi_slice_t* slice) {
+ mi_segment_t* segment = _mi_ptr_segment(slice);
+ mi_assert_internal(slice >= segment->slices && slice < mi_segment_slices_end(segment));
+ return ((uint8_t*)segment + ((slice - segment->slices)*MI_SEGMENT_SLICE_SIZE));
+}
+
+
+/* -----------------------------------------------------------
+ Bins
+----------------------------------------------------------- */
+// Use bit scan forward to quickly find the first zero bit if it is available
+
+static inline size_t mi_slice_bin8(size_t slice_count) {
+ if (slice_count<=1) return slice_count;
+ mi_assert_internal(slice_count <= MI_SLICES_PER_SEGMENT);
+ slice_count--;
+ size_t s = mi_bsr(slice_count); // slice_count > 1
+ if (s <= 2) return slice_count + 1;
+ size_t bin = ((s << 2) | ((slice_count >> (s - 2))&0x03)) - 4;
+ return bin;
+}
+
+static inline size_t mi_slice_bin(size_t slice_count) {
+ mi_assert_internal(slice_count*MI_SEGMENT_SLICE_SIZE <= MI_SEGMENT_SIZE);
+ mi_assert_internal(mi_slice_bin8(MI_SLICES_PER_SEGMENT) <= MI_SEGMENT_BIN_MAX);
+ size_t bin = mi_slice_bin8(slice_count);
+ mi_assert_internal(bin <= MI_SEGMENT_BIN_MAX);
+ return bin;
+}
+
+static inline size_t mi_slice_index(const mi_slice_t* slice) {
+ mi_segment_t* segment = _mi_ptr_segment(slice);
+ ptrdiff_t index = slice - segment->slices;
+ mi_assert_internal(index >= 0 && index < (ptrdiff_t)segment->slice_entries);
+ return index;
+}
+
+
+/* -----------------------------------------------------------
+ Slice span queues
+----------------------------------------------------------- */
+
+static void mi_span_queue_push(mi_span_queue_t* sq, mi_slice_t* slice) {
+ // todo: or push to the end?
+ mi_assert_internal(slice->prev == NULL && slice->next==NULL);
+ slice->prev = NULL; // paranoia
+ slice->next = sq->first;
+ sq->first = slice;
+ if (slice->next != NULL) slice->next->prev = slice;
+ else sq->last = slice;
+ slice->xblock_size = 0; // free
+}
+
+static mi_span_queue_t* mi_span_queue_for(size_t slice_count, mi_segments_tld_t* tld) {
+ size_t bin = mi_slice_bin(slice_count);
+ mi_span_queue_t* sq = &tld->spans[bin];
+ mi_assert_internal(sq->slice_count >= slice_count);
+ return sq;
+}
+
+static void mi_span_queue_delete(mi_span_queue_t* sq, mi_slice_t* slice) {
+ mi_assert_internal(slice->xblock_size==0 && slice->slice_count>0 && slice->slice_offset==0);
+ // should work too if the queue does not contain slice (which can happen during reclaim)
+ if (slice->prev != NULL) slice->prev->next = slice->next;
+ if (slice == sq->first) sq->first = slice->next;
+ if (slice->next != NULL) slice->next->prev = slice->prev;
+ if (slice == sq->last) sq->last = slice->prev;
+ slice->prev = NULL;
+ slice->next = NULL;
+ slice->xblock_size = 1; // no more free
+}
+
+
+/* -----------------------------------------------------------
+ Invariant checking
+----------------------------------------------------------- */
+
+static bool mi_slice_is_used(const mi_slice_t* slice) {
+ return (slice->xblock_size > 0);
+}
+
+
+#if (MI_DEBUG>=3)
+static bool mi_span_queue_contains(mi_span_queue_t* sq, mi_slice_t* slice) {
+ for (mi_slice_t* s = sq->first; s != NULL; s = s->next) {
+ if (s==slice) return true;
+ }
+ return false;
+}
+
+static bool mi_segment_is_valid(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_assert_internal(segment != NULL);
+ mi_assert_internal(_mi_ptr_cookie(segment) == segment->cookie);
+ mi_assert_internal(segment->abandoned <= segment->used);
+ mi_assert_internal(segment->thread_id == 0 || segment->thread_id == _mi_thread_id());
+ mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->purge_mask)); // can only decommit committed blocks
+ //mi_assert_internal(segment->segment_info_size % MI_SEGMENT_SLICE_SIZE == 0);
+ mi_slice_t* slice = &segment->slices[0];
+ const mi_slice_t* end = mi_segment_slices_end(segment);
+ size_t used_count = 0;
+ mi_span_queue_t* sq;
+ while(slice < end) {
+ mi_assert_internal(slice->slice_count > 0);
+ mi_assert_internal(slice->slice_offset == 0);
+ size_t index = mi_slice_index(slice);
+ size_t maxindex = (index + slice->slice_count >= segment->slice_entries ? segment->slice_entries : index + slice->slice_count) - 1;
+ if (mi_slice_is_used(slice)) { // a page in use, we need at least MAX_SLICE_OFFSET valid back offsets
+ used_count++;
+ for (size_t i = 0; i <= MI_MAX_SLICE_OFFSET && index + i <= maxindex; i++) {
+ mi_assert_internal(segment->slices[index + i].slice_offset == i*sizeof(mi_slice_t));
+ mi_assert_internal(i==0 || segment->slices[index + i].slice_count == 0);
+ mi_assert_internal(i==0 || segment->slices[index + i].xblock_size == 1);
+ }
+ // and the last entry as well (for coalescing)
+ const mi_slice_t* last = slice + slice->slice_count - 1;
+ if (last > slice && last < mi_segment_slices_end(segment)) {
+ mi_assert_internal(last->slice_offset == (slice->slice_count-1)*sizeof(mi_slice_t));
+ mi_assert_internal(last->slice_count == 0);
+ mi_assert_internal(last->xblock_size == 1);
+ }
+ }
+ else { // free range of slices; only last slice needs a valid back offset
+ mi_slice_t* last = &segment->slices[maxindex];
+ if (segment->kind != MI_SEGMENT_HUGE || slice->slice_count <= (segment->slice_entries - segment->segment_info_slices)) {
+ mi_assert_internal((uint8_t*)slice == (uint8_t*)last - last->slice_offset);
+ }
+ mi_assert_internal(slice == last || last->slice_count == 0 );
+ mi_assert_internal(last->xblock_size == 0 || (segment->kind==MI_SEGMENT_HUGE && last->xblock_size==1));
+ if (segment->kind != MI_SEGMENT_HUGE && segment->thread_id != 0) { // segment is not huge or abandoned
+ sq = mi_span_queue_for(slice->slice_count,tld);
+ mi_assert_internal(mi_span_queue_contains(sq,slice));
+ }
+ }
+ slice = &segment->slices[maxindex+1];
+ }
+ mi_assert_internal(slice == end);
+ mi_assert_internal(used_count == segment->used + 1);
+ return true;
+}
+#endif
+
+/* -----------------------------------------------------------
+ Segment size calculations
+----------------------------------------------------------- */
+
+static size_t mi_segment_info_size(mi_segment_t* segment) {
+ return segment->segment_info_slices * MI_SEGMENT_SLICE_SIZE;
+}
+
+static uint8_t* _mi_segment_page_start_from_slice(const mi_segment_t* segment, const mi_slice_t* slice, size_t xblock_size, size_t* page_size)
+{
+ ptrdiff_t idx = slice - segment->slices;
+ size_t psize = (size_t)slice->slice_count * MI_SEGMENT_SLICE_SIZE;
+ // make the start not OS page aligned for smaller blocks to avoid page/cache effects
+ // note: the offset must always be an xblock_size multiple since we assume small allocations
+ // are aligned (see `mi_heap_malloc_aligned`).
+ size_t start_offset = 0;
+ if (xblock_size >= MI_INTPTR_SIZE) {
+ if (xblock_size <= 64) { start_offset = 3*xblock_size; }
+ else if (xblock_size <= 512) { start_offset = xblock_size; }
+ }
+ if (page_size != NULL) { *page_size = psize - start_offset; }
+ return (uint8_t*)segment + ((idx*MI_SEGMENT_SLICE_SIZE) + start_offset);
+}
+
+// Start of the page available memory; can be used on uninitialized pages
+uint8_t* _mi_segment_page_start(const mi_segment_t* segment, const mi_page_t* page, size_t* page_size)
+{
+ const mi_slice_t* slice = mi_page_to_slice((mi_page_t*)page);
+ uint8_t* p = _mi_segment_page_start_from_slice(segment, slice, page->xblock_size, page_size);
+ mi_assert_internal(page->xblock_size > 0 || _mi_ptr_page(p) == page);
+ mi_assert_internal(_mi_ptr_segment(p) == segment);
+ return p;
+}
+
+
+static size_t mi_segment_calculate_slices(size_t required, size_t* pre_size, size_t* info_slices) {
+ size_t page_size = _mi_os_page_size();
+ size_t isize = _mi_align_up(sizeof(mi_segment_t), page_size);
+ size_t guardsize = 0;
+
+ if (MI_SECURE>0) {
+ // in secure mode, we set up a protected page in between the segment info
+ // and the page data (and one at the end of the segment)
+ guardsize = page_size;
+ if (required > 0) {
+ required = _mi_align_up(required, MI_SEGMENT_SLICE_SIZE) + page_size;
+ }
+ }
+
+ if (pre_size != NULL) *pre_size = isize;
+ isize = _mi_align_up(isize + guardsize, MI_SEGMENT_SLICE_SIZE);
+ if (info_slices != NULL) *info_slices = isize / MI_SEGMENT_SLICE_SIZE;
+ size_t segment_size = (required==0 ? MI_SEGMENT_SIZE : _mi_align_up( required + isize + guardsize, MI_SEGMENT_SLICE_SIZE) );
+ mi_assert_internal(segment_size % MI_SEGMENT_SLICE_SIZE == 0);
+ return (segment_size / MI_SEGMENT_SLICE_SIZE);
+}
+
+
+/* ----------------------------------------------------------------------------
+Segment caches
+We keep a small segment cache per thread to increase local
+reuse and avoid setting/clearing guard pages in secure mode.
+------------------------------------------------------------------------------- */
+
+static void mi_segments_track_size(long segment_size, mi_segments_tld_t* tld) {
+ if (segment_size>=0) _mi_stat_increase(&tld->stats->segments,1);
+ else _mi_stat_decrease(&tld->stats->segments,1);
+ tld->count += (segment_size >= 0 ? 1 : -1);
+ if (tld->count > tld->peak_count) tld->peak_count = tld->count;
+ tld->current_size += segment_size;
+ if (tld->current_size > tld->peak_size) tld->peak_size = tld->current_size;
+}
+
+static void mi_segment_os_free(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ segment->thread_id = 0;
+ _mi_segment_map_freed_at(segment);
+ mi_segments_track_size(-((long)mi_segment_size(segment)),tld);
+ if (MI_SECURE>0) {
+ // _mi_os_unprotect(segment, mi_segment_size(segment)); // ensure no more guard pages are set
+ // unprotect the guard pages; we cannot just unprotect the whole segment size as part may be decommitted
+ size_t os_pagesize = _mi_os_page_size();
+ _mi_os_unprotect((uint8_t*)segment + mi_segment_info_size(segment) - os_pagesize, os_pagesize);
+ uint8_t* end = (uint8_t*)segment + mi_segment_size(segment) - os_pagesize;
+ _mi_os_unprotect(end, os_pagesize);
+ }
+
+ // purge delayed decommits now? (no, leave it to the arena)
+ // mi_segment_try_purge(segment,true,tld->stats);
+
+ const size_t size = mi_segment_size(segment);
+ const size_t csize = _mi_commit_mask_committed_size(&segment->commit_mask, size);
+
+ _mi_abandoned_await_readers(tld->abandoned); // wait until safe to free
+ _mi_arena_free(segment, mi_segment_size(segment), csize, segment->memid, tld->stats);
+}
+
+// called by threads that are terminating
+void _mi_segment_thread_collect(mi_segments_tld_t* tld) {
+ MI_UNUSED(tld);
+ // nothing to do
+}
+
+
+/* -----------------------------------------------------------
+ Commit/Decommit ranges
+----------------------------------------------------------- */
+
+static void mi_segment_commit_mask(mi_segment_t* segment, bool conservative, uint8_t* p, size_t size, uint8_t** start_p, size_t* full_size, mi_commit_mask_t* cm) {
+ mi_assert_internal(_mi_ptr_segment(p + 1) == segment);
+ mi_assert_internal(segment->kind != MI_SEGMENT_HUGE);
+ mi_commit_mask_create_empty(cm);
+ if (size == 0 || size > MI_SEGMENT_SIZE || segment->kind == MI_SEGMENT_HUGE) return;
+ const size_t segstart = mi_segment_info_size(segment);
+ const size_t segsize = mi_segment_size(segment);
+ if (p >= (uint8_t*)segment + segsize) return;
+
+ size_t pstart = (p - (uint8_t*)segment);
+ mi_assert_internal(pstart + size <= segsize);
+
+ size_t start;
+ size_t end;
+ if (conservative) {
+ // decommit conservative
+ start = _mi_align_up(pstart, MI_COMMIT_SIZE);
+ end = _mi_align_down(pstart + size, MI_COMMIT_SIZE);
+ mi_assert_internal(start >= segstart);
+ mi_assert_internal(end <= segsize);
+ }
+ else {
+ // commit liberal
+ start = _mi_align_down(pstart, MI_MINIMAL_COMMIT_SIZE);
+ end = _mi_align_up(pstart + size, MI_MINIMAL_COMMIT_SIZE);
+ }
+ if (pstart >= segstart && start < segstart) { // note: the mask is also calculated for an initial commit of the info area
+ start = segstart;
+ }
+ if (end > segsize) {
+ end = segsize;
+ }
+
+ mi_assert_internal(start <= pstart && (pstart + size) <= end);
+ mi_assert_internal(start % MI_COMMIT_SIZE==0 && end % MI_COMMIT_SIZE == 0);
+ *start_p = (uint8_t*)segment + start;
+ *full_size = (end > start ? end - start : 0);
+ if (*full_size == 0) return;
+
+ size_t bitidx = start / MI_COMMIT_SIZE;
+ mi_assert_internal(bitidx < MI_COMMIT_MASK_BITS);
+
+ size_t bitcount = *full_size / MI_COMMIT_SIZE; // can be 0
+ if (bitidx + bitcount > MI_COMMIT_MASK_BITS) {
+ _mi_warning_message("commit mask overflow: idx=%zu count=%zu start=%zx end=%zx p=0x%p size=%zu fullsize=%zu\n", bitidx, bitcount, start, end, p, size, *full_size);
+ }
+ mi_assert_internal((bitidx + bitcount) <= MI_COMMIT_MASK_BITS);
+ mi_commit_mask_create(bitidx, bitcount, cm);
+}
+
+static bool mi_segment_commit(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) {
+ mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->purge_mask));
+
+ // commit liberal
+ uint8_t* start = NULL;
+ size_t full_size = 0;
+ mi_commit_mask_t mask;
+ mi_segment_commit_mask(segment, false /* conservative? */, p, size, &start, &full_size, &mask);
+ if (mi_commit_mask_is_empty(&mask) || full_size == 0) return true;
+
+ if (!mi_commit_mask_all_set(&segment->commit_mask, &mask)) {
+ // committing
+ bool is_zero = false;
+ mi_commit_mask_t cmask;
+ mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask);
+ _mi_stat_decrease(&_mi_stats_main.committed, _mi_commit_mask_committed_size(&cmask, MI_SEGMENT_SIZE)); // adjust for overlap
+ if (!_mi_os_commit(start, full_size, &is_zero, stats)) return false;
+ mi_commit_mask_set(&segment->commit_mask, &mask);
+ }
+
+ // increase purge expiration when using part of delayed purges -- we assume more allocations are coming soon.
+ if (mi_commit_mask_any_set(&segment->purge_mask, &mask)) {
+ segment->purge_expire = _mi_clock_now() + mi_option_get(mi_option_purge_delay);
+ }
+
+ // always clear any delayed purges in our range (as they are either committed now)
+ mi_commit_mask_clear(&segment->purge_mask, &mask);
+ return true;
+}
+
+static bool mi_segment_ensure_committed(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) {
+ mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->purge_mask));
+ // note: assumes commit_mask is always full for huge segments as otherwise the commit mask bits can overflow
+ if (mi_commit_mask_is_full(&segment->commit_mask) && mi_commit_mask_is_empty(&segment->purge_mask)) return true; // fully committed
+ mi_assert_internal(segment->kind != MI_SEGMENT_HUGE);
+ return mi_segment_commit(segment, p, size, stats);
+}
+
+static bool mi_segment_purge(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) {
+ mi_assert_internal(mi_commit_mask_all_set(&segment->commit_mask, &segment->purge_mask));
+ if (!segment->allow_purge) return true;
+
+ // purge conservative
+ uint8_t* start = NULL;
+ size_t full_size = 0;
+ mi_commit_mask_t mask;
+ mi_segment_commit_mask(segment, true /* conservative? */, p, size, &start, &full_size, &mask);
+ if (mi_commit_mask_is_empty(&mask) || full_size==0) return true;
+
+ if (mi_commit_mask_any_set(&segment->commit_mask, &mask)) {
+ // purging
+ mi_assert_internal((void*)start != (void*)segment);
+ mi_assert_internal(segment->allow_decommit);
+ const bool decommitted = _mi_os_purge(start, full_size, stats); // reset or decommit
+ if (decommitted) {
+ mi_commit_mask_t cmask;
+ mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask);
+ _mi_stat_increase(&_mi_stats_main.committed, full_size - _mi_commit_mask_committed_size(&cmask, MI_SEGMENT_SIZE)); // adjust for double counting
+ mi_commit_mask_clear(&segment->commit_mask, &mask);
+ }
+ }
+
+ // always clear any scheduled purges in our range
+ mi_commit_mask_clear(&segment->purge_mask, &mask);
+ return true;
+}
+
+static void mi_segment_schedule_purge(mi_segment_t* segment, uint8_t* p, size_t size, mi_stats_t* stats) {
+ if (!segment->allow_purge) return;
+
+ if (mi_option_get(mi_option_purge_delay) == 0) {
+ mi_segment_purge(segment, p, size, stats);
+ }
+ else {
+ // register for future purge in the purge mask
+ uint8_t* start = NULL;
+ size_t full_size = 0;
+ mi_commit_mask_t mask;
+ mi_segment_commit_mask(segment, true /*conservative*/, p, size, &start, &full_size, &mask);
+ if (mi_commit_mask_is_empty(&mask) || full_size==0) return;
+
+ // update delayed commit
+ mi_assert_internal(segment->purge_expire > 0 || mi_commit_mask_is_empty(&segment->purge_mask));
+ mi_commit_mask_t cmask;
+ mi_commit_mask_create_intersect(&segment->commit_mask, &mask, &cmask); // only purge what is committed; span_free may try to decommit more
+ mi_commit_mask_set(&segment->purge_mask, &cmask);
+ mi_msecs_t now = _mi_clock_now();
+ if (segment->purge_expire == 0) {
+ // no previous purgess, initialize now
+ segment->purge_expire = now + mi_option_get(mi_option_purge_delay);
+ }
+ else if (segment->purge_expire <= now) {
+ // previous purge mask already expired
+ if (segment->purge_expire + mi_option_get(mi_option_purge_extend_delay) <= now) {
+ mi_segment_try_purge(segment, true, stats);
+ }
+ else {
+ segment->purge_expire = now + mi_option_get(mi_option_purge_extend_delay); // (mi_option_get(mi_option_purge_delay) / 8); // wait a tiny bit longer in case there is a series of free's
+ }
+ }
+ else {
+ // previous purge mask is not yet expired, increase the expiration by a bit.
+ segment->purge_expire += mi_option_get(mi_option_purge_extend_delay);
+ }
+ }
+}
+
+static void mi_segment_try_purge(mi_segment_t* segment, bool force, mi_stats_t* stats) {
+ if (!segment->allow_purge || mi_commit_mask_is_empty(&segment->purge_mask)) return;
+ mi_msecs_t now = _mi_clock_now();
+ if (!force && now < segment->purge_expire) return;
+
+ mi_commit_mask_t mask = segment->purge_mask;
+ segment->purge_expire = 0;
+ mi_commit_mask_create_empty(&segment->purge_mask);
+
+ size_t idx;
+ size_t count;
+ mi_commit_mask_foreach(&mask, idx, count) {
+ // if found, decommit that sequence
+ if (count > 0) {
+ uint8_t* p = (uint8_t*)segment + (idx*MI_COMMIT_SIZE);
+ size_t size = count * MI_COMMIT_SIZE;
+ mi_segment_purge(segment, p, size, stats);
+ }
+ }
+ mi_commit_mask_foreach_end()
+ mi_assert_internal(mi_commit_mask_is_empty(&segment->purge_mask));
+}
+
+
+/* -----------------------------------------------------------
+ Span free
+----------------------------------------------------------- */
+
+static bool mi_segment_is_abandoned(mi_segment_t* segment) {
+ return (segment->thread_id == 0);
+}
+
+// note: can be called on abandoned segments
+static void mi_segment_span_free(mi_segment_t* segment, size_t slice_index, size_t slice_count, bool allow_purge, mi_segments_tld_t* tld) {
+ mi_assert_internal(slice_index < segment->slice_entries);
+ mi_span_queue_t* sq = (segment->kind == MI_SEGMENT_HUGE || mi_segment_is_abandoned(segment)
+ ? NULL : mi_span_queue_for(slice_count,tld));
+ if (slice_count==0) slice_count = 1;
+ mi_assert_internal(slice_index + slice_count - 1 < segment->slice_entries);
+
+ // set first and last slice (the intermediates can be undetermined)
+ mi_slice_t* slice = &segment->slices[slice_index];
+ slice->slice_count = (uint32_t)slice_count;
+ mi_assert_internal(slice->slice_count == slice_count); // no overflow?
+ slice->slice_offset = 0;
+ if (slice_count > 1) {
+ mi_slice_t* last = &segment->slices[slice_index + slice_count - 1];
+ last->slice_count = 0;
+ last->slice_offset = (uint32_t)(sizeof(mi_page_t)*(slice_count - 1));
+ last->xblock_size = 0;
+ }
+
+ // perhaps decommit
+ if (allow_purge) {
+ mi_segment_schedule_purge(segment, mi_slice_start(slice), slice_count * MI_SEGMENT_SLICE_SIZE, tld->stats);
+ }
+
+ // and push it on the free page queue (if it was not a huge page)
+ if (sq != NULL) mi_span_queue_push( sq, slice );
+ else slice->xblock_size = 0; // mark huge page as free anyways
+}
+
+/*
+// called from reclaim to add existing free spans
+static void mi_segment_span_add_free(mi_slice_t* slice, mi_segments_tld_t* tld) {
+ mi_segment_t* segment = _mi_ptr_segment(slice);
+ mi_assert_internal(slice->xblock_size==0 && slice->slice_count>0 && slice->slice_offset==0);
+ size_t slice_index = mi_slice_index(slice);
+ mi_segment_span_free(segment,slice_index,slice->slice_count,tld);
+}
+*/
+
+static void mi_segment_span_remove_from_queue(mi_slice_t* slice, mi_segments_tld_t* tld) {
+ mi_assert_internal(slice->slice_count > 0 && slice->slice_offset==0 && slice->xblock_size==0);
+ mi_assert_internal(_mi_ptr_segment(slice)->kind != MI_SEGMENT_HUGE);
+ mi_span_queue_t* sq = mi_span_queue_for(slice->slice_count, tld);
+ mi_span_queue_delete(sq, slice);
+}
+
+// note: can be called on abandoned segments
+static mi_slice_t* mi_segment_span_free_coalesce(mi_slice_t* slice, mi_segments_tld_t* tld) {
+ mi_assert_internal(slice != NULL && slice->slice_count > 0 && slice->slice_offset == 0);
+ mi_segment_t* segment = _mi_ptr_segment(slice);
+ bool is_abandoned = mi_segment_is_abandoned(segment);
+
+ // for huge pages, just mark as free but don't add to the queues
+ if (segment->kind == MI_SEGMENT_HUGE) {
+ // issue #691: segment->used can be 0 if the huge page block was freed while abandoned (reclaim will get here in that case)
+ mi_assert_internal((segment->used==0 && slice->xblock_size==0) || segment->used == 1); // decreased right after this call in `mi_segment_page_clear`
+ slice->xblock_size = 0; // mark as free anyways
+ // we should mark the last slice `xblock_size=0` now to maintain invariants but we skip it to
+ // avoid a possible cache miss (and the segment is about to be freed)
+ return slice;
+ }
+
+ // otherwise coalesce the span and add to the free span queues
+ size_t slice_count = slice->slice_count;
+ mi_slice_t* next = slice + slice->slice_count;
+ mi_assert_internal(next <= mi_segment_slices_end(segment));
+ if (next < mi_segment_slices_end(segment) && next->xblock_size==0) {
+ // free next block -- remove it from free and merge
+ mi_assert_internal(next->slice_count > 0 && next->slice_offset==0);
+ slice_count += next->slice_count; // extend
+ if (!is_abandoned) { mi_segment_span_remove_from_queue(next, tld); }
+ }
+ if (slice > segment->slices) {
+ mi_slice_t* prev = mi_slice_first(slice - 1);
+ mi_assert_internal(prev >= segment->slices);
+ if (prev->xblock_size==0) {
+ // free previous slice -- remove it from free and merge
+ mi_assert_internal(prev->slice_count > 0 && prev->slice_offset==0);
+ slice_count += prev->slice_count;
+ if (!is_abandoned) { mi_segment_span_remove_from_queue(prev, tld); }
+ slice = prev;
+ }
+ }
+
+ // and add the new free page
+ mi_segment_span_free(segment, mi_slice_index(slice), slice_count, true, tld);
+ return slice;
+}
+
+
+
+/* -----------------------------------------------------------
+ Page allocation
+----------------------------------------------------------- */
+
+// Note: may still return NULL if committing the memory failed
+static mi_page_t* mi_segment_span_allocate(mi_segment_t* segment, size_t slice_index, size_t slice_count, mi_segments_tld_t* tld) {
+ mi_assert_internal(slice_index < segment->slice_entries);
+ mi_slice_t* const slice = &segment->slices[slice_index];
+ mi_assert_internal(slice->xblock_size==0 || slice->xblock_size==1);
+
+ // commit before changing the slice data
+ if (!mi_segment_ensure_committed(segment, _mi_segment_page_start_from_slice(segment, slice, 0, NULL), slice_count * MI_SEGMENT_SLICE_SIZE, tld->stats)) {
+ return NULL; // commit failed!
+ }
+
+ // convert the slices to a page
+ slice->slice_offset = 0;
+ slice->slice_count = (uint32_t)slice_count;
+ mi_assert_internal(slice->slice_count == slice_count);
+ const size_t bsize = slice_count * MI_SEGMENT_SLICE_SIZE;
+ slice->xblock_size = (uint32_t)(bsize >= MI_HUGE_BLOCK_SIZE ? MI_HUGE_BLOCK_SIZE : bsize);
+ mi_page_t* page = mi_slice_to_page(slice);
+ mi_assert_internal(mi_page_block_size(page) == bsize);
+
+ // set slice back pointers for the first MI_MAX_SLICE_OFFSET entries
+ size_t extra = slice_count-1;
+ if (extra > MI_MAX_SLICE_OFFSET) extra = MI_MAX_SLICE_OFFSET;
+ if (slice_index + extra >= segment->slice_entries) extra = segment->slice_entries - slice_index - 1; // huge objects may have more slices than avaiable entries in the segment->slices
+
+ mi_slice_t* slice_next = slice + 1;
+ for (size_t i = 1; i <= extra; i++, slice_next++) {
+ slice_next->slice_offset = (uint32_t)(sizeof(mi_slice_t)*i);
+ slice_next->slice_count = 0;
+ slice_next->xblock_size = 1;
+ }
+
+ // and also for the last one (if not set already) (the last one is needed for coalescing and for large alignments)
+ // note: the cast is needed for ubsan since the index can be larger than MI_SLICES_PER_SEGMENT for huge allocations (see #543)
+ mi_slice_t* last = slice + slice_count - 1;
+ mi_slice_t* end = (mi_slice_t*)mi_segment_slices_end(segment);
+ if (last > end) last = end;
+ if (last > slice) {
+ last->slice_offset = (uint32_t)(sizeof(mi_slice_t) * (last - slice));
+ last->slice_count = 0;
+ last->xblock_size = 1;
+ }
+
+ // and initialize the page
+ page->is_committed = true;
+ segment->used++;
+ return page;
+}
+
+static void mi_segment_slice_split(mi_segment_t* segment, mi_slice_t* slice, size_t slice_count, mi_segments_tld_t* tld) {
+ mi_assert_internal(_mi_ptr_segment(slice) == segment);
+ mi_assert_internal(slice->slice_count >= slice_count);
+ mi_assert_internal(slice->xblock_size > 0); // no more in free queue
+ if (slice->slice_count <= slice_count) return;
+ mi_assert_internal(segment->kind != MI_SEGMENT_HUGE);
+ size_t next_index = mi_slice_index(slice) + slice_count;
+ size_t next_count = slice->slice_count - slice_count;
+ mi_segment_span_free(segment, next_index, next_count, false /* don't purge left-over part */, tld);
+ slice->slice_count = (uint32_t)slice_count;
+}
+
+static mi_page_t* mi_segments_page_find_and_allocate(size_t slice_count, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld) {
+ mi_assert_internal(slice_count*MI_SEGMENT_SLICE_SIZE <= MI_LARGE_OBJ_SIZE_MAX);
+ // search from best fit up
+ mi_span_queue_t* sq = mi_span_queue_for(slice_count, tld);
+ if (slice_count == 0) slice_count = 1;
+ while (sq <= &tld->spans[MI_SEGMENT_BIN_MAX]) {
+ for (mi_slice_t* slice = sq->first; slice != NULL; slice = slice->next) {
+ if (slice->slice_count >= slice_count) {
+ // found one
+ mi_segment_t* segment = _mi_ptr_segment(slice);
+ if (_mi_arena_memid_is_suitable(segment->memid, req_arena_id)) {
+ // found a suitable page span
+ mi_span_queue_delete(sq, slice);
+
+ if (slice->slice_count > slice_count) {
+ mi_segment_slice_split(segment, slice, slice_count, tld);
+ }
+ mi_assert_internal(slice != NULL && slice->slice_count == slice_count && slice->xblock_size > 0);
+ mi_page_t* page = mi_segment_span_allocate(segment, mi_slice_index(slice), slice->slice_count, tld);
+ if (page == NULL) {
+ // commit failed; return NULL but first restore the slice
+ mi_segment_span_free_coalesce(slice, tld);
+ return NULL;
+ }
+ return page;
+ }
+ }
+ }
+ sq++;
+ }
+ // could not find a page..
+ return NULL;
+}
+
+
+/* -----------------------------------------------------------
+ Segment allocation
+----------------------------------------------------------- */
+
+static mi_segment_t* mi_segment_os_alloc( size_t required, size_t page_alignment, bool eager_delayed, mi_arena_id_t req_arena_id,
+ size_t* psegment_slices, size_t* ppre_size, size_t* pinfo_slices,
+ bool commit, mi_segments_tld_t* tld, mi_os_tld_t* os_tld)
+
+{
+ mi_memid_t memid;
+ bool allow_large = (!eager_delayed && (MI_SECURE == 0)); // only allow large OS pages once we are no longer lazy
+ size_t align_offset = 0;
+ size_t alignment = MI_SEGMENT_ALIGN;
+
+ if (page_alignment > 0) {
+ // mi_assert_internal(huge_page != NULL);
+ mi_assert_internal(page_alignment >= MI_SEGMENT_ALIGN);
+ alignment = page_alignment;
+ const size_t info_size = (*pinfo_slices) * MI_SEGMENT_SLICE_SIZE;
+ align_offset = _mi_align_up( info_size, MI_SEGMENT_ALIGN );
+ const size_t extra = align_offset - info_size;
+ // recalculate due to potential guard pages
+ *psegment_slices = mi_segment_calculate_slices(required + extra, ppre_size, pinfo_slices);
+
+ // mi_page_t.slice_count type is uint32_t
+ if (*psegment_slices > (size_t)UINT32_MAX) return NULL;
+ }
+
+ const size_t segment_size = (*psegment_slices) * MI_SEGMENT_SLICE_SIZE;
+ mi_segment_t* segment = (mi_segment_t*)_mi_arena_alloc_aligned(segment_size, alignment, align_offset, commit, allow_large, req_arena_id, &memid, os_tld);
+ if (segment == NULL) {
+ return NULL; // failed to allocate
+ }
+
+ // ensure metadata part of the segment is committed
+ mi_commit_mask_t commit_mask;
+ if (memid.initially_committed) {
+ mi_commit_mask_create_full(&commit_mask);
+ }
+ else {
+ // at least commit the info slices
+ const size_t commit_needed = _mi_divide_up((*pinfo_slices)*MI_SEGMENT_SLICE_SIZE, MI_COMMIT_SIZE);
+ mi_assert_internal(commit_needed>0);
+ mi_commit_mask_create(0, commit_needed, &commit_mask);
+ mi_assert_internal(commit_needed*MI_COMMIT_SIZE >= (*pinfo_slices)*MI_SEGMENT_SLICE_SIZE);
+ if (!_mi_os_commit(segment, commit_needed*MI_COMMIT_SIZE, NULL, tld->stats)) {
+ _mi_arena_free(segment,segment_size,0,memid,tld->stats);
+ return NULL;
+ }
+ }
+ mi_assert_internal(segment != NULL && (uintptr_t)segment % MI_SEGMENT_SIZE == 0);
+
+ segment->memid = memid;
+ segment->allow_decommit = !memid.is_pinned;
+ segment->allow_purge = segment->allow_decommit && (mi_option_get(mi_option_purge_delay) >= 0);
+ segment->segment_size = segment_size;
+ segment->commit_mask = commit_mask;
+ segment->purge_expire = 0;
+ mi_commit_mask_create_empty(&segment->purge_mask);
+ mi_atomic_store_ptr_release(mi_segment_t, &segment->abandoned_next, NULL); // tsan
+
+ mi_segments_track_size((long)(segment_size), tld);
+ _mi_segment_map_allocated_at(segment);
+ return segment;
+}
+
+
+// Allocate a segment from the OS aligned to `MI_SEGMENT_SIZE` .
+static mi_segment_t* mi_segment_alloc(size_t required, size_t page_alignment, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld, mi_page_t** huge_page)
+{
+ mi_assert_internal((required==0 && huge_page==NULL) || (required>0 && huge_page != NULL));
+
+ // calculate needed sizes first
+ size_t info_slices;
+ size_t pre_size;
+ size_t segment_slices = mi_segment_calculate_slices(required, &pre_size, &info_slices);
+
+ // mi_page_t.slice_count type is uint32_t
+ if (segment_slices > (size_t)UINT32_MAX) return NULL;
+
+ // Commit eagerly only if not the first N lazy segments (to reduce impact of many threads that allocate just a little)
+ const bool eager_delay = (// !_mi_os_has_overcommit() && // never delay on overcommit systems
+ _mi_current_thread_count() > 1 && // do not delay for the first N threads
+ tld->count < (size_t)mi_option_get(mi_option_eager_commit_delay));
+ const bool eager = !eager_delay && mi_option_is_enabled(mi_option_eager_commit);
+ bool commit = eager || (required > 0);
+
+ // Allocate the segment from the OS
+ mi_segment_t* segment = mi_segment_os_alloc(required, page_alignment, eager_delay, req_arena_id,
+ &segment_slices, &pre_size, &info_slices, commit, tld, os_tld);
+ if (segment == NULL) return NULL;
+
+ // zero the segment info? -- not always needed as it may be zero initialized from the OS
+ if (!segment->memid.initially_zero) {
+ ptrdiff_t ofs = offsetof(mi_segment_t, next);
+ size_t prefix = offsetof(mi_segment_t, slices) - ofs;
+ size_t zsize = prefix + (sizeof(mi_slice_t) * (segment_slices + 1)); // one more
+ _mi_memzero((uint8_t*)segment + ofs, zsize);
+ }
+
+ // initialize the rest of the segment info
+ const size_t slice_entries = (segment_slices > MI_SLICES_PER_SEGMENT ? MI_SLICES_PER_SEGMENT : segment_slices);
+ segment->segment_slices = segment_slices;
+ segment->segment_info_slices = info_slices;
+ segment->thread_id = _mi_thread_id();
+ segment->cookie = _mi_ptr_cookie(segment);
+ segment->slice_entries = slice_entries;
+ segment->kind = (required == 0 ? MI_SEGMENT_NORMAL : MI_SEGMENT_HUGE);
+
+ // _mi_memzero(segment->slices, sizeof(mi_slice_t)*(info_slices+1));
+ _mi_stat_increase(&tld->stats->page_committed, mi_segment_info_size(segment));
+
+ // set up guard pages
+ size_t guard_slices = 0;
+ if (MI_SECURE>0) {
+ // in secure mode, we set up a protected page in between the segment info
+ // and the page data, and at the end of the segment.
+ size_t os_pagesize = _mi_os_page_size();
+ mi_assert_internal(mi_segment_info_size(segment) - os_pagesize >= pre_size);
+ _mi_os_protect((uint8_t*)segment + mi_segment_info_size(segment) - os_pagesize, os_pagesize);
+ uint8_t* end = (uint8_t*)segment + mi_segment_size(segment) - os_pagesize;
+ mi_segment_ensure_committed(segment, end, os_pagesize, tld->stats);
+ _mi_os_protect(end, os_pagesize);
+ if (slice_entries == segment_slices) segment->slice_entries--; // don't use the last slice :-(
+ guard_slices = 1;
+ }
+
+ // reserve first slices for segment info
+ mi_page_t* page0 = mi_segment_span_allocate(segment, 0, info_slices, tld);
+ mi_assert_internal(page0!=NULL); if (page0==NULL) return NULL; // cannot fail as we always commit in advance
+ mi_assert_internal(segment->used == 1);
+ segment->used = 0; // don't count our internal slices towards usage
+
+ // initialize initial free pages
+ if (segment->kind == MI_SEGMENT_NORMAL) { // not a huge page
+ mi_assert_internal(huge_page==NULL);
+ mi_segment_span_free(segment, info_slices, segment->slice_entries - info_slices, false /* don't purge */, tld);
+ }
+ else {
+ mi_assert_internal(huge_page!=NULL);
+ mi_assert_internal(mi_commit_mask_is_empty(&segment->purge_mask));
+ mi_assert_internal(mi_commit_mask_is_full(&segment->commit_mask));
+ *huge_page = mi_segment_span_allocate(segment, info_slices, segment_slices - info_slices - guard_slices, tld);
+ mi_assert_internal(*huge_page != NULL); // cannot fail as we commit in advance
+ }
+
+ mi_assert_expensive(mi_segment_is_valid(segment,tld));
+ return segment;
+}
+
+
+static void mi_segment_free(mi_segment_t* segment, bool force, mi_segments_tld_t* tld) {
+ MI_UNUSED(force);
+ mi_assert_internal(segment != NULL);
+ mi_assert_internal(segment->next == NULL);
+ mi_assert_internal(segment->used == 0);
+
+ // Remove the free pages
+ mi_slice_t* slice = &segment->slices[0];
+ const mi_slice_t* end = mi_segment_slices_end(segment);
+ #if MI_DEBUG>1
+ size_t page_count = 0;
+ #endif
+ while (slice < end) {
+ mi_assert_internal(slice->slice_count > 0);
+ mi_assert_internal(slice->slice_offset == 0);
+ mi_assert_internal(mi_slice_index(slice)==0 || slice->xblock_size == 0); // no more used pages ..
+ if (slice->xblock_size == 0 && segment->kind != MI_SEGMENT_HUGE) {
+ mi_segment_span_remove_from_queue(slice, tld);
+ }
+ #if MI_DEBUG>1
+ page_count++;
+ #endif
+ slice = slice + slice->slice_count;
+ }
+ mi_assert_internal(page_count == 2); // first page is allocated by the segment itself
+
+ // stats
+ _mi_stat_decrease(&tld->stats->page_committed, mi_segment_info_size(segment));
+
+ // return it to the OS
+ mi_segment_os_free(segment, tld);
+}
+
+
+/* -----------------------------------------------------------
+ Page Free
+----------------------------------------------------------- */
+
+static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld);
+
+// note: can be called on abandoned pages
+static mi_slice_t* mi_segment_page_clear(mi_page_t* page, mi_segments_tld_t* tld) {
+ mi_assert_internal(page->xblock_size > 0);
+ mi_assert_internal(mi_page_all_free(page));
+ mi_segment_t* segment = _mi_ptr_segment(page);
+ mi_assert_internal(segment->used > 0);
+#ifdef Py_GIL_DISABLED
+ mi_assert_internal(page->qsbr_goal == 0);
+ mi_assert_internal(page->qsbr_node.next == NULL);
+#endif
+
+ size_t inuse = page->capacity * mi_page_block_size(page);
+ _mi_stat_decrease(&tld->stats->page_committed, inuse);
+ _mi_stat_decrease(&tld->stats->pages, 1);
+
+ // reset the page memory to reduce memory pressure?
+ if (segment->allow_decommit && mi_option_is_enabled(mi_option_deprecated_page_reset)) {
+ size_t psize;
+ uint8_t* start = _mi_page_start(segment, page, &psize);
+ _mi_os_reset(start, psize, tld->stats);
+ }
+
+ // zero the page data, but not the segment fields
+ page->is_zero_init = false;
+ ptrdiff_t ofs = offsetof(mi_page_t, capacity);
+ _mi_memzero((uint8_t*)page + ofs, sizeof(*page) - ofs);
+ page->xblock_size = 1;
+
+ // and free it
+ mi_slice_t* slice = mi_segment_span_free_coalesce(mi_page_to_slice(page), tld);
+ segment->used--;
+ // cannot assert segment valid as it is called during reclaim
+ // mi_assert_expensive(mi_segment_is_valid(segment, tld));
+ return slice;
+}
+
+void _mi_segment_page_free(mi_page_t* page, bool force, mi_segments_tld_t* tld)
+{
+ mi_assert(page != NULL);
+
+ mi_segment_t* segment = _mi_page_segment(page);
+ mi_assert_expensive(mi_segment_is_valid(segment,tld));
+
+ // mark it as free now
+ mi_segment_page_clear(page, tld);
+ mi_assert_expensive(mi_segment_is_valid(segment, tld));
+
+ if (segment->used == 0) {
+ // no more used pages; remove from the free list and free the segment
+ mi_segment_free(segment, force, tld);
+ }
+ else if (segment->used == segment->abandoned) {
+ // only abandoned pages; remove from free list and abandon
+ mi_segment_abandon(segment,tld);
+ }
+}
+
+
+/* -----------------------------------------------------------
+Abandonment
+
+When threads terminate, they can leave segments with
+live blocks (reachable through other threads). Such segments
+are "abandoned" and will be reclaimed by other threads to
+reuse their pages and/or free them eventually
+
+We maintain a global list of abandoned segments that are
+reclaimed on demand. Since this is shared among threads
+the implementation needs to avoid the A-B-A problem on
+popping abandoned segments: <https://en.wikipedia.org/wiki/ABA_problem>
+We use tagged pointers to avoid accidentally identifying
+reused segments, much like stamped references in Java.
+Secondly, we maintain a reader counter to avoid resetting
+or decommitting segments that have a pending read operation.
+
+Note: the current implementation is one possible design;
+another way might be to keep track of abandoned segments
+in the arenas/segment_cache's. This would have the advantage of keeping
+all concurrent code in one place and not needing to deal
+with ABA issues. The drawback is that it is unclear how to
+scan abandoned segments efficiently in that case as they
+would be spread among all other segments in the arenas.
+----------------------------------------------------------- */
+
+// Use the bottom 20-bits (on 64-bit) of the aligned segment pointers
+// to put in a tag that increments on update to avoid the A-B-A problem.
+#define MI_TAGGED_MASK MI_SEGMENT_MASK
+
+static mi_segment_t* mi_tagged_segment_ptr(mi_tagged_segment_t ts) {
+ return (mi_segment_t*)(ts & ~MI_TAGGED_MASK);
+}
+
+static mi_tagged_segment_t mi_tagged_segment(mi_segment_t* segment, mi_tagged_segment_t ts) {
+ mi_assert_internal(((uintptr_t)segment & MI_TAGGED_MASK) == 0);
+ uintptr_t tag = ((ts & MI_TAGGED_MASK) + 1) & MI_TAGGED_MASK;
+ return ((uintptr_t)segment | tag);
+}
+
+mi_abandoned_pool_t _mi_abandoned_default;
+
+// Push on the visited list
+static void mi_abandoned_visited_push(mi_abandoned_pool_t *pool, mi_segment_t* segment) {
+ mi_assert_internal(segment->thread_id == 0);
+ mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_segment_t,&segment->abandoned_next) == NULL);
+ mi_assert_internal(segment->next == NULL);
+ mi_assert_internal(segment->used > 0);
+ mi_segment_t* anext = mi_atomic_load_ptr_relaxed(mi_segment_t, &pool->abandoned_visited);
+ do {
+ mi_atomic_store_ptr_release(mi_segment_t, &segment->abandoned_next, anext);
+ } while (!mi_atomic_cas_ptr_weak_release(mi_segment_t, &pool->abandoned_visited, &anext, segment));
+ mi_atomic_increment_relaxed(&pool->abandoned_visited_count);
+}
+
+// Move the visited list to the abandoned list.
+static bool mi_abandoned_visited_revisit(mi_abandoned_pool_t *pool)
+{
+ // quick check if the visited list is empty
+ if (mi_atomic_load_ptr_relaxed(mi_segment_t, &pool->abandoned_visited) == NULL) return false;
+
+ // grab the whole visited list
+ mi_segment_t* first = mi_atomic_exchange_ptr_acq_rel(mi_segment_t, &pool->abandoned_visited, NULL);
+ if (first == NULL) return false;
+
+ // first try to swap directly if the abandoned list happens to be NULL
+ mi_tagged_segment_t afirst;
+ mi_tagged_segment_t ts = mi_atomic_load_relaxed(&pool->abandoned);
+ if (mi_tagged_segment_ptr(ts)==NULL) {
+ size_t count = mi_atomic_load_relaxed(&pool->abandoned_visited_count);
+ afirst = mi_tagged_segment(first, ts);
+ if (mi_atomic_cas_strong_acq_rel(&pool->abandoned, &ts, afirst)) {
+ mi_atomic_add_relaxed(&pool->abandoned_count, count);
+ mi_atomic_sub_relaxed(&pool->abandoned_visited_count, count);
+ return true;
+ }
+ }
+
+ // find the last element of the visited list: O(n)
+ mi_segment_t* last = first;
+ mi_segment_t* next;
+ while ((next = mi_atomic_load_ptr_relaxed(mi_segment_t, &last->abandoned_next)) != NULL) {
+ last = next;
+ }
+
+ // and atomically prepend to the abandoned list
+ // (no need to increase the readers as we don't access the abandoned segments)
+ mi_tagged_segment_t anext = mi_atomic_load_relaxed(&pool->abandoned);
+ size_t count;
+ do {
+ count = mi_atomic_load_relaxed(&pool->abandoned_visited_count);
+ mi_atomic_store_ptr_release(mi_segment_t, &last->abandoned_next, mi_tagged_segment_ptr(anext));
+ afirst = mi_tagged_segment(first, anext);
+ } while (!mi_atomic_cas_weak_release(&pool->abandoned, &anext, afirst));
+ mi_atomic_add_relaxed(&pool->abandoned_count, count);
+ mi_atomic_sub_relaxed(&pool->abandoned_visited_count, count);
+ return true;
+}
+
+// Push on the abandoned list.
+static void mi_abandoned_push(mi_abandoned_pool_t* pool, mi_segment_t* segment) {
+ mi_assert_internal(segment->thread_id == 0);
+ mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_segment_t, &segment->abandoned_next) == NULL);
+ mi_assert_internal(segment->next == NULL);
+ mi_assert_internal(segment->used > 0);
+ mi_tagged_segment_t next;
+ mi_tagged_segment_t ts = mi_atomic_load_relaxed(&pool->abandoned);
+ do {
+ mi_atomic_store_ptr_release(mi_segment_t, &segment->abandoned_next, mi_tagged_segment_ptr(ts));
+ next = mi_tagged_segment(segment, ts);
+ } while (!mi_atomic_cas_weak_release(&pool->abandoned, &ts, next));
+ mi_atomic_increment_relaxed(&pool->abandoned_count);
+}
+
+// Wait until there are no more pending reads on segments that used to be in the abandoned list
+// called for example from `arena.c` before decommitting
+void _mi_abandoned_await_readers(mi_abandoned_pool_t* pool) {
+ size_t n;
+ do {
+ n = mi_atomic_load_acquire(&pool->abandoned_readers);
+ if (n != 0) mi_atomic_yield();
+ } while (n != 0);
+}
+
+// Pop from the abandoned list
+static mi_segment_t* mi_abandoned_pop(mi_abandoned_pool_t* pool) {
+ mi_segment_t* segment;
+ // Check efficiently if it is empty (or if the visited list needs to be moved)
+ mi_tagged_segment_t ts = mi_atomic_load_relaxed(&pool->abandoned);
+ segment = mi_tagged_segment_ptr(ts);
+ if mi_likely(segment == NULL) {
+ if mi_likely(!mi_abandoned_visited_revisit(pool)) { // try to swap in the visited list on NULL
+ return NULL;
+ }
+ }
+
+ // Do a pop. We use a reader count to prevent
+ // a segment to be decommitted while a read is still pending,
+ // and a tagged pointer to prevent A-B-A link corruption.
+ // (this is called from `region.c:_mi_mem_free` for example)
+ mi_atomic_increment_relaxed(&pool->abandoned_readers); // ensure no segment gets decommitted
+ mi_tagged_segment_t next = 0;
+ ts = mi_atomic_load_acquire(&pool->abandoned);
+ do {
+ segment = mi_tagged_segment_ptr(ts);
+ if (segment != NULL) {
+ mi_segment_t* anext = mi_atomic_load_ptr_relaxed(mi_segment_t, &segment->abandoned_next);
+ next = mi_tagged_segment(anext, ts); // note: reads the segment's `abandoned_next` field so should not be decommitted
+ }
+ } while (segment != NULL && !mi_atomic_cas_weak_acq_rel(&pool->abandoned, &ts, next));
+ mi_atomic_decrement_relaxed(&pool->abandoned_readers); // release reader lock
+ if (segment != NULL) {
+ mi_atomic_store_ptr_release(mi_segment_t, &segment->abandoned_next, NULL);
+ mi_atomic_decrement_relaxed(&pool->abandoned_count);
+ }
+ return segment;
+}
+
+/* -----------------------------------------------------------
+ Abandon segment/page
+----------------------------------------------------------- */
+
+static void mi_segment_abandon(mi_segment_t* segment, mi_segments_tld_t* tld) {
+ mi_assert_internal(segment->used == segment->abandoned);
+ mi_assert_internal(segment->used > 0);
+ mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_segment_t, &segment->abandoned_next) == NULL);
+ mi_assert_internal(segment->abandoned_visits == 0);
+ mi_assert_expensive(mi_segment_is_valid(segment,tld));
+
+ // remove the free pages from the free page queues
+ mi_slice_t* slice = &segment->slices[0];
+ const mi_slice_t* end = mi_segment_slices_end(segment);
+ while (slice < end) {
+ mi_assert_internal(slice->slice_count > 0);
+ mi_assert_internal(slice->slice_offset == 0);
+ if (slice->xblock_size == 0) { // a free page
+ mi_segment_span_remove_from_queue(slice,tld);
+ slice->xblock_size = 0; // but keep it free
+ }
+ slice = slice + slice->slice_count;
+ }
+
+ // perform delayed decommits (forcing is much slower on mstress)
+ mi_segment_try_purge(segment, mi_option_is_enabled(mi_option_abandoned_page_purge) /* force? */, tld->stats);
+
+ // all pages in the segment are abandoned; add it to the abandoned list
+ _mi_stat_increase(&tld->stats->segments_abandoned, 1);
+ mi_segments_track_size(-((long)mi_segment_size(segment)), tld);
+ segment->thread_id = 0;
+ mi_atomic_store_ptr_release(mi_segment_t, &segment->abandoned_next, NULL);
+ segment->abandoned_visits = 1; // from 0 to 1 to signify it is abandoned
+ mi_abandoned_push(tld->abandoned, segment);
+}
+
+void _mi_segment_page_abandon(mi_page_t* page, mi_segments_tld_t* tld) {
+ mi_assert(page != NULL);
+ mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE);
+ mi_assert_internal(mi_page_heap(page) == NULL);
+ mi_segment_t* segment = _mi_page_segment(page);
+
+ mi_assert_expensive(mi_segment_is_valid(segment,tld));
+ segment->abandoned++;
+
+ _mi_stat_increase(&tld->stats->pages_abandoned, 1);
+ mi_assert_internal(segment->abandoned <= segment->used);
+ if (segment->used == segment->abandoned) {
+ // all pages are abandoned, abandon the entire segment
+ mi_segment_abandon(segment, tld);
+ }
+}
+
+/* -----------------------------------------------------------
+ Reclaim abandoned pages
+----------------------------------------------------------- */
+
+static mi_slice_t* mi_slices_start_iterate(mi_segment_t* segment, const mi_slice_t** end) {
+ mi_slice_t* slice = &segment->slices[0];
+ *end = mi_segment_slices_end(segment);
+ mi_assert_internal(slice->slice_count>0 && slice->xblock_size>0); // segment allocated page
+ slice = slice + slice->slice_count; // skip the first segment allocated page
+ return slice;
+}
+
+// Possibly free pages and check if free space is available
+static bool mi_segment_check_free(mi_segment_t* segment, size_t slices_needed, size_t block_size, mi_segments_tld_t* tld)
+{
+ mi_assert_internal(block_size < MI_HUGE_BLOCK_SIZE);
+ mi_assert_internal(mi_segment_is_abandoned(segment));
+ bool has_page = false;
+
+ // for all slices
+ const mi_slice_t* end;
+ mi_slice_t* slice = mi_slices_start_iterate(segment, &end);
+ while (slice < end) {
+ mi_assert_internal(slice->slice_count > 0);
+ mi_assert_internal(slice->slice_offset == 0);
+ if (mi_slice_is_used(slice)) { // used page
+ // ensure used count is up to date and collect potential concurrent frees
+ mi_page_t* const page = mi_slice_to_page(slice);
+ _mi_page_free_collect(page, false);
+ if (mi_page_all_free(page) && _PyMem_mi_page_is_safe_to_free(page)) {
+ // if this page is all free now, free it without adding to any queues (yet)
+ mi_assert_internal(page->next == NULL && page->prev==NULL);
+ _mi_stat_decrease(&tld->stats->pages_abandoned, 1);
+#ifdef Py_GIL_DISABLED
+ page->qsbr_goal = 0;
+#endif
+ segment->abandoned--;
+ slice = mi_segment_page_clear(page, tld); // re-assign slice due to coalesce!
+ mi_assert_internal(!mi_slice_is_used(slice));
+ if (slice->slice_count >= slices_needed) {
+ has_page = true;
+ }
+ }
+ else {
+ if (page->xblock_size == block_size && mi_page_has_any_available(page)) {
+ // a page has available free blocks of the right size
+ has_page = true;
+ }
+ }
+ }
+ else {
+ // empty span
+ if (slice->slice_count >= slices_needed) {
+ has_page = true;
+ }
+ }
+ slice = slice + slice->slice_count;
+ }
+ return has_page;
+}
+
+static mi_heap_t* mi_heap_by_tag(mi_heap_t* heap, uint8_t tag) {
+ if (heap->tag == tag) {
+ return heap;
+ }
+ for (mi_heap_t *curr = heap->tld->heaps; curr != NULL; curr = curr->next) {
+ if (curr->tag == tag) {
+ return curr;
+ }
+ }
+ return NULL;
+}
+
+// Reclaim an abandoned segment; returns NULL if the segment was freed
+// set `right_page_reclaimed` to `true` if it reclaimed a page of the right `block_size` that was not full.
+static mi_segment_t* mi_segment_reclaim(mi_segment_t* segment, mi_heap_t* heap, size_t requested_block_size, bool* right_page_reclaimed, mi_segments_tld_t* tld) {
+ mi_assert_internal(mi_atomic_load_ptr_relaxed(mi_segment_t, &segment->abandoned_next) == NULL);
+ mi_assert_expensive(mi_segment_is_valid(segment, tld));
+ if (right_page_reclaimed != NULL) { *right_page_reclaimed = false; }
+
+ segment->thread_id = _mi_thread_id();
+ segment->abandoned_visits = 0;
+ mi_segments_track_size((long)mi_segment_size(segment), tld);
+ mi_assert_internal(segment->next == NULL);
+ _mi_stat_decrease(&tld->stats->segments_abandoned, 1);
+
+ // for all slices
+ const mi_slice_t* end;
+ mi_slice_t* slice = mi_slices_start_iterate(segment, &end);
+ while (slice < end) {
+ mi_assert_internal(slice->slice_count > 0);
+ mi_assert_internal(slice->slice_offset == 0);
+ if (mi_slice_is_used(slice)) {
+ // in use: reclaim the page in our heap
+ mi_page_t* page = mi_slice_to_page(slice);
+ mi_heap_t* target_heap = mi_heap_by_tag(heap, page->tag);
+ mi_assert_internal(page->is_committed);
+ mi_assert_internal(mi_page_thread_free_flag(page)==MI_NEVER_DELAYED_FREE);
+ mi_assert_internal(mi_page_heap(page) == NULL);
+ mi_assert_internal(page->next == NULL && page->prev==NULL);
+ _mi_stat_decrease(&tld->stats->pages_abandoned, 1);
+ segment->abandoned--;
+ // set the heap again and allow delayed free again
+ mi_page_set_heap(page, target_heap);
+ _mi_page_use_delayed_free(page, MI_USE_DELAYED_FREE, true); // override never (after heap is set)
+ _mi_page_free_collect(page, false); // ensure used count is up to date
+ if (mi_page_all_free(page) && _PyMem_mi_page_is_safe_to_free(page)) {
+ // if everything free by now, free the page
+#ifdef Py_GIL_DISABLED
+ page->qsbr_goal = 0;
+#endif
+ slice = mi_segment_page_clear(page, tld); // set slice again due to coalesceing
+ }
+ else {
+ // otherwise reclaim it into the heap
+ _mi_page_reclaim(target_heap, page);
+ if (requested_block_size == page->xblock_size && mi_page_has_any_available(page) &&
+ requested_block_size <= MI_MEDIUM_OBJ_SIZE_MAX && heap == target_heap) {
+ if (right_page_reclaimed != NULL) { *right_page_reclaimed = true; }
+ }
+ }
+ }
+ else {
+ // the span is free, add it to our page queues
+ slice = mi_segment_span_free_coalesce(slice, tld); // set slice again due to coalesceing
+ }
+ mi_assert_internal(slice->slice_count>0 && slice->slice_offset==0);
+ slice = slice + slice->slice_count;
+ }
+
+ mi_assert(segment->abandoned == 0);
+ if (segment->used == 0) { // due to page_clear
+ mi_assert_internal(right_page_reclaimed == NULL || !(*right_page_reclaimed));
+ mi_segment_free(segment, false, tld);
+ return NULL;
+ }
+ else {
+ return segment;
+ }
+}
+
+
+void _mi_abandoned_reclaim_all(mi_heap_t* heap, mi_segments_tld_t* tld) {
+ mi_segment_t* segment;
+ while ((segment = mi_abandoned_pop(tld->abandoned)) != NULL) {
+ mi_segment_reclaim(segment, heap, 0, NULL, tld);
+ }
+}
+
+static mi_segment_t* mi_segment_try_reclaim(mi_heap_t* heap, size_t needed_slices, size_t block_size, bool* reclaimed, mi_segments_tld_t* tld)
+{
+ *reclaimed = false;
+ mi_segment_t* segment;
+ long max_tries = mi_option_get_clamp(mi_option_max_segment_reclaim, 8, 1024); // limit the work to bound allocation times
+ while ((max_tries-- > 0) && ((segment = mi_abandoned_pop(tld->abandoned)) != NULL)) {
+ segment->abandoned_visits++;
+ // todo: an arena exclusive heap will potentially visit many abandoned unsuitable segments
+ // and push them into the visited list and use many tries. Perhaps we can skip non-suitable ones in a better way?
+ bool is_suitable = _mi_heap_memid_is_suitable(heap, segment->memid);
+ bool has_page = mi_segment_check_free(segment,needed_slices,block_size,tld); // try to free up pages (due to concurrent frees)
+ if (segment->used == 0) {
+ // free the segment (by forced reclaim) to make it available to other threads.
+ // note1: we prefer to free a segment as that might lead to reclaiming another
+ // segment that is still partially used.
+ // note2: we could in principle optimize this by skipping reclaim and directly
+ // freeing but that would violate some invariants temporarily)
+ mi_segment_reclaim(segment, heap, 0, NULL, tld);
+ }
+ else if (has_page && is_suitable) {
+ // found a large enough free span, or a page of the right block_size with free space
+ // we return the result of reclaim (which is usually `segment`) as it might free
+ // the segment due to concurrent frees (in which case `NULL` is returned).
+ return mi_segment_reclaim(segment, heap, block_size, reclaimed, tld);
+ }
+ else if (segment->abandoned_visits > 3 && is_suitable) {
+ // always reclaim on 3rd visit to limit the abandoned queue length.
+ mi_segment_reclaim(segment, heap, 0, NULL, tld);
+ }
+ else {
+ // otherwise, push on the visited list so it gets not looked at too quickly again
+ mi_segment_try_purge(segment, true /* force? */, tld->stats); // force purge if needed as we may not visit soon again
+ mi_abandoned_visited_push(tld->abandoned, segment);
+ }
+ }
+ return NULL;
+}
+
+
+void _mi_abandoned_collect(mi_heap_t* heap, bool force, mi_segments_tld_t* tld)
+{
+ mi_segment_t* segment;
+ mi_abandoned_pool_t* pool = tld->abandoned;
+ int max_tries = (force ? 16*1024 : 1024); // limit latency
+ if (force) {
+ mi_abandoned_visited_revisit(pool);
+ }
+ while ((max_tries-- > 0) && ((segment = mi_abandoned_pop(pool)) != NULL)) {
+ mi_segment_check_free(segment,0,0,tld); // try to free up pages (due to concurrent frees)
+ if (segment->used == 0) {
+ // free the segment (by forced reclaim) to make it available to other threads.
+ // note: we could in principle optimize this by skipping reclaim and directly
+ // freeing but that would violate some invariants temporarily)
+ mi_segment_reclaim(segment, heap, 0, NULL, tld);
+ }
+ else {
+ // otherwise, purge if needed and push on the visited list
+ // note: forced purge can be expensive if many threads are destroyed/created as in mstress.
+ mi_segment_try_purge(segment, force, tld->stats);
+ mi_abandoned_visited_push(pool, segment);
+ }
+ }
+}
+
+/* -----------------------------------------------------------
+ Reclaim or allocate
+----------------------------------------------------------- */
+
+static mi_segment_t* mi_segment_reclaim_or_alloc(mi_heap_t* heap, size_t needed_slices, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld)
+{
+ mi_assert_internal(block_size < MI_HUGE_BLOCK_SIZE);
+ mi_assert_internal(block_size <= MI_LARGE_OBJ_SIZE_MAX);
+
+ // 1. try to reclaim an abandoned segment
+ bool reclaimed;
+ mi_segment_t* segment = mi_segment_try_reclaim(heap, needed_slices, block_size, &reclaimed, tld);
+ if (reclaimed) {
+ // reclaimed the right page right into the heap
+ mi_assert_internal(segment != NULL);
+ return NULL; // pretend out-of-memory as the page will be in the page queue of the heap with available blocks
+ }
+ else if (segment != NULL) {
+ // reclaimed a segment with a large enough empty span in it
+ return segment;
+ }
+ // 2. otherwise allocate a fresh segment
+ return mi_segment_alloc(0, 0, heap->arena_id, tld, os_tld, NULL);
+}
+
+
+/* -----------------------------------------------------------
+ Page allocation
+----------------------------------------------------------- */
+
+static mi_page_t* mi_segments_page_alloc(mi_heap_t* heap, mi_page_kind_t page_kind, size_t required, size_t block_size, mi_segments_tld_t* tld, mi_os_tld_t* os_tld)
+{
+ mi_assert_internal(required <= MI_LARGE_OBJ_SIZE_MAX && page_kind <= MI_PAGE_LARGE);
+
+ // find a free page
+ size_t page_size = _mi_align_up(required, (required > MI_MEDIUM_PAGE_SIZE ? MI_MEDIUM_PAGE_SIZE : MI_SEGMENT_SLICE_SIZE));
+ size_t slices_needed = page_size / MI_SEGMENT_SLICE_SIZE;
+ mi_assert_internal(slices_needed * MI_SEGMENT_SLICE_SIZE == page_size);
+ mi_page_t* page = mi_segments_page_find_and_allocate(slices_needed, heap->arena_id, tld); //(required <= MI_SMALL_SIZE_MAX ? 0 : slices_needed), tld);
+ if (page==NULL) {
+ // no free page, allocate a new segment and try again
+ if (mi_segment_reclaim_or_alloc(heap, slices_needed, block_size, tld, os_tld) == NULL) {
+ // OOM or reclaimed a good page in the heap
+ return NULL;
+ }
+ else {
+ // otherwise try again
+ return mi_segments_page_alloc(heap, page_kind, required, block_size, tld, os_tld);
+ }
+ }
+ mi_assert_internal(page != NULL && page->slice_count*MI_SEGMENT_SLICE_SIZE == page_size);
+ mi_assert_internal(_mi_ptr_segment(page)->thread_id == _mi_thread_id());
+ mi_segment_try_purge(_mi_ptr_segment(page), false, tld->stats);
+ return page;
+}
+
+
+
+/* -----------------------------------------------------------
+ Huge page allocation
+----------------------------------------------------------- */
+
+static mi_page_t* mi_segment_huge_page_alloc(size_t size, size_t page_alignment, mi_arena_id_t req_arena_id, mi_segments_tld_t* tld, mi_os_tld_t* os_tld)
+{
+ mi_page_t* page = NULL;
+ mi_segment_t* segment = mi_segment_alloc(size,page_alignment,req_arena_id,tld,os_tld,&page);
+ if (segment == NULL || page==NULL) return NULL;
+ mi_assert_internal(segment->used==1);
+ mi_assert_internal(mi_page_block_size(page) >= size);
+ #if MI_HUGE_PAGE_ABANDON
+ segment->thread_id = 0; // huge segments are immediately abandoned
+ #endif
+
+ // for huge pages we initialize the xblock_size as we may
+ // overallocate to accommodate large alignments.
+ size_t psize;
+ uint8_t* start = _mi_segment_page_start(segment, page, &psize);
+ page->xblock_size = (psize > MI_HUGE_BLOCK_SIZE ? MI_HUGE_BLOCK_SIZE : (uint32_t)psize);
+
+ // decommit the part of the prefix of a page that will not be used; this can be quite large (close to MI_SEGMENT_SIZE)
+ if (page_alignment > 0 && segment->allow_decommit) {
+ uint8_t* aligned_p = (uint8_t*)_mi_align_up((uintptr_t)start, page_alignment);
+ mi_assert_internal(_mi_is_aligned(aligned_p, page_alignment));
+ mi_assert_internal(psize - (aligned_p - start) >= size);
+ uint8_t* decommit_start = start + sizeof(mi_block_t); // for the free list
+ ptrdiff_t decommit_size = aligned_p - decommit_start;
+ _mi_os_reset(decommit_start, decommit_size, &_mi_stats_main); // note: cannot use segment_decommit on huge segments
+ }
+
+ return page;
+}
+
+#if MI_HUGE_PAGE_ABANDON
+// free huge block from another thread
+void _mi_segment_huge_page_free(mi_segment_t* segment, mi_page_t* page, mi_block_t* block) {
+ // huge page segments are always abandoned and can be freed immediately by any thread
+ mi_assert_internal(segment->kind==MI_SEGMENT_HUGE);
+ mi_assert_internal(segment == _mi_page_segment(page));
+ mi_assert_internal(mi_atomic_load_relaxed(&segment->thread_id)==0);
+
+ // claim it and free
+ mi_heap_t* heap = mi_heap_get_default(); // issue #221; don't use the internal get_default_heap as we need to ensure the thread is initialized.
+ // paranoia: if this it the last reference, the cas should always succeed
+ size_t expected_tid = 0;
+ if (mi_atomic_cas_strong_acq_rel(&segment->thread_id, &expected_tid, heap->thread_id)) {
+ mi_block_set_next(page, block, page->free);
+ page->free = block;
+ page->used--;
+ page->is_zero = false;
+ mi_assert(page->used == 0);
+ mi_tld_t* tld = heap->tld;
+ _mi_segment_page_free(page, true, &tld->segments);
+ }
+#if (MI_DEBUG!=0)
+ else {
+ mi_assert_internal(false);
+ }
+#endif
+}
+
+#else
+// reset memory of a huge block from another thread
+void _mi_segment_huge_page_reset(mi_segment_t* segment, mi_page_t* page, mi_block_t* block) {
+ MI_UNUSED(page);
+ mi_assert_internal(segment->kind == MI_SEGMENT_HUGE);
+ mi_assert_internal(segment == _mi_page_segment(page));
+ mi_assert_internal(page->used == 1); // this is called just before the free
+ mi_assert_internal(page->free == NULL);
+ if (segment->allow_decommit) {
+ size_t csize = mi_usable_size(block);
+ if (csize > sizeof(mi_block_t)) {
+ csize = csize - sizeof(mi_block_t);
+ uint8_t* p = (uint8_t*)block + sizeof(mi_block_t);
+ _mi_os_reset(p, csize, &_mi_stats_main); // note: cannot use segment_decommit on huge segments
+ }
+ }
+}
+#endif
+
+/* -----------------------------------------------------------
+ Page allocation and free
+----------------------------------------------------------- */
+mi_page_t* _mi_segment_page_alloc(mi_heap_t* heap, size_t block_size, size_t page_alignment, mi_segments_tld_t* tld, mi_os_tld_t* os_tld) {
+ mi_page_t* page;
+ if mi_unlikely(page_alignment > MI_ALIGNMENT_MAX) {
+ mi_assert_internal(_mi_is_power_of_two(page_alignment));
+ mi_assert_internal(page_alignment >= MI_SEGMENT_SIZE);
+ if (page_alignment < MI_SEGMENT_SIZE) { page_alignment = MI_SEGMENT_SIZE; }
+ page = mi_segment_huge_page_alloc(block_size,page_alignment,heap->arena_id,tld,os_tld);
+ }
+ else if (block_size <= MI_SMALL_OBJ_SIZE_MAX) {
+ page = mi_segments_page_alloc(heap,MI_PAGE_SMALL,block_size,block_size,tld,os_tld);
+ }
+ else if (block_size <= MI_MEDIUM_OBJ_SIZE_MAX) {
+ page = mi_segments_page_alloc(heap,MI_PAGE_MEDIUM,MI_MEDIUM_PAGE_SIZE,block_size,tld, os_tld);
+ }
+ else if (block_size <= MI_LARGE_OBJ_SIZE_MAX) {
+ page = mi_segments_page_alloc(heap,MI_PAGE_LARGE,block_size,block_size,tld, os_tld);
+ }
+ else {
+ page = mi_segment_huge_page_alloc(block_size,page_alignment,heap->arena_id,tld,os_tld);
+ }
+ mi_assert_internal(page == NULL || _mi_heap_memid_is_suitable(heap, _mi_page_segment(page)->memid));
+ mi_assert_expensive(page == NULL || mi_segment_is_valid(_mi_page_segment(page),tld));
+ return page;
+}
+
+/* -----------------------------------------------------------
+ Visit blocks in abandoned segments
+----------------------------------------------------------- */
+
+static bool mi_segment_visit_page(mi_segment_t* segment, mi_page_t* page, bool visit_blocks, mi_block_visit_fun* visitor, void* arg)
+{
+ mi_heap_area_t area;
+ _mi_heap_area_init(&area, page);
+ if (!visitor(NULL, &area, NULL, area.block_size, arg)) return false;
+ if (visit_blocks) {
+ return _mi_heap_area_visit_blocks(&area, page, visitor, arg);
+ }
+ else {
+ return true;
+ }
+}
+
+static bool mi_segment_visit_pages(mi_segment_t* segment, uint8_t page_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) {
+ const mi_slice_t* end;
+ mi_slice_t* slice = mi_slices_start_iterate(segment, &end);
+ while (slice < end) {
+ if (mi_slice_is_used(slice)) {
+ mi_page_t* const page = mi_slice_to_page(slice);
+ if (page->tag == page_tag) {
+ if (!mi_segment_visit_page(segment, page, visit_blocks, visitor, arg)) return false;
+ }
+ }
+ slice = slice + slice->slice_count;
+ }
+ return true;
+}
+
+// Visit all blocks in a abandoned segments
+bool _mi_abandoned_pool_visit_blocks(mi_abandoned_pool_t* pool, uint8_t page_tag, bool visit_blocks, mi_block_visit_fun* visitor, void* arg) {
+ // Note: this is not safe in any other thread is abandoning or claiming segments from the pool
+ mi_segment_t* segment = mi_tagged_segment_ptr(pool->abandoned);
+ while (segment != NULL) {
+ if (!mi_segment_visit_pages(segment, page_tag, visit_blocks, visitor, arg)) return false;
+ segment = segment->abandoned_next;
+ }
+
+ segment = pool->abandoned_visited;
+ while (segment != NULL) {
+ if (!mi_segment_visit_pages(segment, page_tag, visit_blocks, visitor, arg)) return false;
+ segment = segment->abandoned_next;
+ }
+
+ return true;
+}
diff --git a/contrib/tools/python3/Objects/mimalloc/static.c b/contrib/tools/python3/Objects/mimalloc/static.c
new file mode 100644
index 00000000000..98c4aa18e45
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/static.c
@@ -0,0 +1,40 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2020, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#ifndef _DEFAULT_SOURCE
+#define _DEFAULT_SOURCE
+#endif
+#if defined(__sun)
+// same remarks as os.c for the static's context.
+#undef _XOPEN_SOURCE
+#undef _POSIX_C_SOURCE
+#endif
+
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+
+// For a static override we create a single object file
+// containing the whole library. If it is linked first
+// it will override all the standard library allocation
+// functions (on Unix's).
+#include "alloc.c" // includes alloc-override.c
+#include "alloc-aligned.c"
+#include "alloc-posix.c"
+#include "arena.c"
+#include "bitmap.c"
+#include "heap.c"
+#include "init.c"
+#include "options.c"
+#include "os.c"
+#include "page.c" // includes page-queue.c
+#include "random.c"
+#include "segment.c"
+#include "segment-map.c"
+#include "stats.c"
+#include "prim/prim.c"
+#if MI_OSX_ZONE
+#include "prim/osx/alloc-override-zone.c"
+#endif
diff --git a/contrib/tools/python3/Objects/mimalloc/stats.c b/contrib/tools/python3/Objects/mimalloc/stats.c
new file mode 100644
index 00000000000..e7ea397ec4c
--- /dev/null
+++ b/contrib/tools/python3/Objects/mimalloc/stats.c
@@ -0,0 +1,467 @@
+/* ----------------------------------------------------------------------------
+Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
+This is free software; you can redistribute it and/or modify it under the
+terms of the MIT license. A copy of the license can be found in the file
+"LICENSE" at the root of this distribution.
+-----------------------------------------------------------------------------*/
+#include "mimalloc.h"
+#include "mimalloc/internal.h"
+#include "mimalloc/atomic.h"
+#include "mimalloc/prim.h"
+
+#include <stdio.h> // snprintf
+#include <string.h> // memset
+
+#if defined(_MSC_VER) && (_MSC_VER < 1920)
+#pragma warning(disable:4204) // non-constant aggregate initializer
+#endif
+
+/* -----------------------------------------------------------
+ Statistics operations
+----------------------------------------------------------- */
+
+static bool mi_is_in_main(void* stat) {
+ return ((uint8_t*)stat >= (uint8_t*)&_mi_stats_main
+ && (uint8_t*)stat < ((uint8_t*)&_mi_stats_main + sizeof(mi_stats_t)));
+}
+
+static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) {
+ if (amount == 0) return;
+ if (mi_is_in_main(stat))
+ {
+ // add atomically (for abandoned pages)
+ int64_t current = mi_atomic_addi64_relaxed(&stat->current, amount);
+ mi_atomic_maxi64_relaxed(&stat->peak, current + amount);
+ if (amount > 0) {
+ mi_atomic_addi64_relaxed(&stat->allocated,amount);
+ }
+ else {
+ mi_atomic_addi64_relaxed(&stat->freed, -amount);
+ }
+ }
+ else {
+ // add thread local
+ stat->current += amount;
+ if (stat->current > stat->peak) stat->peak = stat->current;
+ if (amount > 0) {
+ stat->allocated += amount;
+ }
+ else {
+ stat->freed += -amount;
+ }
+ }
+}
+
+void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) {
+ if (mi_is_in_main(stat)) {
+ mi_atomic_addi64_relaxed( &stat->count, 1 );
+ mi_atomic_addi64_relaxed( &stat->total, (int64_t)amount );
+ }
+ else {
+ stat->count++;
+ stat->total += amount;
+ }
+}
+
+void _mi_stat_increase(mi_stat_count_t* stat, size_t amount) {
+ mi_stat_update(stat, (int64_t)amount);
+}
+
+void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount) {
+ mi_stat_update(stat, -((int64_t)amount));
+}
+
+// must be thread safe as it is called from stats_merge
+static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64_t unit) {
+ if (stat==src) return;
+ if (src->allocated==0 && src->freed==0) return;
+ mi_atomic_addi64_relaxed( &stat->allocated, src->allocated * unit);
+ mi_atomic_addi64_relaxed( &stat->current, src->current * unit);
+ mi_atomic_addi64_relaxed( &stat->freed, src->freed * unit);
+ // peak scores do not work across threads..
+ mi_atomic_addi64_relaxed( &stat->peak, src->peak * unit);
+}
+
+static void mi_stat_counter_add(mi_stat_counter_t* stat, const mi_stat_counter_t* src, int64_t unit) {
+ if (stat==src) return;
+ mi_atomic_addi64_relaxed( &stat->total, src->total * unit);
+ mi_atomic_addi64_relaxed( &stat->count, src->count * unit);
+}
+
+// must be thread safe as it is called from stats_merge
+static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) {
+ if (stats==src) return;
+ mi_stat_add(&stats->segments, &src->segments,1);
+ mi_stat_add(&stats->pages, &src->pages,1);
+ mi_stat_add(&stats->reserved, &src->reserved, 1);
+ mi_stat_add(&stats->committed, &src->committed, 1);
+ mi_stat_add(&stats->reset, &src->reset, 1);
+ mi_stat_add(&stats->purged, &src->purged, 1);
+ mi_stat_add(&stats->page_committed, &src->page_committed, 1);
+
+ mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1);
+ mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1);
+ mi_stat_add(&stats->threads, &src->threads, 1);
+
+ mi_stat_add(&stats->malloc, &src->malloc, 1);
+ mi_stat_add(&stats->segments_cache, &src->segments_cache, 1);
+ mi_stat_add(&stats->normal, &src->normal, 1);
+ mi_stat_add(&stats->huge, &src->huge, 1);
+ mi_stat_add(&stats->large, &src->large, 1);
+
+ mi_stat_counter_add(&stats->pages_extended, &src->pages_extended, 1);
+ mi_stat_counter_add(&stats->mmap_calls, &src->mmap_calls, 1);
+ mi_stat_counter_add(&stats->commit_calls, &src->commit_calls, 1);
+ mi_stat_counter_add(&stats->reset_calls, &src->reset_calls, 1);
+ mi_stat_counter_add(&stats->purge_calls, &src->purge_calls, 1);
+
+ mi_stat_counter_add(&stats->page_no_retire, &src->page_no_retire, 1);
+ mi_stat_counter_add(&stats->searches, &src->searches, 1);
+ mi_stat_counter_add(&stats->normal_count, &src->normal_count, 1);
+ mi_stat_counter_add(&stats->huge_count, &src->huge_count, 1);
+ mi_stat_counter_add(&stats->large_count, &src->large_count, 1);
+#if MI_STAT>1
+ for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
+ if (src->normal_bins[i].allocated > 0 || src->normal_bins[i].freed > 0) {
+ mi_stat_add(&stats->normal_bins[i], &src->normal_bins[i], 1);
+ }
+ }
+#endif
+}
+
+/* -----------------------------------------------------------
+ Display statistics
+----------------------------------------------------------- */
+
+// unit > 0 : size in binary bytes
+// unit == 0: count as decimal
+// unit < 0 : count in binary
+static void mi_printf_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg, const char* fmt) {
+ char buf[32]; buf[0] = 0;
+ int len = 32;
+ const char* suffix = (unit <= 0 ? " " : "B");
+ const int64_t base = (unit == 0 ? 1000 : 1024);
+ if (unit>0) n *= unit;
+
+ const int64_t pos = (n < 0 ? -n : n);
+ if (pos < base) {
+ if (n!=1 || suffix[0] != 'B') { // skip printing 1 B for the unit column
+ snprintf(buf, len, "%d %-3s", (int)n, (n==0 ? "" : suffix));
+ }
+ }
+ else {
+ int64_t divider = base;
+ const char* magnitude = "K";
+ if (pos >= divider*base) { divider *= base; magnitude = "M"; }
+ if (pos >= divider*base) { divider *= base; magnitude = "G"; }
+ const int64_t tens = (n / (divider/10));
+ const long whole = (long)(tens/10);
+ const long frac1 = (long)(tens%10);
+ char unitdesc[8];
+ snprintf(unitdesc, 8, "%s%s%s", magnitude, (base==1024 ? "i" : ""), suffix);
+ snprintf(buf, len, "%ld.%ld %-3s", whole, (frac1 < 0 ? -frac1 : frac1), unitdesc);
+ }
+ _mi_fprintf(out, arg, (fmt==NULL ? "%12s" : fmt), buf);
+}
+
+
+static void mi_print_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg) {
+ mi_printf_amount(n,unit,out,arg,NULL);
+}
+
+static void mi_print_count(int64_t n, int64_t unit, mi_output_fun* out, void* arg) {
+ if (unit==1) _mi_fprintf(out, arg, "%12s"," ");
+ else mi_print_amount(n,0,out,arg);
+}
+
+static void mi_stat_print_ex(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg, const char* notok ) {
+ _mi_fprintf(out, arg,"%10s:", msg);
+ if (unit > 0) {
+ mi_print_amount(stat->peak, unit, out, arg);
+ mi_print_amount(stat->allocated, unit, out, arg);
+ mi_print_amount(stat->freed, unit, out, arg);
+ mi_print_amount(stat->current, unit, out, arg);
+ mi_print_amount(unit, 1, out, arg);
+ mi_print_count(stat->allocated, unit, out, arg);
+ if (stat->allocated > stat->freed) {
+ _mi_fprintf(out, arg, " ");
+ _mi_fprintf(out, arg, (notok == NULL ? "not all freed" : notok));
+ _mi_fprintf(out, arg, "\n");
+ }
+ else {
+ _mi_fprintf(out, arg, " ok\n");
+ }
+ }
+ else if (unit<0) {
+ mi_print_amount(stat->peak, -1, out, arg);
+ mi_print_amount(stat->allocated, -1, out, arg);
+ mi_print_amount(stat->freed, -1, out, arg);
+ mi_print_amount(stat->current, -1, out, arg);
+ if (unit==-1) {
+ _mi_fprintf(out, arg, "%24s", "");
+ }
+ else {
+ mi_print_amount(-unit, 1, out, arg);
+ mi_print_count((stat->allocated / -unit), 0, out, arg);
+ }
+ if (stat->allocated > stat->freed)
+ _mi_fprintf(out, arg, " not all freed!\n");
+ else
+ _mi_fprintf(out, arg, " ok\n");
+ }
+ else {
+ mi_print_amount(stat->peak, 1, out, arg);
+ mi_print_amount(stat->allocated, 1, out, arg);
+ _mi_fprintf(out, arg, "%11s", " "); // no freed
+ mi_print_amount(stat->current, 1, out, arg);
+ _mi_fprintf(out, arg, "\n");
+ }
+}
+
+static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) {
+ mi_stat_print_ex(stat, msg, unit, out, arg, NULL);
+}
+
+static void mi_stat_peak_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) {
+ _mi_fprintf(out, arg, "%10s:", msg);
+ mi_print_amount(stat->peak, unit, out, arg);
+ _mi_fprintf(out, arg, "\n");
+}
+
+static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) {
+ _mi_fprintf(out, arg, "%10s:", msg);
+ mi_print_amount(stat->total, -1, out, arg);
+ _mi_fprintf(out, arg, "\n");
+}
+
+
+static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg) {
+ const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count));
+ const long avg_whole = (long)(avg_tens/10);
+ const long avg_frac1 = (long)(avg_tens%10);
+ _mi_fprintf(out, arg, "%10s: %5ld.%ld avg\n", msg, avg_whole, avg_frac1);
+}
+
+
+static void mi_print_header(mi_output_fun* out, void* arg ) {
+ _mi_fprintf(out, arg, "%10s: %11s %11s %11s %11s %11s %11s\n", "heap stats", "peak ", "total ", "freed ", "current ", "unit ", "count ");
+}
+
+#if MI_STAT>1
+static void mi_stats_print_bins(const mi_stat_count_t* bins, size_t max, const char* fmt, mi_output_fun* out, void* arg) {
+ bool found = false;
+ char buf[64];
+ for (size_t i = 0; i <= max; i++) {
+ if (bins[i].allocated > 0) {
+ found = true;
+ int64_t unit = _mi_bin_size((uint8_t)i);
+ snprintf(buf, 64, "%s %3lu", fmt, (long)i);
+ mi_stat_print(&bins[i], buf, unit, out, arg);
+ }
+ }
+ if (found) {
+ _mi_fprintf(out, arg, "\n");
+ mi_print_header(out, arg);
+ }
+}
+#endif
+
+
+
+//------------------------------------------------------------
+// Use an output wrapper for line-buffered output
+// (which is nice when using loggers etc.)
+//------------------------------------------------------------
+typedef struct buffered_s {
+ mi_output_fun* out; // original output function
+ void* arg; // and state
+ char* buf; // local buffer of at least size `count+1`
+ size_t used; // currently used chars `used <= count`
+ size_t count; // total chars available for output
+} buffered_t;
+
+static void mi_buffered_flush(buffered_t* buf) {
+ buf->buf[buf->used] = 0;
+ _mi_fputs(buf->out, buf->arg, NULL, buf->buf);
+ buf->used = 0;
+}
+
+static void mi_cdecl mi_buffered_out(const char* msg, void* arg) {
+ buffered_t* buf = (buffered_t*)arg;
+ if (msg==NULL || buf==NULL) return;
+ for (const char* src = msg; *src != 0; src++) {
+ char c = *src;
+ if (buf->used >= buf->count) mi_buffered_flush(buf);
+ mi_assert_internal(buf->used < buf->count);
+ buf->buf[buf->used++] = c;
+ if (c == '\n') mi_buffered_flush(buf);
+ }
+}
+
+//------------------------------------------------------------
+// Print statistics
+//------------------------------------------------------------
+
+static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_attr_noexcept {
+ // wrap the output function to be line buffered
+ char buf[256];
+ buffered_t buffer = { out0, arg0, NULL, 0, 255 };
+ buffer.buf = buf;
+ mi_output_fun* out = &mi_buffered_out;
+ void* arg = &buffer;
+
+ // and print using that
+ mi_print_header(out,arg);
+ #if MI_STAT>1
+ mi_stats_print_bins(stats->normal_bins, MI_BIN_HUGE, "normal",out,arg);
+ #endif
+ #if MI_STAT
+ mi_stat_print(&stats->normal, "normal", (stats->normal_count.count == 0 ? 1 : -(stats->normal.allocated / stats->normal_count.count)), out, arg);
+ mi_stat_print(&stats->large, "large", (stats->large_count.count == 0 ? 1 : -(stats->large.allocated / stats->large_count.count)), out, arg);
+ mi_stat_print(&stats->huge, "huge", (stats->huge_count.count == 0 ? 1 : -(stats->huge.allocated / stats->huge_count.count)), out, arg);
+ mi_stat_count_t total = { 0,0,0,0 };
+ mi_stat_add(&total, &stats->normal, 1);
+ mi_stat_add(&total, &stats->large, 1);
+ mi_stat_add(&total, &stats->huge, 1);
+ mi_stat_print(&total, "total", 1, out, arg);
+ #endif
+ #if MI_STAT>1
+ mi_stat_print(&stats->malloc, "malloc req", 1, out, arg);
+ _mi_fprintf(out, arg, "\n");
+ #endif
+ mi_stat_print_ex(&stats->reserved, "reserved", 1, out, arg, "");
+ mi_stat_print_ex(&stats->committed, "committed", 1, out, arg, "");
+ mi_stat_peak_print(&stats->reset, "reset", 1, out, arg );
+ mi_stat_peak_print(&stats->purged, "purged", 1, out, arg );
+ mi_stat_print(&stats->page_committed, "touched", 1, out, arg);
+ mi_stat_print(&stats->segments, "segments", -1, out, arg);
+ mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out, arg);
+ mi_stat_print(&stats->segments_cache, "-cached", -1, out, arg);
+ mi_stat_print(&stats->pages, "pages", -1, out, arg);
+ mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out, arg);
+ mi_stat_counter_print(&stats->pages_extended, "-extended", out, arg);
+ mi_stat_counter_print(&stats->page_no_retire, "-noretire", out, arg);
+ mi_stat_counter_print(&stats->mmap_calls, "mmaps", out, arg);
+ mi_stat_counter_print(&stats->commit_calls, "commits", out, arg);
+ mi_stat_counter_print(&stats->reset_calls, "resets", out, arg);
+ mi_stat_counter_print(&stats->purge_calls, "purges", out, arg);
+ mi_stat_print(&stats->threads, "threads", -1, out, arg);
+ mi_stat_counter_print_avg(&stats->searches, "searches", out, arg);
+ _mi_fprintf(out, arg, "%10s: %5zu\n", "numa nodes", _mi_os_numa_node_count());
+
+ size_t elapsed;
+ size_t user_time;
+ size_t sys_time;
+ size_t current_rss;
+ size_t peak_rss;
+ size_t current_commit;
+ size_t peak_commit;
+ size_t page_faults;
+ mi_process_info(&elapsed, &user_time, &sys_time, &current_rss, &peak_rss, &current_commit, &peak_commit, &page_faults);
+ _mi_fprintf(out, arg, "%10s: %5ld.%03ld s\n", "elapsed", elapsed/1000, elapsed%1000);
+ _mi_fprintf(out, arg, "%10s: user: %ld.%03ld s, system: %ld.%03ld s, faults: %lu, rss: ", "process",
+ user_time/1000, user_time%1000, sys_time/1000, sys_time%1000, (unsigned long)page_faults );
+ mi_printf_amount((int64_t)peak_rss, 1, out, arg, "%s");
+ if (peak_commit > 0) {
+ _mi_fprintf(out, arg, ", commit: ");
+ mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s");
+ }
+ _mi_fprintf(out, arg, "\n");
+}
+
+static mi_msecs_t mi_process_start; // = 0
+
+static mi_stats_t* mi_stats_get_default(void) {
+ mi_heap_t* heap = mi_heap_get_default();
+ return &heap->tld->stats;
+}
+
+static void mi_stats_merge_from(mi_stats_t* stats) {
+ if (stats != &_mi_stats_main) {
+ mi_stats_add(&_mi_stats_main, stats);
+ memset(stats, 0, sizeof(mi_stats_t));
+ }
+}
+
+void mi_stats_reset(void) mi_attr_noexcept {
+ mi_stats_t* stats = mi_stats_get_default();
+ if (stats != &_mi_stats_main) { memset(stats, 0, sizeof(mi_stats_t)); }
+ memset(&_mi_stats_main, 0, sizeof(mi_stats_t));
+ if (mi_process_start == 0) { mi_process_start = _mi_clock_start(); };
+}
+
+void mi_stats_merge(void) mi_attr_noexcept {
+ mi_stats_merge_from( mi_stats_get_default() );
+}
+
+void _mi_stats_done(mi_stats_t* stats) { // called from `mi_thread_done`
+ mi_stats_merge_from(stats);
+}
+
+void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept {
+ mi_stats_merge_from(mi_stats_get_default());
+ _mi_stats_print(&_mi_stats_main, out, arg);
+}
+
+void mi_stats_print(void* out) mi_attr_noexcept {
+ // for compatibility there is an `out` parameter (which can be `stdout` or `stderr`)
+ mi_stats_print_out((mi_output_fun*)out, NULL);
+}
+
+void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept {
+ _mi_stats_print(mi_stats_get_default(), out, arg);
+}
+
+
+// ----------------------------------------------------------------
+// Basic timer for convenience; use milli-seconds to avoid doubles
+// ----------------------------------------------------------------
+
+static mi_msecs_t mi_clock_diff;
+
+mi_msecs_t _mi_clock_now(void) {
+ return _mi_prim_clock_now();
+}
+
+mi_msecs_t _mi_clock_start(void) {
+ if (mi_clock_diff == 0.0) {
+ mi_msecs_t t0 = _mi_clock_now();
+ mi_clock_diff = _mi_clock_now() - t0;
+ }
+ return _mi_clock_now();
+}
+
+mi_msecs_t _mi_clock_end(mi_msecs_t start) {
+ mi_msecs_t end = _mi_clock_now();
+ return (end - start - mi_clock_diff);
+}
+
+
+// --------------------------------------------------------
+// Basic process statistics
+// --------------------------------------------------------
+
+mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults) mi_attr_noexcept
+{
+ mi_process_info_t pinfo;
+ _mi_memzero_var(pinfo);
+ pinfo.elapsed = _mi_clock_end(mi_process_start);
+ pinfo.current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current));
+ pinfo.peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak));
+ pinfo.current_rss = pinfo.current_commit;
+ pinfo.peak_rss = pinfo.peak_commit;
+ pinfo.utime = 0;
+ pinfo.stime = 0;
+ pinfo.page_faults = 0;
+
+ _mi_prim_process_info(&pinfo);
+
+ if (elapsed_msecs!=NULL) *elapsed_msecs = (pinfo.elapsed < 0 ? 0 : (pinfo.elapsed < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.elapsed : PTRDIFF_MAX));
+ if (user_msecs!=NULL) *user_msecs = (pinfo.utime < 0 ? 0 : (pinfo.utime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.utime : PTRDIFF_MAX));
+ if (system_msecs!=NULL) *system_msecs = (pinfo.stime < 0 ? 0 : (pinfo.stime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.stime : PTRDIFF_MAX));
+ if (current_rss!=NULL) *current_rss = pinfo.current_rss;
+ if (peak_rss!=NULL) *peak_rss = pinfo.peak_rss;
+ if (current_commit!=NULL) *current_commit = pinfo.current_commit;
+ if (peak_commit!=NULL) *peak_commit = pinfo.peak_commit;
+ if (page_faults!=NULL) *page_faults = pinfo.page_faults;
+}
diff --git a/contrib/tools/python3/Objects/moduleobject.c b/contrib/tools/python3/Objects/moduleobject.c
index 4daf1a929e0..d787f290045 100644
--- a/contrib/tools/python3/Objects/moduleobject.c
+++ b/contrib/tools/python3/Objects/moduleobject.c
@@ -3,15 +3,21 @@
#include "Python.h"
#include "pycore_call.h" // _PyObject_CallNoArgs()
+#include "pycore_fileutils.h" // _Py_wgetcwd
#include "pycore_interp.h" // PyInterpreterState.importlib
+#include "pycore_modsupport.h" // _PyModule_CreateInitialized()
+#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_AllocNoTrack
+#include "pycore_pyerrors.h" // _PyErr_FormatFromCause()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
-#include "pycore_moduleobject.h" // _PyModule_GetDef()
-#include "structmember.h" // PyMemberDef
+#include "pycore_sysmodule.h" // _PySys_GetOptionalAttrString()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
+
+#include "osdefs.h" // MAXPATHLEN
static PyMemberDef module_members[] = {
- {"__dict__", T_OBJECT, offsetof(PyModuleObject, md_dict), READONLY},
+ {"__dict__", _Py_T_OBJECT, offsetof(PyModuleObject, md_dict), Py_READONLY},
{0}
};
@@ -86,21 +92,31 @@ new_module_notrack(PyTypeObject *mt)
m->md_weaklist = NULL;
m->md_name = NULL;
m->md_dict = PyDict_New();
- if (m->md_dict != NULL) {
- return m;
+ if (m->md_dict == NULL) {
+ Py_DECREF(m);
+ return NULL;
}
- Py_DECREF(m);
- return NULL;
+ return m;
+}
+
+static void
+track_module(PyModuleObject *m)
+{
+ _PyObject_SetDeferredRefcount(m->md_dict);
+ PyObject_GC_Track(m->md_dict);
+
+ _PyObject_SetDeferredRefcount((PyObject *)m);
+ PyObject_GC_Track(m);
}
static PyObject *
new_module(PyTypeObject *mt, PyObject *args, PyObject *kws)
{
- PyObject *m = (PyObject *)new_module_notrack(mt);
+ PyModuleObject *m = new_module_notrack(mt);
if (m != NULL) {
- PyObject_GC_Track(m);
+ track_module(m);
}
- return m;
+ return (PyObject *)m;
}
PyObject *
@@ -111,7 +127,7 @@ PyModule_NewObject(PyObject *name)
return NULL;
if (module_init_dict(m, m->md_dict, name, NULL) != 0)
goto fail;
- PyObject_GC_Track(m);
+ track_module(m);
return (PyObject *)m;
fail:
@@ -169,6 +185,7 @@ _add_methods_to_object(PyObject *module, PyObject *name, PyMethodDef *functions)
if (func == NULL) {
return -1;
}
+ _PyObject_SetDeferredRefcount(func);
if (PyObject_SetAttrString(module, fdef->ml_name, func) != 0) {
Py_DECREF(func);
return -1;
@@ -235,6 +252,9 @@ _PyModule_CreateInitialized(PyModuleDef* module, int module_api_version)
}
}
m->md_def = module;
+#ifdef Py_GIL_DISABLED
+ m->md_gil = Py_MOD_GIL_USED;
+#endif
return (PyObject*)m;
}
@@ -247,6 +267,8 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
PyObject *m = NULL;
int has_multiple_interpreters_slot = 0;
void *multiple_interpreters = (void *)0;
+ int has_gil_slot = 0;
+ void *gil_slot = Py_MOD_GIL_USED;
int has_execution_slots = 0;
const char *name;
int ret;
@@ -301,6 +323,17 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
multiple_interpreters = cur_slot->value;
has_multiple_interpreters_slot = 1;
break;
+ case Py_mod_gil:
+ if (has_gil_slot) {
+ PyErr_Format(
+ PyExc_SystemError,
+ "module %s has more than one 'gil' slot",
+ name);
+ goto error;
+ }
+ gil_slot = cur_slot->value;
+ has_gil_slot = 1;
+ break;
default:
assert(cur_slot->slot < 0 || cur_slot->slot > _Py_mod_LAST_SLOT);
PyErr_Format(
@@ -360,6 +393,11 @@ PyModule_FromDefAndSpec2(PyModuleDef* def, PyObject *spec, int module_api_versio
if (PyModule_Check(m)) {
((PyModuleObject*)m)->md_state = NULL;
((PyModuleObject*)m)->md_def = def;
+#ifdef Py_GIL_DISABLED
+ ((PyModuleObject*)m)->md_gil = gil_slot;
+#else
+ (void)gil_slot;
+#endif
} else {
if (def->m_size > 0 || def->m_traverse || def->m_clear || def->m_free) {
PyErr_Format(
@@ -401,6 +439,19 @@ error:
return NULL;
}
+#ifdef Py_GIL_DISABLED
+int
+PyUnstable_Module_SetGIL(PyObject *module, void *gil)
+{
+ if (!PyModule_Check(module)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ ((PyModuleObject *)module)->md_gil = gil;
+ return 0;
+}
+#endif
+
int
PyModule_ExecDef(PyObject *module, PyModuleDef *def)
{
@@ -456,6 +507,7 @@ PyModule_ExecDef(PyObject *module, PyModuleDef *def)
}
break;
case Py_mod_multiple_interpreters:
+ case Py_mod_gil:
/* handled in PyModule_FromDefAndSpec2 */
break;
default:
@@ -504,29 +556,36 @@ PyModule_GetDict(PyObject *m)
PyErr_BadInternalCall();
return NULL;
}
- return _PyModule_GetDict(m);
+ return _PyModule_GetDict(m); // borrowed reference
}
PyObject*
-PyModule_GetNameObject(PyObject *m)
+PyModule_GetNameObject(PyObject *mod)
{
- PyObject *d;
- PyObject *name;
- if (!PyModule_Check(m)) {
+ if (!PyModule_Check(mod)) {
PyErr_BadArgument();
return NULL;
}
- d = ((PyModuleObject *)m)->md_dict;
- if (d == NULL || !PyDict_Check(d) ||
- (name = PyDict_GetItemWithError(d, &_Py_ID(__name__))) == NULL ||
- !PyUnicode_Check(name))
- {
- if (!PyErr_Occurred()) {
- PyErr_SetString(PyExc_SystemError, "nameless module");
- }
- return NULL;
+ PyObject *dict = ((PyModuleObject *)mod)->md_dict; // borrowed reference
+ if (dict == NULL || !PyDict_Check(dict)) {
+ goto error;
+ }
+ PyObject *name;
+ if (PyDict_GetItemRef(dict, &_Py_ID(__name__), &name) <= 0) {
+ // error or not found
+ goto error;
+ }
+ if (!PyUnicode_Check(name)) {
+ Py_DECREF(name);
+ goto error;
}
- return Py_NewRef(name);
+ return name;
+
+error:
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_SystemError, "nameless module");
+ }
+ return NULL;
}
const char *
@@ -542,25 +601,32 @@ PyModule_GetName(PyObject *m)
}
PyObject*
-PyModule_GetFilenameObject(PyObject *m)
+PyModule_GetFilenameObject(PyObject *mod)
{
- PyObject *d;
- PyObject *fileobj;
- if (!PyModule_Check(m)) {
+ if (!PyModule_Check(mod)) {
PyErr_BadArgument();
return NULL;
}
- d = ((PyModuleObject *)m)->md_dict;
- if (d == NULL ||
- (fileobj = PyDict_GetItemWithError(d, &_Py_ID(__file__))) == NULL ||
- !PyUnicode_Check(fileobj))
- {
- if (!PyErr_Occurred()) {
- PyErr_SetString(PyExc_SystemError, "module filename missing");
- }
- return NULL;
+ PyObject *dict = ((PyModuleObject *)mod)->md_dict; // borrowed reference
+ if (dict == NULL) {
+ goto error;
+ }
+ PyObject *fileobj;
+ if (PyDict_GetItemRef(dict, &_Py_ID(__file__), &fileobj) <= 0) {
+ // error or not found
+ goto error;
+ }
+ if (!PyUnicode_Check(fileobj)) {
+ Py_DECREF(fileobj);
+ goto error;
+ }
+ return fileobj;
+
+error:
+ if (!PyErr_Occurred()) {
+ PyErr_SetString(PyExc_SystemError, "module filename missing");
}
- return Py_NewRef(fileobj);
+ return NULL;
}
const char *
@@ -633,7 +699,7 @@ _PyModule_ClearDict(PyObject *d)
PyErr_Clear();
}
if (PyDict_SetItem(d, key, Py_None) != 0) {
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored on clearing module dict");
}
}
}
@@ -654,7 +720,7 @@ _PyModule_ClearDict(PyObject *d)
PyErr_Clear();
}
if (PyDict_SetItem(d, key, Py_None) != 0) {
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored on clearing module dict");
}
}
}
@@ -689,16 +755,7 @@ static int
module___init___impl(PyModuleObject *self, PyObject *name, PyObject *doc)
/*[clinic end generated code: output=e7e721c26ce7aad7 input=57f9e177401e5e1e]*/
{
- PyObject *dict = self->md_dict;
- if (dict == NULL) {
- dict = PyDict_New();
- if (dict == NULL)
- return -1;
- self->md_dict = dict;
- }
- if (module_init_dict(self, dict, name, doc) < 0)
- return -1;
- return 0;
+ return module_init_dict(self, self->md_dict, name, doc);
}
static void
@@ -710,8 +767,7 @@ module_dealloc(PyModuleObject *m)
if (verbose && m->md_name) {
PySys_FormatStderr("# destroy %U\n", m->md_name);
}
- if (m->md_weaklist != NULL)
- PyObject_ClearWeakRefs((PyObject *) m);
+ FT_CLEAR_WEAKREFS((PyObject *) m, m->md_weaklist);
/* bpo-39824: Don't call m_free() if m_size > 0 and md_state=NULL */
if (m->md_def && m->md_def->m_free
&& (m->md_def->m_size <= 0 || m->md_state != NULL))
@@ -733,27 +789,20 @@ module_repr(PyModuleObject *m)
}
/* Check if the "_initializing" attribute of the module spec is set to true.
- Clear the exception and return 0 if spec is NULL.
*/
int
_PyModuleSpec_IsInitializing(PyObject *spec)
{
- if (spec != NULL) {
- PyObject *value;
- int ok = _PyObject_LookupAttr(spec, &_Py_ID(_initializing), &value);
- if (ok == 0) {
- return 0;
- }
- if (value != NULL) {
- int initializing = PyObject_IsTrue(value);
- Py_DECREF(value);
- if (initializing >= 0) {
- return initializing;
- }
- }
+ if (spec == NULL) {
+ return 0;
}
- PyErr_Clear();
- return 0;
+ PyObject *value;
+ int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(_initializing), &value);
+ if (rc > 0) {
+ rc = PyObject_IsTrue(value);
+ Py_DECREF(value);
+ }
+ return rc;
}
/* Check if the submodule name is in the "_uninitialized_submodules" attribute
@@ -766,17 +815,108 @@ _PyModuleSpec_IsUninitializedSubmodule(PyObject *spec, PyObject *name)
return 0;
}
- PyObject *value = PyObject_GetAttr(spec, &_Py_ID(_uninitialized_submodules));
- if (value == NULL) {
+ PyObject *value;
+ int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(_uninitialized_submodules), &value);
+ if (rc > 0) {
+ rc = PySequence_Contains(value, name);
+ Py_DECREF(value);
+ }
+ return rc;
+}
+
+int
+_PyModuleSpec_GetFileOrigin(PyObject *spec, PyObject **p_origin)
+{
+ PyObject *has_location = NULL;
+ int rc = PyObject_GetOptionalAttr(spec, &_Py_ID(has_location), &has_location);
+ if (rc <= 0) {
+ return rc;
+ }
+ // If origin is not a location, or doesn't exist, or is not a str, we could consider falling
+ // back to module.__file__. But the cases in which module.__file__ is not __spec__.origin
+ // are cases in which we probably shouldn't be guessing.
+ rc = PyObject_IsTrue(has_location);
+ Py_DECREF(has_location);
+ if (rc <= 0) {
+ return rc;
+ }
+ // has_location is true, so origin is a location
+ PyObject *origin = NULL;
+ rc = PyObject_GetOptionalAttr(spec, &_Py_ID(origin), &origin);
+ if (rc <= 0) {
+ return rc;
+ }
+ assert(origin != NULL);
+ if (!PyUnicode_Check(origin)) {
+ Py_DECREF(origin);
+ return 0;
+ }
+ *p_origin = origin;
+ return 1;
+}
+
+int
+_PyModule_IsPossiblyShadowing(PyObject *origin)
+{
+ // origin must be a unicode subtype
+ // Returns 1 if the module at origin could be shadowing a module of the
+ // same name later in the module search path. The condition we check is basically:
+ // root = os.path.dirname(origin.removesuffix(os.sep + "__init__.py"))
+ // return not sys.flags.safe_path and root == (sys.path[0] or os.getcwd())
+ // Returns 0 otherwise (or if we aren't sure)
+ // Returns -1 if an error occurred that should be propagated
+ if (origin == NULL) {
+ return 0;
+ }
+
+ // not sys.flags.safe_path
+ const PyConfig *config = _Py_GetConfig();
+ if (config->safe_path) {
+ return 0;
+ }
+
+ // root = os.path.dirname(origin.removesuffix(os.sep + "__init__.py"))
+ wchar_t root[MAXPATHLEN + 1];
+ Py_ssize_t size = PyUnicode_AsWideChar(origin, root, MAXPATHLEN);
+ if (size < 0) {
+ return -1;
+ }
+ assert(size <= MAXPATHLEN);
+ root[size] = L'\0';
+
+ wchar_t *sep = wcsrchr(root, SEP);
+ if (sep == NULL) {
return 0;
}
+ // If it's a package then we need to look one directory further up
+ if (wcscmp(sep + 1, L"__init__.py") == 0) {
+ *sep = L'\0';
+ sep = wcsrchr(root, SEP);
+ if (sep == NULL) {
+ return 0;
+ }
+ }
+ *sep = L'\0';
- int is_uninitialized = PySequence_Contains(value, name);
- Py_DECREF(value);
- if (is_uninitialized == -1) {
+ // sys.path[0] or os.getcwd()
+ wchar_t *sys_path_0 = config->sys_path_0;
+ if (!sys_path_0) {
return 0;
}
- return is_uninitialized;
+
+ wchar_t sys_path_0_buf[MAXPATHLEN];
+ if (sys_path_0[0] == L'\0') {
+ // if sys.path[0] == "", treat it as if it were the current directory
+ if (!_Py_wgetcwd(sys_path_0_buf, MAXPATHLEN)) {
+ // If we failed to getcwd, don't raise an exception and instead
+ // let the caller proceed assuming no shadowing
+ return 0;
+ }
+ sys_path_0 = sys_path_0_buf;
+ }
+
+ int result = wcscmp(sys_path_0, root) == 0;
+ return result;
}
PyObject*
@@ -802,58 +942,132 @@ _Py_module_getattro_impl(PyModuleObject *m, PyObject *name, int suppress)
PyErr_Clear();
}
assert(m->md_dict != NULL);
- getattr = PyDict_GetItemWithError(m->md_dict, &_Py_ID(__getattr__));
+ if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__getattr__), &getattr) < 0) {
+ return NULL;
+ }
if (getattr) {
PyObject *result = PyObject_CallOneArg(getattr, name);
if (result == NULL && suppress == 1 && PyErr_ExceptionMatches(PyExc_AttributeError)) {
// suppress AttributeError
PyErr_Clear();
}
+ Py_DECREF(getattr);
return result;
}
- if (PyErr_Occurred()) {
+
+ // The attribute was not found. We make a best effort attempt at a useful error message,
+ // but only if we're not suppressing AttributeError.
+ if (suppress == 1) {
return NULL;
}
- mod_name = PyDict_GetItemWithError(m->md_dict, &_Py_ID(__name__));
- if (mod_name && PyUnicode_Check(mod_name)) {
- Py_INCREF(mod_name);
- PyObject *spec = PyDict_GetItemWithError(m->md_dict, &_Py_ID(__spec__));
- if (spec == NULL && PyErr_Occurred()) {
- Py_DECREF(mod_name);
- return NULL;
+ if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__name__), &mod_name) < 0) {
+ return NULL;
+ }
+ if (!mod_name || !PyUnicode_Check(mod_name)) {
+ Py_XDECREF(mod_name);
+ PyErr_Format(PyExc_AttributeError,
+ "module has no attribute '%U'", name);
+ return NULL;
+ }
+ PyObject *spec;
+ if (PyDict_GetItemRef(m->md_dict, &_Py_ID(__spec__), &spec) < 0) {
+ Py_DECREF(mod_name);
+ return NULL;
+ }
+ if (spec == NULL) {
+ PyErr_Format(PyExc_AttributeError,
+ "module '%U' has no attribute '%U'",
+ mod_name, name);
+ Py_DECREF(mod_name);
+ return NULL;
+ }
+
+ PyObject *origin = NULL;
+ if (_PyModuleSpec_GetFileOrigin(spec, &origin) < 0) {
+ goto done;
+ }
+
+ int is_possibly_shadowing = _PyModule_IsPossiblyShadowing(origin);
+ if (is_possibly_shadowing < 0) {
+ goto done;
+ }
+ int is_possibly_shadowing_stdlib = 0;
+ if (is_possibly_shadowing) {
+ PyObject *stdlib_modules;
+ if (_PySys_GetOptionalAttrString("stdlib_module_names", &stdlib_modules) < 0) {
+ goto done;
+ }
+ if (stdlib_modules && PyAnySet_Check(stdlib_modules)) {
+ is_possibly_shadowing_stdlib = PySet_Contains(stdlib_modules, mod_name);
+ if (is_possibly_shadowing_stdlib < 0) {
+ Py_DECREF(stdlib_modules);
+ goto done;
+ }
+ }
+ Py_XDECREF(stdlib_modules);
+ }
+
+ if (is_possibly_shadowing_stdlib) {
+ assert(origin);
+ PyErr_Format(PyExc_AttributeError,
+ "module '%U' has no attribute '%U' "
+ "(consider renaming '%U' since it has the same "
+ "name as the standard library module named '%U' "
+ "and prevents importing that standard library module)",
+ mod_name, name, origin, mod_name);
+ }
+ else {
+ int rc = _PyModuleSpec_IsInitializing(spec);
+ if (rc < 0) {
+ goto done;
}
- if (suppress != 1) {
- Py_XINCREF(spec);
- if (_PyModuleSpec_IsInitializing(spec)) {
+ else if (rc > 0) {
+ if (is_possibly_shadowing) {
+ assert(origin);
+ // For non-stdlib modules, only mention the possibility of
+ // shadowing if the module is being initialized.
PyErr_Format(PyExc_AttributeError,
- "partially initialized "
- "module '%U' has no attribute '%U' "
- "(most likely due to a circular import)",
- mod_name, name);
+ "module '%U' has no attribute '%U' "
+ "(consider renaming '%U' if it has the same name "
+ "as a library you intended to import)",
+ mod_name, name, origin);
}
- else if (_PyModuleSpec_IsUninitializedSubmodule(spec, name)) {
+ else if (origin) {
PyErr_Format(PyExc_AttributeError,
- "cannot access submodule '%U' of module '%U' "
- "(most likely due to a circular import)",
- name, mod_name);
+ "partially initialized "
+ "module '%U' from '%U' has no attribute '%U' "
+ "(most likely due to a circular import)",
+ mod_name, origin, name);
}
else {
PyErr_Format(PyExc_AttributeError,
- "module '%U' has no attribute '%U'",
- mod_name, name);
+ "partially initialized "
+ "module '%U' has no attribute '%U' "
+ "(most likely due to a circular import)",
+ mod_name, name);
+ }
+ }
+ else {
+ assert(rc == 0);
+ rc = _PyModuleSpec_IsUninitializedSubmodule(spec, name);
+ if (rc > 0) {
+ PyErr_Format(PyExc_AttributeError,
+ "cannot access submodule '%U' of module '%U' "
+ "(most likely due to a circular import)",
+ name, mod_name);
+ }
+ else if (rc == 0) {
+ PyErr_Format(PyExc_AttributeError,
+ "module '%U' has no attribute '%U'",
+ mod_name, name);
}
- Py_XDECREF(spec);
}
- Py_DECREF(mod_name);
- return NULL;
- }
- else if (PyErr_Occurred()) {
- return NULL;
- }
- if (suppress != 1) {
- PyErr_Format(PyExc_AttributeError,
- "module has no attribute '%U'", name);
}
+
+done:
+ Py_XDECREF(origin);
+ Py_DECREF(spec);
+ Py_DECREF(mod_name);
return NULL;
}
@@ -888,10 +1102,9 @@ module_clear(PyModuleObject *m)
{
int res = m->md_def->m_clear((PyObject*)m);
if (PyErr_Occurred()) {
- PySys_FormatStderr("Exception ignored in m_clear of module%s%V\n",
- m->md_name ? " " : "",
- m->md_name, "");
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored in m_clear of module%s%V",
+ m->md_name ? " " : "",
+ m->md_name, "");
}
if (res)
return res;
@@ -944,11 +1157,8 @@ module_get_annotations(PyModuleObject *m, void *Py_UNUSED(ignored))
return NULL;
}
- PyObject *annotations = PyDict_GetItemWithError(dict, &_Py_ID(__annotations__));
- if (annotations) {
- Py_INCREF(annotations);
- }
- else if (!PyErr_Occurred()) {
+ PyObject *annotations;
+ if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) == 0) {
annotations = PyDict_New();
if (annotations) {
int result = PyDict_SetItem(
@@ -981,9 +1191,13 @@ module_set_annotations(PyModuleObject *m, PyObject *value, void *Py_UNUSED(ignor
}
else {
/* delete */
- ret = PyDict_DelItem(dict, &_Py_ID(__annotations__));
- if (ret < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
- PyErr_SetString(PyExc_AttributeError, "__annotations__");
+ ret = PyDict_Pop(dict, &_Py_ID(__annotations__), NULL);
+ if (ret == 0) {
+ PyErr_SetObject(PyExc_AttributeError, &_Py_ID(__annotations__));
+ ret = -1;
+ }
+ else if (ret > 0) {
+ ret = 0;
}
}
diff --git a/contrib/tools/python3/Objects/namespaceobject.c b/contrib/tools/python3/Objects/namespaceobject.c
index 7082c6fd465..4b1d625fc78 100644
--- a/contrib/tools/python3/Objects/namespaceobject.c
+++ b/contrib/tools/python3/Objects/namespaceobject.c
@@ -1,8 +1,10 @@
// namespace object implementation
#include "Python.h"
+#include "pycore_modsupport.h" // _PyArg_NoPositional()
#include "pycore_namespace.h" // _PyNamespace_Type
-#include "structmember.h" // PyMemberDef
+
+#include <stddef.h> // offsetof()
typedef struct {
@@ -10,9 +12,12 @@ typedef struct {
PyObject *ns_dict;
} _PyNamespaceObject;
+#define _PyNamespace_CAST(op) _Py_CAST(_PyNamespaceObject*, (op))
+#define _PyNamespace_Check(op) PyObject_TypeCheck((op), &_PyNamespace_Type)
+
static PyMemberDef namespace_members[] = {
- {"__dict__", T_OBJECT, offsetof(_PyNamespaceObject, ns_dict), READONLY},
+ {"__dict__", _Py_T_OBJECT, offsetof(_PyNamespaceObject, ns_dict), Py_READONLY},
{NULL}
};
@@ -41,10 +46,28 @@ namespace_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
static int
namespace_init(_PyNamespaceObject *ns, PyObject *args, PyObject *kwds)
{
- if (PyTuple_GET_SIZE(args) != 0) {
- PyErr_Format(PyExc_TypeError, "no positional arguments expected");
+ PyObject *arg = NULL;
+ if (!PyArg_UnpackTuple(args, _PyType_Name(Py_TYPE(ns)), 0, 1, &arg)) {
return -1;
}
+ if (arg != NULL) {
+ PyObject *dict;
+ if (PyDict_CheckExact(arg)) {
+ dict = Py_NewRef(arg);
+ }
+ else {
+ dict = PyObject_CallOneArg((PyObject *)&PyDict_Type, arg);
+ if (dict == NULL) {
+ return -1;
+ }
+ }
+ int err = (!PyArg_ValidateKeywordArguments(dict) ||
+ PyDict_Update(ns->ns_dict, dict) < 0);
+ Py_DECREF(dict);
+ if (err) {
+ return -1;
+ }
+ }
if (kwds == NULL) {
return 0;
}
@@ -100,9 +123,10 @@ namespace_repr(PyObject *ns)
if (PyUnicode_Check(key) && PyUnicode_GET_LENGTH(key) > 0) {
PyObject *value, *item;
- value = PyDict_GetItemWithError(d, key);
- if (value != NULL) {
+ int has_key = PyDict_GetItemRef(d, key, &value);
+ if (has_key == 1) {
item = PyUnicode_FromFormat("%U=%R", key, value);
+ Py_DECREF(value);
if (item == NULL) {
loop_error = 1;
}
@@ -111,7 +135,7 @@ namespace_repr(PyObject *ns)
Py_DECREF(item);
}
}
- else if (PyErr_Occurred()) {
+ else if (has_key < 0) {
loop_error = 1;
}
}
@@ -191,17 +215,55 @@ namespace_reduce(_PyNamespaceObject *ns, PyObject *Py_UNUSED(ignored))
}
+static PyObject *
+namespace_replace(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ if (!_PyArg_NoPositional("__replace__", args)) {
+ return NULL;
+ }
+
+ PyObject *result = PyObject_CallNoArgs((PyObject *)Py_TYPE(self));
+ if (!result) {
+ return NULL;
+ }
+ if (!_PyNamespace_Check(result)) {
+ PyErr_Format(PyExc_TypeError,
+ "expect %N type, but %T() returned '%T' object",
+ &_PyNamespace_Type, self, result);
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ if (PyDict_Update(((_PyNamespaceObject*)result)->ns_dict,
+ ((_PyNamespaceObject*)self)->ns_dict) < 0)
+ {
+ Py_DECREF(result);
+ return NULL;
+ }
+ if (kwargs) {
+ if (PyDict_Update(((_PyNamespaceObject*)result)->ns_dict, kwargs) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ }
+ return result;
+}
+
+
static PyMethodDef namespace_methods[] = {
{"__reduce__", (PyCFunction)namespace_reduce, METH_NOARGS,
namespace_reduce__doc__},
+ {"__replace__", _PyCFunction_CAST(namespace_replace), METH_VARARGS|METH_KEYWORDS,
+ PyDoc_STR("__replace__($self, /, **changes)\n--\n\n"
+ "Return a copy of the namespace object with new values for the specified attributes.")},
{NULL, NULL} // sentinel
};
PyDoc_STRVAR(namespace_doc,
-"A simple attribute-based namespace.\n\
-\n\
-SimpleNamespace(**kwargs)");
+"SimpleNamespace(mapping_or_iterable=(), /, **kwargs)\n\
+--\n\n\
+A simple attribute-based namespace.");
PyTypeObject _PyNamespace_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
diff --git a/contrib/tools/python3/Objects/object.c b/contrib/tools/python3/Objects/object.c
index 706719116da..c6d5e163528 100644
--- a/contrib/tools/python3/Objects/object.c
+++ b/contrib/tools/python3/Objects/object.c
@@ -2,32 +2,36 @@
/* Generic object operations; and implementation of None */
#include "Python.h"
+#include "pycore_brc.h" // _Py_brc_queue_object()
#include "pycore_call.h" // _PyObject_CallNoArgs()
#include "pycore_ceval.h" // _Py_EnterRecursiveCallTstate()
#include "pycore_context.h" // _PyContextTokenMissing_Type
+#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION, Py_END_CRITICAL_SECTION
+#include "pycore_descrobject.h" // _PyMethodWrapper_Type
#include "pycore_dict.h" // _PyObject_MakeDictFromInstanceAttributes()
#include "pycore_floatobject.h" // _PyFloat_DebugMallocStats()
#include "pycore_initconfig.h" // _PyStatus_EXCEPTION()
+#include "pycore_instruction_sequence.h" // _PyInstructionSequence_Type
+#include "pycore_hashtable.h" // _Py_hashtable_new()
+#include "pycore_memoryobject.h" // _PyManagedBuffer_Type
#include "pycore_namespace.h" // _PyNamespace_Type
-#include "pycore_object.h" // _PyType_CheckConsistency(), _Py_FatalRefcountError()
+#include "pycore_object.h" // PyAPI_DATA() _Py_SwappedOp definition
+#include "pycore_long.h" // _PyLong_GetZero()
+#include "pycore_optimizer.h" // _PyUOpExecutor_Type, _PyUOpOptimizer_Type, ...
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_symtable.h" // PySTEntry_Type
-#include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_initialize_generic
#include "pycore_typeobject.h" // _PyBufferWrapper_Type
+#include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_initialize_generic
#include "pycore_unionobject.h" // _PyUnion_Type
-#include "interpreteridobject.h" // _PyInterpreterID_Type
+
#ifdef Py_LIMITED_API
// Prevent recursive call _Py_IncRef() <=> Py_INCREF()
# error "Py_LIMITED_API macro must not be defined"
#endif
-#ifdef __cplusplus
-extern "C" {
-#endif
-
/* Defined in tracemalloc.c */
extern void _PyMem_DumpTraceback(int fd, const void *ptr);
@@ -72,21 +76,16 @@ get_legacy_reftotal(void)
interp->object_state.reftotal
static inline void
-reftotal_increment(PyInterpreterState *interp)
-{
- REFTOTAL(interp)++;
-}
-
-static inline void
-reftotal_decrement(PyInterpreterState *interp)
+reftotal_add(PyThreadState *tstate, Py_ssize_t n)
{
- REFTOTAL(interp)--;
-}
-
-static inline void
-reftotal_add(PyInterpreterState *interp, Py_ssize_t n)
-{
- REFTOTAL(interp) += n;
+#ifdef Py_GIL_DISABLED
+ _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
+ // relaxed store to avoid data race with read in get_reftotal()
+ Py_ssize_t reftotal = tstate_impl->reftotal + n;
+ _Py_atomic_store_ssize_relaxed(&tstate_impl->reftotal, reftotal);
+#else
+ REFTOTAL(tstate->interp) += n;
+#endif
}
static inline Py_ssize_t get_global_reftotal(_PyRuntimeState *);
@@ -116,7 +115,15 @@ get_reftotal(PyInterpreterState *interp)
{
/* For a single interpreter, we ignore the legacy _Py_RefTotal,
since we can't determine which interpreter updated it. */
- return REFTOTAL(interp);
+ Py_ssize_t total = REFTOTAL(interp);
+#ifdef Py_GIL_DISABLED
+ for (PyThreadState *p = interp->threads.head; p != NULL; p = p->next) {
+ /* This may race with other threads modifications to their reftotal */
+ _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)p;
+ total += _Py_atomic_load_ssize_relaxed(&tstate_impl->reftotal);
+ }
+#endif
+ return total;
}
static inline Py_ssize_t
@@ -128,7 +135,7 @@ get_global_reftotal(_PyRuntimeState *runtime)
HEAD_LOCK(&_PyRuntime);
PyInterpreterState *interp = PyInterpreterState_Head();
for (; interp != NULL; interp = PyInterpreterState_Next(interp)) {
- total += REFTOTAL(interp);
+ total += get_reftotal(interp);
}
HEAD_UNLOCK(&_PyRuntime);
@@ -160,6 +167,7 @@ _PyDebug_PrintTotalRefs(void) {
#ifdef Py_TRACE_REFS
#define REFCHAIN(interp) interp->object_state.refchain
+#define REFCHAIN_VALUE ((void*)(uintptr_t)1)
static inline int
has_own_refchain(PyInterpreterState *interp)
@@ -171,48 +179,80 @@ has_own_refchain(PyInterpreterState *interp)
return 1;
}
-static inline void
-init_refchain(PyInterpreterState *interp)
+static int
+refchain_init(PyInterpreterState *interp)
{
if (!has_own_refchain(interp)) {
// Legacy subinterpreters share a refchain with the main interpreter.
REFCHAIN(interp) = REFCHAIN(_PyInterpreterState_Main());
- return;
+ return 0;
}
- REFCHAIN(interp) = &interp->object_state._refchain_obj;
- PyObject *refchain = REFCHAIN(interp);
- refchain->_ob_prev = refchain;
- refchain->_ob_next = refchain;
+ _Py_hashtable_allocator_t alloc = {
+ // Don't use default PyMem_Malloc() and PyMem_Free() which
+ // require the caller to hold the GIL.
+ .malloc = PyMem_RawMalloc,
+ .free = PyMem_RawFree,
+ };
+ REFCHAIN(interp) = _Py_hashtable_new_full(
+ _Py_hashtable_hash_ptr, _Py_hashtable_compare_direct,
+ NULL, NULL, &alloc);
+ if (REFCHAIN(interp) == NULL) {
+ return -1;
+ }
+ return 0;
}
-/* Insert op at the front of the list of all objects. If force is true,
- * op is added even if _ob_prev and _ob_next are non-NULL already. If
- * force is false amd _ob_prev or _ob_next are non-NULL, do nothing.
- * force should be true if and only if op points to freshly allocated,
- * uninitialized memory, or you've unlinked op from the list and are
- * relinking it into the front.
- * Note that objects are normally added to the list via _Py_NewReference,
- * which is called by PyObject_Init. Not all objects are initialized that
- * way, though; exceptions include statically allocated type objects, and
- * statically allocated singletons (like Py_True and Py_None).
- */
-void
-_Py_AddToAllObjects(PyObject *op, int force)
+static void
+refchain_fini(PyInterpreterState *interp)
{
-#ifdef Py_DEBUG
- if (!force) {
- /* If it's initialized memory, op must be in or out of
- * the list unambiguously.
- */
- _PyObject_ASSERT(op, (op->_ob_prev == NULL) == (op->_ob_next == NULL));
+ if (has_own_refchain(interp) && REFCHAIN(interp) != NULL) {
+ _Py_hashtable_destroy(REFCHAIN(interp));
}
+ REFCHAIN(interp) = NULL;
+}
+
+bool
+_PyRefchain_IsTraced(PyInterpreterState *interp, PyObject *obj)
+{
+ return (_Py_hashtable_get(REFCHAIN(interp), obj) == REFCHAIN_VALUE);
+}
+
+
+static void
+_PyRefchain_Trace(PyInterpreterState *interp, PyObject *obj)
+{
+ if (_Py_hashtable_set(REFCHAIN(interp), obj, REFCHAIN_VALUE) < 0) {
+ // Use a fatal error because _Py_NewReference() cannot report
+ // the error to the caller.
+ Py_FatalError("_Py_hashtable_set() memory allocation failed");
+ }
+}
+
+
+static void
+_PyRefchain_Remove(PyInterpreterState *interp, PyObject *obj)
+{
+ void *value = _Py_hashtable_steal(REFCHAIN(interp), obj);
+#ifndef NDEBUG
+ assert(value == REFCHAIN_VALUE);
+#else
+ (void)value;
#endif
- if (force || op->_ob_prev == NULL) {
- PyObject *refchain = REFCHAIN(_PyInterpreterState_GET());
- op->_ob_next = refchain->_ob_next;
- op->_ob_prev = refchain;
- refchain->_ob_next->_ob_prev = op;
- refchain->_ob_next = op;
+}
+
+
+/* Add an object to the refchain hash table.
+ *
+ * Note that objects are normally added to the list by PyObject_Init()
+ * indirectly. Not all objects are initialized that way, though; exceptions
+ * include statically allocated type objects, and statically allocated
+ * singletons (like Py_True and Py_None). */
+void
+_Py_AddToAllObjects(PyObject *op)
+{
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (!_PyRefchain_IsTraced(interp, op)) {
+ _PyRefchain_Trace(interp, op);
}
}
#endif /* Py_TRACE_REFS */
@@ -230,32 +270,32 @@ _Py_NegativeRefcount(const char *filename, int lineno, PyObject *op)
void
_Py_INCREF_IncRefTotal(void)
{
- reftotal_increment(_PyInterpreterState_GET());
+ reftotal_add(_PyThreadState_GET(), 1);
}
/* This is used strictly by Py_DECREF(). */
void
_Py_DECREF_DecRefTotal(void)
{
- reftotal_decrement(_PyInterpreterState_GET());
+ reftotal_add(_PyThreadState_GET(), -1);
}
void
-_Py_IncRefTotal(PyInterpreterState *interp)
+_Py_IncRefTotal(PyThreadState *tstate)
{
- reftotal_increment(interp);
+ reftotal_add(tstate, 1);
}
void
-_Py_DecRefTotal(PyInterpreterState *interp)
+_Py_DecRefTotal(PyThreadState *tstate)
{
- reftotal_decrement(interp);
+ reftotal_add(tstate, -1);
}
void
-_Py_AddRefTotal(PyInterpreterState *interp, Py_ssize_t n)
+_Py_AddRefTotal(PyThreadState *tstate, Py_ssize_t n)
{
- reftotal_add(interp, n);
+ reftotal_add(tstate, n);
}
/* This includes the legacy total
@@ -275,7 +315,10 @@ _Py_GetLegacyRefTotal(void)
Py_ssize_t
_PyInterpreterState_GetRefTotal(PyInterpreterState *interp)
{
- return get_reftotal(interp);
+ HEAD_LOCK(&_PyRuntime);
+ Py_ssize_t total = get_reftotal(interp);
+ HEAD_UNLOCK(&_PyRuntime);
+ return total;
}
#endif /* Py_REF_DEBUG */
@@ -304,6 +347,162 @@ _Py_DecRef(PyObject *o)
Py_DECREF(o);
}
+#ifdef Py_GIL_DISABLED
+# ifdef Py_REF_DEBUG
+static int
+is_dead(PyObject *o)
+{
+# if SIZEOF_SIZE_T == 8
+ return (uintptr_t)o->ob_type == 0xDDDDDDDDDDDDDDDD;
+# else
+ return (uintptr_t)o->ob_type == 0xDDDDDDDD;
+# endif
+}
+# endif
+
+// Decrement the shared reference count of an object. Return 1 if the object
+// is dead and should be deallocated, 0 otherwise.
+static int
+_Py_DecRefSharedIsDead(PyObject *o, const char *filename, int lineno)
+{
+ // Should we queue the object for the owning thread to merge?
+ int should_queue;
+
+ Py_ssize_t new_shared;
+ Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&o->ob_ref_shared);
+ do {
+ should_queue = (shared == 0 || shared == _Py_REF_MAYBE_WEAKREF);
+
+ if (should_queue) {
+ // If the object had refcount zero, not queued, and not merged,
+ // then we enqueue the object to be merged by the owning thread.
+ // In this case, we don't subtract one from the reference count
+ // because the queue holds a reference.
+ new_shared = _Py_REF_QUEUED;
+ }
+ else {
+ // Otherwise, subtract one from the reference count. This might
+ // be negative!
+ new_shared = shared - (1 << _Py_REF_SHARED_SHIFT);
+ }
+
+#ifdef Py_REF_DEBUG
+ if ((new_shared < 0 && _Py_REF_IS_MERGED(new_shared)) ||
+ (should_queue && is_dead(o)))
+ {
+ _Py_NegativeRefcount(filename, lineno, o);
+ }
+#endif
+ } while (!_Py_atomic_compare_exchange_ssize(&o->ob_ref_shared,
+ &shared, new_shared));
+
+ if (should_queue) {
+#ifdef Py_REF_DEBUG
+ _Py_IncRefTotal(_PyThreadState_GET());
+#endif
+ _Py_brc_queue_object(o);
+ }
+ else if (new_shared == _Py_REF_MERGED) {
+ // refcount is zero AND merged
+ return 1;
+ }
+ return 0;
+}
+
+void
+_Py_DecRefSharedDebug(PyObject *o, const char *filename, int lineno)
+{
+ if (_Py_DecRefSharedIsDead(o, filename, lineno)) {
+ _Py_Dealloc(o);
+ }
+}
+
+void
+_Py_DecRefShared(PyObject *o)
+{
+ _Py_DecRefSharedDebug(o, NULL, 0);
+}
+
+void
+_Py_MergeZeroLocalRefcount(PyObject *op)
+{
+ assert(op->ob_ref_local == 0);
+
+ Py_ssize_t shared = _Py_atomic_load_ssize_acquire(&op->ob_ref_shared);
+ if (shared == 0) {
+ // Fast-path: shared refcount is zero (including flags)
+ _Py_Dealloc(op);
+ return;
+ }
+
+ // gh-121794: This must be before the store to `ob_ref_shared` (gh-119999),
+ // but should outside the fast-path to maintain the invariant that
+ // a zero `ob_tid` implies a merged refcount.
+ _Py_atomic_store_uintptr_relaxed(&op->ob_tid, 0);
+
+ // Slow-path: atomically set the flags (low two bits) to _Py_REF_MERGED.
+ Py_ssize_t new_shared;
+ do {
+ new_shared = (shared & ~_Py_REF_SHARED_FLAG_MASK) | _Py_REF_MERGED;
+ } while (!_Py_atomic_compare_exchange_ssize(&op->ob_ref_shared,
+ &shared, new_shared));
+
+ if (new_shared == _Py_REF_MERGED) {
+ // i.e., the shared refcount is zero (only the flags are set) so we
+ // deallocate the object.
+ _Py_Dealloc(op);
+ }
+}
+
+Py_ssize_t
+_Py_ExplicitMergeRefcount(PyObject *op, Py_ssize_t extra)
+{
+ assert(!_Py_IsImmortal(op));
+
+#ifdef Py_REF_DEBUG
+ _Py_AddRefTotal(_PyThreadState_GET(), extra);
+#endif
+
+ // gh-119999: Write to ob_ref_local and ob_tid before merging the refcount.
+ Py_ssize_t local = (Py_ssize_t)op->ob_ref_local;
+ _Py_atomic_store_uint32_relaxed(&op->ob_ref_local, 0);
+ _Py_atomic_store_uintptr_relaxed(&op->ob_tid, 0);
+
+ Py_ssize_t refcnt;
+ Py_ssize_t new_shared;
+ Py_ssize_t shared = _Py_atomic_load_ssize_relaxed(&op->ob_ref_shared);
+ do {
+ refcnt = Py_ARITHMETIC_RIGHT_SHIFT(Py_ssize_t, shared, _Py_REF_SHARED_SHIFT);
+ refcnt += local;
+ refcnt += extra;
+
+ new_shared = _Py_REF_SHARED(refcnt, _Py_REF_MERGED);
+ } while (!_Py_atomic_compare_exchange_ssize(&op->ob_ref_shared,
+ &shared, new_shared));
+ return refcnt;
+}
+
+// The more complicated "slow" path for undoing the resurrection of an object.
+int
+_PyObject_ResurrectEndSlow(PyObject *op)
+{
+ if (_Py_IsImmortal(op)) {
+ return 1;
+ }
+ if (_Py_IsOwnedByCurrentThread(op)) {
+ // If the object is owned by the current thread, give up ownership and
+ // merge the refcount. This isn't necessary in all cases, but it
+ // simplifies the implementation.
+ Py_ssize_t refcount = _Py_ExplicitMergeRefcount(op, -1);
+ return refcount != 0;
+ }
+ int is_dead = _Py_DecRefSharedIsDead(op, NULL, 0);
+ return !is_dead;
+}
+
+
+#endif /* Py_GIL_DISABLED */
+
/**************************************/
@@ -380,7 +579,7 @@ PyObject_CallFinalizerFromDealloc(PyObject *self)
}
/* Temporarily resurrect the object. */
- Py_SET_REFCNT(self, 1);
+ _PyObject_ResurrectStart(self);
PyObject_CallFinalizer(self);
@@ -390,16 +589,13 @@ PyObject_CallFinalizerFromDealloc(PyObject *self)
/* Undo the temporary resurrection; can't use DECREF here, it would
* cause a recursive call. */
- Py_SET_REFCNT(self, Py_REFCNT(self) - 1);
- if (Py_REFCNT(self) == 0) {
+ if (!_PyObject_ResurrectEnd(self)) {
return 0; /* this is the normal path out */
}
/* tp_finalize resurrected it! Make it look like the original Py_DECREF
* never happened. */
- Py_ssize_t refcnt = Py_REFCNT(self);
- _Py_NewReferenceNoTotal(self);
- Py_SET_REFCNT(self, refcnt);
+ _Py_ResurrectReference(self);
_PyObject_ASSERT(self,
(!_PyType_IS_GC(Py_TYPE(self))
@@ -411,6 +607,7 @@ int
PyObject_Print(PyObject *op, FILE *fp, int flags)
{
int ret = 0;
+ int write_error = 0;
if (PyErr_CheckSignals())
return -1;
#ifdef USE_STACKCHECK
@@ -449,14 +646,20 @@ PyObject_Print(PyObject *op, FILE *fp, int flags)
ret = -1;
}
else {
- fwrite(t, 1, len, fp);
+ /* Versions of Android and OpenBSD from before 2023 fail to
+ set the `ferror` indicator when writing to a read-only
+ stream, so we need to check the return value.
+ (https://github.com/openbsd/src/commit/fc99cf9338942ecd9adc94ea08bf6188f0428c15) */
+ if (fwrite(t, 1, len, fp) != (size_t)len) {
+ write_error = 1;
+ }
}
Py_DECREF(s);
}
}
}
if (ret == 0) {
- if (ferror(fp)) {
+ if (write_error || ferror(fp)) {
PyErr_SetFromErrno(PyExc_OSError);
clearerr(fp);
ret = -1;
@@ -484,21 +687,11 @@ _PyObject_IsFreed(PyObject *op)
if (_PyMem_IsPtrFreed(op) || _PyMem_IsPtrFreed(Py_TYPE(op))) {
return 1;
}
- /* ignore op->ob_ref: its value can have be modified
- by Py_INCREF() and Py_DECREF(). */
-#ifdef Py_TRACE_REFS
- if (op->_ob_next != NULL && _PyMem_IsPtrFreed(op->_ob_next)) {
- return 1;
- }
- if (op->_ob_prev != NULL && _PyMem_IsPtrFreed(op->_ob_prev)) {
- return 1;
- }
-#endif
return 0;
}
-/* For debugging convenience. See Misc/gdbinit for some useful gdb hooks */
+/* For debugging convenience. */
void
_PyObject_Dump(PyObject* op)
{
@@ -582,11 +775,6 @@ PyObject_Repr(PyObject *v)
Py_DECREF(res);
return NULL;
}
-#ifndef Py_DEBUG
- if (PyUnicode_READY(res) < 0) {
- return NULL;
- }
-#endif
return res;
}
@@ -605,10 +793,6 @@ PyObject_Str(PyObject *v)
if (v == NULL)
return PyUnicode_FromString("<NULL>");
if (PyUnicode_CheckExact(v)) {
-#ifndef Py_DEBUG
- if (PyUnicode_READY(v) < 0)
- return NULL;
-#endif
return Py_NewRef(v);
}
if (Py_TYPE(v)->tp_str == NULL)
@@ -640,11 +824,6 @@ PyObject_Str(PyObject *v)
Py_DECREF(res);
return NULL;
}
-#ifndef Py_DEBUG
- if (PyUnicode_READY(res) < 0) {
- return NULL;
- }
-#endif
assert(_PyUnicode_CheckConsistency(res, 1));
return res;
}
@@ -708,6 +887,21 @@ PyObject_Bytes(PyObject *v)
return PyBytes_FromObject(v);
}
+void
+_PyObject_ClearFreeLists(struct _Py_object_freelists *freelists, int is_finalization)
+{
+ // In the free-threaded build, freelists are per-PyThreadState and cleared in PyThreadState_Clear()
+ // In the default build, freelists are per-interpreter and cleared in finalize_interp_types()
+ _PyFloat_ClearFreeList(freelists, is_finalization);
+ _PyTuple_ClearFreeList(freelists, is_finalization);
+ _PyList_ClearFreeList(freelists, is_finalization);
+ _PyDict_ClearFreeList(freelists, is_finalization);
+ _PyContext_ClearFreeList(freelists, is_finalization);
+ _PyAsyncGen_ClearFreeLists(freelists, is_finalization);
+ // Only be cleared if is_finalization is true.
+ _PyObjectStackChunk_ClearFreeList(freelists, is_finalization);
+ _PySlice_ClearFreeList(freelists, is_finalization);
+}
/*
def _PyObject_FunctionStr(x):
@@ -728,7 +922,7 @@ _PyObject_FunctionStr(PyObject *x)
{
assert(!PyErr_Occurred());
PyObject *qualname;
- int ret = _PyObject_LookupAttr(x, &_Py_ID(__qualname__), &qualname);
+ int ret = PyObject_GetOptionalAttr(x, &_Py_ID(__qualname__), &qualname);
if (qualname == NULL) {
if (ret < 0) {
return NULL;
@@ -737,7 +931,7 @@ _PyObject_FunctionStr(PyObject *x)
}
PyObject *module;
PyObject *result = NULL;
- ret = _PyObject_LookupAttr(x, &_Py_ID(__module__), &module);
+ ret = PyObject_GetOptionalAttr(x, &_Py_ID(__module__), &module);
if (module != NULL && module != Py_None) {
ret = PyObject_RichCompareBool(module, &_Py_ID(builtins), Py_NE);
if (ret < 0) {
@@ -940,26 +1134,27 @@ PyObject_GetAttrString(PyObject *v, const char *name)
}
int
-PyObject_HasAttrString(PyObject *v, const char *name)
+PyObject_HasAttrStringWithError(PyObject *obj, const char *name)
{
- if (Py_TYPE(v)->tp_getattr != NULL) {
- PyObject *res = (*Py_TYPE(v)->tp_getattr)(v, (char*)name);
- if (res != NULL) {
- Py_DECREF(res);
- return 1;
- }
- PyErr_Clear();
- return 0;
- }
+ PyObject *res;
+ int rc = PyObject_GetOptionalAttrString(obj, name, &res);
+ Py_XDECREF(res);
+ return rc;
+}
- PyObject *attr_name = PyUnicode_FromString(name);
- if (attr_name == NULL) {
- PyErr_Clear();
+
+int
+PyObject_HasAttrString(PyObject *obj, const char *name)
+{
+ int rc = PyObject_HasAttrStringWithError(obj, name);
+ if (rc < 0) {
+ PyErr_FormatUnraisable(
+ "Exception ignored in PyObject_HasAttrString(); consider using "
+ "PyObject_HasAttrStringWithError(), "
+ "PyObject_GetOptionalAttrString() or PyObject_GetAttrString()");
return 0;
}
- int ok = PyObject_HasAttr(v, attr_name);
- Py_DECREF(attr_name);
- return ok;
+ return rc;
}
int
@@ -979,6 +1174,12 @@ PyObject_SetAttrString(PyObject *v, const char *name, PyObject *w)
}
int
+PyObject_DelAttrString(PyObject *v, const char *name)
+{
+ return PyObject_SetAttrString(v, name, NULL);
+}
+
+int
_PyObject_IsAbstract(PyObject *obj)
{
int res;
@@ -987,7 +1188,7 @@ _PyObject_IsAbstract(PyObject *obj)
if (obj == NULL)
return 0;
- res = _PyObject_LookupAttr(obj, &_Py_ID(__isabstractmethod__), &isabstract);
+ res = PyObject_GetOptionalAttr(obj, &_Py_ID(__isabstractmethod__), &isabstract);
if (res > 0) {
res = PyObject_IsTrue(isabstract);
Py_DECREF(isabstract);
@@ -1017,8 +1218,8 @@ _PyObject_SetAttrId(PyObject *v, _Py_Identifier *name, PyObject *w)
return result;
}
-static inline int
-set_attribute_error_context(PyObject* v, PyObject* name)
+int
+_PyObject_SetAttributeErrorContext(PyObject* v, PyObject* name)
{
assert(PyErr_Occurred());
if (!PyErr_ExceptionMatches(PyExc_AttributeError)){
@@ -1073,13 +1274,13 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
}
if (result == NULL) {
- set_attribute_error_context(v, name);
+ _PyObject_SetAttributeErrorContext(v, name);
}
return result;
}
int
-_PyObject_LookupAttr(PyObject *v, PyObject *name, PyObject **result)
+PyObject_GetOptionalAttr(PyObject *v, PyObject *name, PyObject **result)
{
PyTypeObject *tp = Py_TYPE(v);
@@ -1101,7 +1302,7 @@ _PyObject_LookupAttr(PyObject *v, PyObject *name, PyObject **result)
}
return 0;
}
- if (tp->tp_getattro == (getattrofunc)_Py_type_getattro) {
+ if (tp->tp_getattro == _Py_type_getattro) {
int supress_missing_attribute_exception = 0;
*result = _Py_type_getattro_impl((PyTypeObject*)v, name, &supress_missing_attribute_exception);
if (supress_missing_attribute_exception) {
@@ -1147,29 +1348,51 @@ _PyObject_LookupAttr(PyObject *v, PyObject *name, PyObject **result)
}
int
-_PyObject_LookupAttrId(PyObject *v, _Py_Identifier *name, PyObject **result)
+PyObject_GetOptionalAttrString(PyObject *obj, const char *name, PyObject **result)
{
- PyObject *oname = _PyUnicode_FromId(name); /* borrowed */
- if (!oname) {
- *result = NULL;
+ if (Py_TYPE(obj)->tp_getattr == NULL) {
+ PyObject *oname = PyUnicode_FromString(name);
+ if (oname == NULL) {
+ *result = NULL;
+ return -1;
+ }
+ int rc = PyObject_GetOptionalAttr(obj, oname, result);
+ Py_DECREF(oname);
+ return rc;
+ }
+
+ *result = (*Py_TYPE(obj)->tp_getattr)(obj, (char*)name);
+ if (*result != NULL) {
+ return 1;
+ }
+ if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
return -1;
}
- return _PyObject_LookupAttr(v, oname, result);
+ PyErr_Clear();
+ return 0;
}
int
-PyObject_HasAttr(PyObject *v, PyObject *name)
+PyObject_HasAttrWithError(PyObject *obj, PyObject *name)
{
PyObject *res;
- if (_PyObject_LookupAttr(v, name, &res) < 0) {
- PyErr_Clear();
- return 0;
- }
- if (res == NULL) {
+ int rc = PyObject_GetOptionalAttr(obj, name, &res);
+ Py_XDECREF(res);
+ return rc;
+}
+
+int
+PyObject_HasAttr(PyObject *obj, PyObject *name)
+{
+ int rc = PyObject_HasAttrWithError(obj, name);
+ if (rc < 0) {
+ PyErr_FormatUnraisable(
+ "Exception ignored in PyObject_HasAttr(); consider using "
+ "PyObject_HasAttrWithError(), "
+ "PyObject_GetOptionalAttr() or PyObject_GetAttr()");
return 0;
}
- Py_DECREF(res);
- return 1;
+ return rc;
}
int
@@ -1222,6 +1445,12 @@ PyObject_SetAttr(PyObject *v, PyObject *name, PyObject *value)
return -1;
}
+int
+PyObject_DelAttr(PyObject *v, PyObject *name)
+{
+ return PyObject_SetAttr(v, name, NULL);
+}
+
PyObject **
_PyObject_ComputedDictPointer(PyObject *obj)
{
@@ -1263,16 +1492,15 @@ _PyObject_GetDictPtr(PyObject *obj)
if ((Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
return _PyObject_ComputedDictPointer(obj);
}
- PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(*dorv_ptr)) {
- PyObject *dict = _PyObject_MakeDictFromInstanceAttributes(obj, _PyDictOrValues_GetValues(*dorv_ptr));
+ PyDictObject *dict = _PyObject_GetManagedDict(obj);
+ if (dict == NULL && Py_TYPE(obj)->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
+ dict = _PyObject_MaterializeManagedDict(obj);
if (dict == NULL) {
PyErr_Clear();
return NULL;
}
- dorv_ptr->dict = dict;
}
- return &dorv_ptr->dict;
+ return (PyObject **)&_PyObject_ManagedDictPointer(obj)->dict;
}
PyObject *
@@ -1325,13 +1553,13 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
return 0;
}
- PyObject *descr = _PyType_Lookup(tp, name);
+ PyObject *descr = _PyType_LookupRef(tp, name);
descrgetfunc f = NULL;
if (descr != NULL) {
- Py_INCREF(descr);
if (_PyType_HasFeature(Py_TYPE(descr), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
meth_found = 1;
- } else {
+ }
+ else {
f = Py_TYPE(descr)->tp_descr_get;
if (f != NULL && PyDescr_IsData(descr)) {
*method = f(descr, obj, (PyObject *)Py_TYPE(obj));
@@ -1340,27 +1568,23 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
}
}
}
- PyObject *dict;
- if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
- PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(*dorv_ptr)) {
- PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
- PyObject *attr = _PyObject_GetInstanceAttribute(obj, values, name);
- if (attr != NULL) {
- *method = attr;
- Py_XDECREF(descr);
- return 0;
- }
- dict = NULL;
- }
- else {
- dict = dorv_ptr->dict;
+ PyObject *dict, *attr;
+ if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES) &&
+ _PyObject_TryGetInstanceAttribute(obj, name, &attr)) {
+ if (attr != NULL) {
+ *method = attr;
+ Py_XDECREF(descr);
+ return 0;
}
+ dict = NULL;
+ }
+ else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
+ dict = (PyObject *)_PyObject_GetManagedDict(obj);
}
else {
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
if (dictptr != NULL) {
- dict = *dictptr;
+ dict = FT_ATOMIC_LOAD_PTR_ACQUIRE(*dictptr);
}
else {
dict = NULL;
@@ -1368,19 +1592,14 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
}
if (dict != NULL) {
Py_INCREF(dict);
- PyObject *attr = PyDict_GetItemWithError(dict, name);
- if (attr != NULL) {
- *method = Py_NewRef(attr);
+ if (PyDict_GetItemRef(dict, name, method) != 0) {
+ // found or error
Py_DECREF(dict);
Py_XDECREF(descr);
return 0;
}
+ // not found
Py_DECREF(dict);
-
- if (PyErr_Occurred()) {
- Py_XDECREF(descr);
- return 0;
- }
}
if (meth_found) {
@@ -1403,7 +1622,7 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
"'%.100s' object has no attribute '%U'",
tp->tp_name, name);
- set_attribute_error_context(obj, name);
+ _PyObject_SetAttributeErrorContext(obj, name);
return 0;
}
@@ -1437,11 +1656,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
goto done;
}
- descr = _PyType_Lookup(tp, name);
+ descr = _PyType_LookupRef(tp, name);
f = NULL;
if (descr != NULL) {
- Py_INCREF(descr);
f = Py_TYPE(descr)->tp_descr_get;
if (f != NULL && PyDescr_IsData(descr)) {
res = f(descr, obj, (PyObject *)Py_TYPE(obj));
@@ -1453,53 +1671,48 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
}
}
if (dict == NULL) {
- if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
- PyDictOrValues* dorv_ptr = _PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(*dorv_ptr)) {
- PyDictValues *values = _PyDictOrValues_GetValues(*dorv_ptr);
- if (PyUnicode_CheckExact(name)) {
- res = _PyObject_GetInstanceAttribute(obj, values, name);
- if (res != NULL) {
- goto done;
- }
- }
- else {
- dict = _PyObject_MakeDictFromInstanceAttributes(obj, values);
- if (dict == NULL) {
- res = NULL;
- goto done;
- }
- dorv_ptr->dict = dict;
+ if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES)) {
+ if (PyUnicode_CheckExact(name) &&
+ _PyObject_TryGetInstanceAttribute(obj, name, &res)) {
+ if (res != NULL) {
+ goto done;
}
}
else {
- dict = _PyDictOrValues_GetDict(*dorv_ptr);
+ dict = (PyObject *)_PyObject_MaterializeManagedDict(obj);
+ if (dict == NULL) {
+ res = NULL;
+ goto done;
+ }
}
}
+ else if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
+ dict = (PyObject *)_PyObject_GetManagedDict(obj);
+ }
else {
PyObject **dictptr = _PyObject_ComputedDictPointer(obj);
if (dictptr) {
+#ifdef Py_GIL_DISABLED
+ dict = _Py_atomic_load_ptr_acquire(dictptr);
+#else
dict = *dictptr;
+#endif
}
}
}
if (dict != NULL) {
Py_INCREF(dict);
- res = PyDict_GetItemWithError(dict, name);
+ int rc = PyDict_GetItemRef(dict, name, &res);
+ Py_DECREF(dict);
if (res != NULL) {
- Py_INCREF(res);
- Py_DECREF(dict);
goto done;
}
- else {
- Py_DECREF(dict);
- if (PyErr_Occurred()) {
- if (suppress && PyErr_ExceptionMatches(PyExc_AttributeError)) {
- PyErr_Clear();
- }
- else {
- goto done;
- }
+ else if (rc < 0) {
+ if (suppress && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ }
+ else {
+ goto done;
}
}
}
@@ -1524,7 +1737,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
"'%.100s' object has no attribute '%U'",
tp->tp_name, name);
- set_attribute_error_context(obj, name);
+ _PyObject_SetAttributeErrorContext(obj, name);
}
done:
Py_XDECREF(descr);
@@ -1547,6 +1760,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
descrsetfunc f;
int res = -1;
+ assert(!PyType_IsSubtype(tp, &PyType_Type));
if (!PyUnicode_Check(name)){
PyErr_Format(PyExc_TypeError,
"attribute name must be string, not '%.200s'",
@@ -1560,10 +1774,9 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
Py_INCREF(name);
Py_INCREF(tp);
- descr = _PyType_Lookup(tp, name);
+ descr = _PyType_LookupRef(tp, name);
if (descr != NULL) {
- Py_INCREF(descr);
f = Py_TYPE(descr)->tp_descr_set;
if (f != NULL) {
res = f(descr, obj, value);
@@ -1573,23 +1786,33 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
if (dict == NULL) {
PyObject **dictptr;
+
+ if ((tp->tp_flags & Py_TPFLAGS_INLINE_VALUES)) {
+ res = _PyObject_StoreInstanceAttribute(obj, name, value);
+ goto error_check;
+ }
+
if ((tp->tp_flags & Py_TPFLAGS_MANAGED_DICT)) {
- PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(obj);
- if (_PyDictOrValues_IsValues(*dorv_ptr)) {
- res = _PyObject_StoreInstanceAttribute(
- obj, _PyDictOrValues_GetValues(*dorv_ptr), name, value);
- goto error_check;
- }
- dictptr = &dorv_ptr->dict;
+ PyManagedDictPointer *managed_dict = _PyObject_ManagedDictPointer(obj);
+ dictptr = (PyObject **)&managed_dict->dict;
}
else {
dictptr = _PyObject_ComputedDictPointer(obj);
}
if (dictptr == NULL) {
if (descr == NULL) {
- PyErr_Format(PyExc_AttributeError,
- "'%.100s' object has no attribute '%U'",
- tp->tp_name, name);
+ if (tp->tp_setattro == PyObject_GenericSetAttr) {
+ PyErr_Format(PyExc_AttributeError,
+ "'%.100s' object has no attribute '%U' and no "
+ "__dict__ for setting new attributes",
+ tp->tp_name, name);
+ }
+ else {
+ PyErr_Format(PyExc_AttributeError,
+ "'%.100s' object has no attribute '%U'",
+ tp->tp_name, name);
+ }
+ _PyObject_SetAttributeErrorContext(obj, name);
}
else {
PyErr_Format(PyExc_AttributeError,
@@ -1599,7 +1822,7 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
goto done;
}
else {
- res = _PyObjectDict_SetItem(tp, dictptr, name, value);
+ res = _PyObjectDict_SetItem(tp, obj, dictptr, name, value);
}
}
else {
@@ -1612,16 +1835,10 @@ _PyObject_GenericSetAttrWithDict(PyObject *obj, PyObject *name,
}
error_check:
if (res < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
- if (PyType_IsSubtype(tp, &PyType_Type)) {
- PyErr_Format(PyExc_AttributeError,
- "type object '%.50s' has no attribute '%U'",
- ((PyTypeObject*)obj)->tp_name, name);
- }
- else {
- PyErr_Format(PyExc_AttributeError,
- "'%.100s' object has no attribute '%U'",
- tp->tp_name, name);
- }
+ PyErr_Format(PyExc_AttributeError,
+ "'%.100s' object has no attribute '%U'",
+ tp->tp_name, name);
+ _PyObject_SetAttributeErrorContext(obj, name);
}
done:
Py_XDECREF(descr);
@@ -1641,9 +1858,9 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context)
{
PyObject **dictptr = _PyObject_GetDictPtr(obj);
if (dictptr == NULL) {
- if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_MANAGED_DICT) &&
- _PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(obj)))
- {
+ if (_PyType_HasFeature(Py_TYPE(obj), Py_TPFLAGS_INLINE_VALUES) &&
+ _PyObject_GetManagedDict(obj) == NULL
+ ) {
/* Was unable to convert to dict */
PyErr_NoMemory();
}
@@ -1663,7 +1880,9 @@ PyObject_GenericSetDict(PyObject *obj, PyObject *value, void *context)
"not a '%.200s'", Py_TYPE(value)->tp_name);
return -1;
}
+ Py_BEGIN_CRITICAL_SECTION(obj);
Py_XSETREF(*dictptr, Py_NewRef(value));
+ Py_END_CRITICAL_SECTION();
return 0;
}
@@ -1871,6 +2090,11 @@ static PyNumberMethods none_as_number = {
0, /* nb_index */
};
+PyDoc_STRVAR(none_doc,
+"NoneType()\n"
+"--\n\n"
+"The type of the None singleton.");
+
PyTypeObject _PyNone_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"NoneType",
@@ -1892,7 +2116,7 @@ PyTypeObject _PyNone_Type = {
0, /*tp_setattro */
0, /*tp_as_buffer */
Py_TPFLAGS_DEFAULT, /*tp_flags */
- 0, /*tp_doc */
+ none_doc, /*tp_doc */
0, /*tp_traverse */
0, /*tp_clear */
_Py_BaseObject_RichCompare, /*tp_richcompare */
@@ -1912,11 +2136,7 @@ PyTypeObject _PyNone_Type = {
none_new, /*tp_new */
};
-PyObject _Py_NoneStruct = {
- _PyObject_EXTRA_INIT
- { _Py_IMMORTAL_REFCNT },
- &_PyNone_Type
-};
+PyObject _Py_NoneStruct = _PyObject_HEAD_INIT(&_PyNone_Type);
/* NotImplemented is an object that can be used to signal that an
operation is not implemented for the given type combination. */
@@ -1974,6 +2194,11 @@ static PyNumberMethods notimplemented_as_number = {
.nb_bool = notimplemented_bool,
};
+PyDoc_STRVAR(notimplemented_doc,
+"NotImplementedType()\n"
+"--\n\n"
+"The type of the NotImplemented singleton.");
+
PyTypeObject _PyNotImplemented_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"NotImplementedType",
@@ -1995,7 +2220,7 @@ PyTypeObject _PyNotImplemented_Type = {
0, /*tp_setattro */
0, /*tp_as_buffer */
Py_TPFLAGS_DEFAULT, /*tp_flags */
- 0, /*tp_doc */
+ notimplemented_doc, /*tp_doc */
0, /*tp_traverse */
0, /*tp_clear */
0, /*tp_richcompare */
@@ -2015,27 +2240,35 @@ PyTypeObject _PyNotImplemented_Type = {
notimplemented_new, /*tp_new */
};
-PyObject _Py_NotImplementedStruct = {
- _PyObject_EXTRA_INIT
- { _Py_IMMORTAL_REFCNT },
- &_PyNotImplemented_Type
-};
+PyObject _Py_NotImplementedStruct = _PyObject_HEAD_INIT(&_PyNotImplemented_Type);
-void
+PyStatus
_PyObject_InitState(PyInterpreterState *interp)
{
#ifdef Py_TRACE_REFS
- init_refchain(interp);
+ if (refchain_init(interp) < 0) {
+ return _PyStatus_NO_MEMORY();
+ }
#endif
+ return _PyStatus_OK();
}
+void
+_PyObject_FiniState(PyInterpreterState *interp)
+{
+#ifdef Py_TRACE_REFS
+ refchain_fini(interp);
+#endif
+}
-extern PyTypeObject _Py_GenericAliasIterType;
-extern PyTypeObject _PyMemoryIter_Type;
+
+extern PyTypeObject _PyAnextAwaitable_Type;
+extern PyTypeObject _PyLegacyEventHandler_Type;
extern PyTypeObject _PyLineIterator;
+extern PyTypeObject _PyMemoryIter_Type;
extern PyTypeObject _PyPositionsIterator;
-extern PyTypeObject _PyLegacyEventHandler_Type;
+extern PyTypeObject _Py_GenericAliasIterType;
static PyTypeObject* static_types[] = {
// The two most important base types: must be initialized first and
@@ -2077,6 +2310,7 @@ static PyTypeObject* static_types[] = {
&PyFilter_Type,
&PyFloat_Type,
&PyFrame_Type,
+ &PyFrameLocalsProxy_Type,
&PyFrozenSet_Type,
&PyFunction_Type,
&PyGen_Type,
@@ -2123,6 +2357,11 @@ static PyTypeObject* static_types[] = {
&_PyBufferWrapper_Type,
&_PyContextTokenMissing_Type,
&_PyCoroWrapper_Type,
+#ifdef _Py_TIER2
+ &_PyCounterExecutor_Type,
+ &_PyCounterOptimizer_Type,
+ &_PyDefaultOptimizer_Type,
+#endif
&_Py_GenericAliasIterType,
&_PyHamtItems_Type,
&_PyHamtKeys_Type,
@@ -2131,8 +2370,8 @@ static PyTypeObject* static_types[] = {
&_PyHamt_BitmapNode_Type,
&_PyHamt_CollisionNode_Type,
&_PyHamt_Type,
+ &_PyInstructionSequence_Type,
&_PyLegacyEventHandler_Type,
- &_PyInterpreterID_Type,
&_PyLineIterator,
&_PyManagedBuffer_Type,
&_PyMemoryIter_Type,
@@ -2143,10 +2382,15 @@ static PyTypeObject* static_types[] = {
&_PyPositionsIterator,
&_PyUnicodeASCIIIter_Type,
&_PyUnion_Type,
+#ifdef _Py_TIER2
+ &_PyUOpExecutor_Type,
+ &_PyUOpOptimizer_Type,
+#endif
&_PyWeakref_CallableProxyType,
&_PyWeakref_ProxyType,
&_PyWeakref_RefType,
&_PyTypeAlias_Type,
+ &_PyNoDefault_Type,
// subclasses: _PyTypes_FiniTypes() deallocates them before their base
// class
@@ -2175,6 +2419,14 @@ _PyTypes_InitTypes(PyInterpreterState *interp)
}
}
+ // Cache __reduce__ from PyBaseObject_Type object
+ PyObject *baseobj_dict = _PyType_GetDict(&PyBaseObject_Type);
+ PyObject *baseobj_reduce = PyDict_GetItemWithError(baseobj_dict, &_Py_ID(__reduce__));
+ if (baseobj_reduce == NULL && PyErr_Occurred()) {
+ return _PyStatus_ERR("Can't get __reduce__ from base object");
+ }
+ _Py_INTERP_CACHED_OBJECT(interp, objreduce) = baseobj_reduce;
+
// Must be after static types are initialized
if (_Py_initialize_generic(interp) < 0) {
return _PyStatus_ERR("Can't initialize generic types");
@@ -2197,7 +2449,7 @@ _PyTypes_FiniTypes(PyInterpreterState *interp)
// their base classes.
for (Py_ssize_t i=Py_ARRAY_LENGTH(static_types)-1; i>=0; i--) {
PyTypeObject *type = static_types[i];
- _PyStaticType_Dealloc(interp, type);
+ _PyStaticType_FiniBuiltin(interp, type);
}
}
@@ -2205,21 +2457,28 @@ _PyTypes_FiniTypes(PyInterpreterState *interp)
static inline void
new_reference(PyObject *op)
{
- if (_PyRuntime.tracemalloc.config.tracing) {
- _PyTraceMalloc_NewReference(op);
- }
// Skip the immortal object check in Py_SET_REFCNT; always set refcnt to 1
+#if !defined(Py_GIL_DISABLED)
op->ob_refcnt = 1;
+#else
+ op->ob_tid = _Py_ThreadId();
+ op->_padding = 0;
+ op->ob_mutex = (PyMutex){ 0 };
+ op->ob_gc_bits = 0;
+ op->ob_ref_local = 1;
+ op->ob_ref_shared = 0;
+#endif
#ifdef Py_TRACE_REFS
- _Py_AddToAllObjects(op, 1);
+ _Py_AddToAllObjects(op);
#endif
+ _PyReftracerTrack(op, PyRefTracer_CREATE);
}
void
_Py_NewReference(PyObject *op)
{
#ifdef Py_REF_DEBUG
- reftotal_increment(_PyInterpreterState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
#endif
new_reference(op);
}
@@ -2230,6 +2489,62 @@ _Py_NewReferenceNoTotal(PyObject *op)
new_reference(op);
}
+void
+_Py_SetImmortalUntracked(PyObject *op)
+{
+#ifdef Py_DEBUG
+ // For strings, use _PyUnicode_InternImmortal instead.
+ if (PyUnicode_CheckExact(op)) {
+ assert(PyUnicode_CHECK_INTERNED(op) == SSTATE_INTERNED_IMMORTAL
+ || PyUnicode_CHECK_INTERNED(op) == SSTATE_INTERNED_IMMORTAL_STATIC);
+ }
+#endif
+#ifdef Py_GIL_DISABLED
+ op->ob_tid = _Py_UNOWNED_TID;
+ op->ob_ref_local = _Py_IMMORTAL_REFCNT_LOCAL;
+ op->ob_ref_shared = 0;
+#else
+ op->ob_refcnt = _Py_IMMORTAL_REFCNT;
+#endif
+}
+
+void
+_Py_SetImmortal(PyObject *op)
+{
+ if (PyObject_IS_GC(op) && _PyObject_GC_IS_TRACKED(op)) {
+ _PyObject_GC_UNTRACK(op);
+ }
+ _Py_SetImmortalUntracked(op);
+}
+
+void
+_PyObject_SetDeferredRefcount(PyObject *op)
+{
+#ifdef Py_GIL_DISABLED
+ assert(PyType_IS_GC(Py_TYPE(op)));
+ assert(_Py_IsOwnedByCurrentThread(op));
+ assert(op->ob_ref_shared == 0);
+ _PyObject_SET_GC_BITS(op, _PyGC_BITS_DEFERRED);
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (_Py_atomic_load_int_relaxed(&interp->gc.immortalize) == 1) {
+ // gh-117696: immortalize objects instead of using deferred reference
+ // counting for now.
+ _Py_SetImmortal(op);
+ return;
+ }
+ op->ob_ref_local += 1;
+ op->ob_ref_shared = _Py_REF_QUEUED;
+#endif
+}
+
+void
+_Py_ResurrectReference(PyObject *op)
+{
+#ifdef Py_TRACE_REFS
+ _Py_AddToAllObjects(op);
+#endif
+}
+
#ifdef Py_TRACE_REFS
void
@@ -2239,53 +2554,62 @@ _Py_ForgetReference(PyObject *op)
_PyObject_ASSERT_FAILED_MSG(op, "negative refcnt");
}
- PyObject *refchain = REFCHAIN(_PyInterpreterState_GET());
- if (op == refchain ||
- op->_ob_prev->_ob_next != op || op->_ob_next->_ob_prev != op)
- {
- _PyObject_ASSERT_FAILED_MSG(op, "invalid object chain");
- }
+ PyInterpreterState *interp = _PyInterpreterState_GET();
#ifdef SLOW_UNREF_CHECK
- PyObject *p;
- for (p = refchain->_ob_next; p != refchain; p = p->_ob_next) {
- if (p == op) {
- break;
- }
- }
- if (p == refchain) {
+ if (!_PyRefchain_Get(interp, op)) {
/* Not found */
_PyObject_ASSERT_FAILED_MSG(op,
"object not found in the objects list");
}
#endif
- op->_ob_next->_ob_prev = op->_ob_prev;
- op->_ob_prev->_ob_next = op->_ob_next;
- op->_ob_next = op->_ob_prev = NULL;
+ _PyRefchain_Remove(interp, op);
}
+static int
+_Py_PrintReference(_Py_hashtable_t *ht,
+ const void *key, const void *value,
+ void *user_data)
+{
+ PyObject *op = (PyObject*)key;
+ FILE *fp = (FILE *)user_data;
+ fprintf(fp, "%p [%zd] ", (void *)op, Py_REFCNT(op));
+ if (PyObject_Print(op, fp, 0) != 0) {
+ PyErr_Clear();
+ }
+ putc('\n', fp);
+ return 0;
+}
+
+
/* Print all live objects. Because PyObject_Print is called, the
* interpreter must be in a healthy state.
*/
void
_Py_PrintReferences(PyInterpreterState *interp, FILE *fp)
{
- PyObject *op;
if (interp == NULL) {
interp = _PyInterpreterState_Main();
}
fprintf(fp, "Remaining objects:\n");
- PyObject *refchain = REFCHAIN(interp);
- for (op = refchain->_ob_next; op != refchain; op = op->_ob_next) {
- fprintf(fp, "%p [%zd] ", (void *)op, Py_REFCNT(op));
- if (PyObject_Print(op, fp, 0) != 0) {
- PyErr_Clear();
- }
- putc('\n', fp);
- }
+ _Py_hashtable_foreach(REFCHAIN(interp), _Py_PrintReference, fp);
+}
+
+
+static int
+_Py_PrintReferenceAddress(_Py_hashtable_t *ht,
+ const void *key, const void *value,
+ void *user_data)
+{
+ PyObject *op = (PyObject*)key;
+ FILE *fp = (FILE *)user_data;
+ fprintf(fp, "%p [%zd] %s\n",
+ (void *)op, Py_REFCNT(op), Py_TYPE(op)->tp_name);
+ return 0;
}
+
/* Print the addresses of all live objects. Unlike _Py_PrintReferences, this
* doesn't make any calls to the Python C API, so is always safe to call.
*/
@@ -2296,47 +2620,96 @@ _Py_PrintReferences(PyInterpreterState *interp, FILE *fp)
void
_Py_PrintReferenceAddresses(PyInterpreterState *interp, FILE *fp)
{
- PyObject *op;
- PyObject *refchain = REFCHAIN(interp);
fprintf(fp, "Remaining object addresses:\n");
- for (op = refchain->_ob_next; op != refchain; op = op->_ob_next)
- fprintf(fp, "%p [%zd] %s\n", (void *)op,
- Py_REFCNT(op), Py_TYPE(op)->tp_name);
+ _Py_hashtable_foreach(REFCHAIN(interp), _Py_PrintReferenceAddress, fp);
+}
+
+
+typedef struct {
+ PyObject *self;
+ PyObject *args;
+ PyObject *list;
+ PyObject *type;
+ Py_ssize_t limit;
+} _Py_GetObjectsData;
+
+enum {
+ _PY_GETOBJECTS_IGNORE = 0,
+ _PY_GETOBJECTS_ERROR = 1,
+ _PY_GETOBJECTS_STOP = 2,
+};
+
+static int
+_Py_GetObject(_Py_hashtable_t *ht,
+ const void *key, const void *value,
+ void *user_data)
+{
+ PyObject *op = (PyObject *)key;
+ _Py_GetObjectsData *data = user_data;
+ if (data->limit > 0) {
+ if (PyList_GET_SIZE(data->list) >= data->limit) {
+ return _PY_GETOBJECTS_STOP;
+ }
+ }
+
+ if (op == data->self) {
+ return _PY_GETOBJECTS_IGNORE;
+ }
+ if (op == data->args) {
+ return _PY_GETOBJECTS_IGNORE;
+ }
+ if (op == data->list) {
+ return _PY_GETOBJECTS_IGNORE;
+ }
+ if (data->type != NULL) {
+ if (op == data->type) {
+ return _PY_GETOBJECTS_IGNORE;
+ }
+ if (!Py_IS_TYPE(op, (PyTypeObject *)data->type)) {
+ return _PY_GETOBJECTS_IGNORE;
+ }
+ }
+
+ if (PyList_Append(data->list, op) < 0) {
+ return _PY_GETOBJECTS_ERROR;
+ }
+ return 0;
}
+
/* The implementation of sys.getobjects(). */
PyObject *
_Py_GetObjects(PyObject *self, PyObject *args)
{
- int i, n;
- PyObject *t = NULL;
- PyObject *res, *op;
- PyInterpreterState *interp = _PyInterpreterState_GET();
+ Py_ssize_t limit;
+ PyObject *type = NULL;
+ if (!PyArg_ParseTuple(args, "n|O", &limit, &type)) {
+ return NULL;
+ }
- if (!PyArg_ParseTuple(args, "i|O", &n, &t))
+ PyObject *list = PyList_New(0);
+ if (list == NULL) {
return NULL;
- PyObject *refchain = REFCHAIN(interp);
- op = refchain->_ob_next;
- res = PyList_New(0);
- if (res == NULL)
+ }
+
+ _Py_GetObjectsData data = {
+ .self = self,
+ .args = args,
+ .list = list,
+ .type = type,
+ .limit = limit,
+ };
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ int res = _Py_hashtable_foreach(REFCHAIN(interp), _Py_GetObject, &data);
+ if (res == _PY_GETOBJECTS_ERROR) {
+ Py_DECREF(list);
return NULL;
- for (i = 0; (n == 0 || i < n) && op != refchain; i++) {
- while (op == self || op == args || op == res || op == t ||
- (t != NULL && !Py_IS_TYPE(op, (PyTypeObject *) t))) {
- op = op->_ob_next;
- if (op == refchain)
- return res;
- }
- if (PyList_Append(res, op) < 0) {
- Py_DECREF(res);
- return NULL;
- }
- op = op->_ob_next;
}
- return res;
+ return list;
}
#undef REFCHAIN
+#undef REFCHAIN_VALUE
#endif /* Py_TRACE_REFS */
@@ -2433,28 +2806,30 @@ finally:
/* Trashcan support. */
-#define _PyTrash_UNWIND_LEVEL 50
-
/* Add op to the gcstate->trash_delete_later list. Called when the current
* call-stack depth gets large. op must be a currently untracked gc'ed
* object, with refcount 0. Py_DECREF must already have been called on it.
*/
-static void
-_PyTrash_thread_deposit_object(struct _py_trashcan *trash, PyObject *op)
+void
+_PyTrash_thread_deposit_object(PyThreadState *tstate, PyObject *op)
{
_PyObject_ASSERT(op, _PyObject_IS_GC(op));
_PyObject_ASSERT(op, !_PyObject_GC_IS_TRACKED(op));
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
- _PyGCHead_SET_PREV(_Py_AS_GC(op), (PyGC_Head*)trash->delete_later);
- trash->delete_later = op;
+#ifdef Py_GIL_DISABLED
+ op->ob_tid = (uintptr_t)tstate->delete_later;
+#else
+ _PyGCHead_SET_PREV(_Py_AS_GC(op), (PyGC_Head*)tstate->delete_later);
+#endif
+ tstate->delete_later = op;
}
/* Deallocate all the objects in the gcstate->trash_delete_later list.
* Called when the call-stack unwinds again. */
-static void
-_PyTrash_thread_destroy_chain(struct _py_trashcan *trash)
+void
+_PyTrash_thread_destroy_chain(PyThreadState *tstate)
{
- /* We need to increase trash_delete_nesting here, otherwise,
+ /* We need to increase c_recursion_remaining here, otherwise,
_PyTrash_thread_destroy_chain will be called recursively
and then possibly crash. An example that may crash without
increase:
@@ -2465,14 +2840,19 @@ _PyTrash_thread_destroy_chain(struct _py_trashcan *trash)
tups = [(tup,) for tup in tups]
del tups
*/
- assert(trash->delete_nesting == 0);
- ++trash->delete_nesting;
- while (trash->delete_later) {
- PyObject *op = trash->delete_later;
+ assert(tstate->c_recursion_remaining > Py_TRASHCAN_HEADROOM);
+ tstate->c_recursion_remaining--;
+ while (tstate->delete_later) {
+ PyObject *op = tstate->delete_later;
destructor dealloc = Py_TYPE(op)->tp_dealloc;
- trash->delete_later =
- (PyObject*) _PyGCHead_PREV(_Py_AS_GC(op));
+#ifdef Py_GIL_DISABLED
+ tstate->delete_later = (PyObject*) op->ob_tid;
+ op->ob_tid = 0;
+ _Py_atomic_store_ssize_relaxed(&op->ob_ref_shared, _Py_REF_MERGED);
+#else
+ tstate->delete_later = (PyObject*) _PyGCHead_PREV(_Py_AS_GC(op));
+#endif
/* Call the deallocator directly. This used to try to
* fool Py_DECREF into calling it indirectly, but
@@ -2482,92 +2862,10 @@ _PyTrash_thread_destroy_chain(struct _py_trashcan *trash)
*/
_PyObject_ASSERT(op, Py_REFCNT(op) == 0);
(*dealloc)(op);
- assert(trash->delete_nesting == 1);
- }
- --trash->delete_nesting;
-}
-
-
-static struct _py_trashcan *
-_PyTrash_get_state(PyThreadState *tstate)
-{
- if (tstate != NULL) {
- return &tstate->trash;
- }
- // The current thread must be finalizing.
- // Fall back to using thread-local state.
- // XXX Use thread-local variable syntax?
- assert(PyThread_tss_is_created(&_PyRuntime.trashTSSkey));
- struct _py_trashcan *trash =
- (struct _py_trashcan *)PyThread_tss_get(&_PyRuntime.trashTSSkey);
- if (trash == NULL) {
- trash = PyMem_RawMalloc(sizeof(struct _py_trashcan));
- if (trash == NULL) {
- Py_FatalError("Out of memory");
- }
- PyThread_tss_set(&_PyRuntime.trashTSSkey, (void *)trash);
- }
- return trash;
-}
-
-static void
-_PyTrash_clear_state(PyThreadState *tstate)
-{
- if (tstate != NULL) {
- assert(tstate->trash.delete_later == NULL);
- return;
- }
- if (PyThread_tss_is_created(&_PyRuntime.trashTSSkey)) {
- struct _py_trashcan *trash =
- (struct _py_trashcan *)PyThread_tss_get(&_PyRuntime.trashTSSkey);
- if (trash != NULL) {
- PyThread_tss_set(&_PyRuntime.trashTSSkey, (void *)NULL);
- PyMem_RawFree(trash);
- }
}
+ tstate->c_recursion_remaining++;
}
-
-int
-_PyTrash_begin(PyThreadState *tstate, PyObject *op)
-{
- // XXX Make sure the GIL is held.
- struct _py_trashcan *trash = _PyTrash_get_state(tstate);
- if (trash->delete_nesting >= _PyTrash_UNWIND_LEVEL) {
- /* Store the object (to be deallocated later) and jump past
- * Py_TRASHCAN_END, skipping the body of the deallocator */
- _PyTrash_thread_deposit_object(trash, op);
- return 1;
- }
- ++trash->delete_nesting;
- return 0;
-}
-
-
-void
-_PyTrash_end(PyThreadState *tstate)
-{
- // XXX Make sure the GIL is held.
- struct _py_trashcan *trash = _PyTrash_get_state(tstate);
- --trash->delete_nesting;
- if (trash->delete_nesting <= 0) {
- if (trash->delete_later != NULL) {
- _PyTrash_thread_destroy_chain(trash);
- }
- _PyTrash_clear_state(tstate);
- }
-}
-
-
-/* bpo-40170: It's only be used in Py_TRASHCAN_BEGIN macro to hide
- implementation details. */
-int
-_PyTrash_cond(PyObject *op, destructor dealloc)
-{
- return Py_TYPE(op)->tp_dealloc == dealloc;
-}
-
-
void _Py_NO_RETURN
_PyObject_AssertFailed(PyObject *obj, const char *expr, const char *msg,
const char *file, int line, const char *function)
@@ -2637,6 +2935,7 @@ _Py_Dealloc(PyObject *op)
#ifdef Py_TRACE_REFS
_Py_ForgetReference(op);
#endif
+ _PyReftracerTrack(op, PyRefTracer_DESTROY);
(*dealloc)(op);
#ifdef Py_DEBUG
@@ -2714,6 +3013,76 @@ int Py_IsFalse(PyObject *x)
return Py_Is(x, Py_False);
}
-#ifdef __cplusplus
+
+// Py_SET_REFCNT() implementation for stable ABI
+void
+_Py_SetRefcnt(PyObject *ob, Py_ssize_t refcnt)
+{
+ Py_SET_REFCNT(ob, refcnt);
+}
+
+int PyRefTracer_SetTracer(PyRefTracer tracer, void *data) {
+ assert(PyGILState_Check());
+ _PyRuntime.ref_tracer.tracer_func = tracer;
+ _PyRuntime.ref_tracer.tracer_data = data;
+ return 0;
+}
+
+PyRefTracer PyRefTracer_GetTracer(void** data) {
+ assert(PyGILState_Check());
+ if (data != NULL) {
+ *data = _PyRuntime.ref_tracer.tracer_data;
+ }
+ return _PyRuntime.ref_tracer.tracer_func;
}
+
+
+
+static PyObject* constants[] = {
+ &_Py_NoneStruct, // Py_CONSTANT_NONE
+ (PyObject*)(&_Py_FalseStruct), // Py_CONSTANT_FALSE
+ (PyObject*)(&_Py_TrueStruct), // Py_CONSTANT_TRUE
+ &_Py_EllipsisObject, // Py_CONSTANT_ELLIPSIS
+ &_Py_NotImplementedStruct, // Py_CONSTANT_NOT_IMPLEMENTED
+ NULL, // Py_CONSTANT_ZERO
+ NULL, // Py_CONSTANT_ONE
+ NULL, // Py_CONSTANT_EMPTY_STR
+ NULL, // Py_CONSTANT_EMPTY_BYTES
+ NULL, // Py_CONSTANT_EMPTY_TUPLE
+};
+
+void
+_Py_GetConstant_Init(void)
+{
+ constants[Py_CONSTANT_ZERO] = _PyLong_GetZero();
+ constants[Py_CONSTANT_ONE] = _PyLong_GetOne();
+ constants[Py_CONSTANT_EMPTY_STR] = PyUnicode_New(0, 0);
+ constants[Py_CONSTANT_EMPTY_BYTES] = PyBytes_FromStringAndSize(NULL, 0);
+ constants[Py_CONSTANT_EMPTY_TUPLE] = PyTuple_New(0);
+#ifndef NDEBUG
+ for (size_t i=0; i < Py_ARRAY_LENGTH(constants); i++) {
+ assert(constants[i] != NULL);
+ assert(_Py_IsImmortal(constants[i]));
+ }
#endif
+}
+
+PyObject*
+Py_GetConstant(unsigned int constant_id)
+{
+ if (constant_id < Py_ARRAY_LENGTH(constants)) {
+ return constants[constant_id];
+ }
+ else {
+ PyErr_BadInternalCall();
+ return NULL;
+ }
+}
+
+
+PyObject*
+Py_GetConstantBorrowed(unsigned int constant_id)
+{
+ // All constants are immortal
+ return Py_GetConstant(constant_id);
+}
diff --git a/contrib/tools/python3/Objects/obmalloc.c b/contrib/tools/python3/Objects/obmalloc.c
index 9620a8fbb44..3fdc9d01f2d 100644
--- a/contrib/tools/python3/Objects/obmalloc.c
+++ b/contrib/tools/python3/Objects/obmalloc.c
@@ -2,13 +2,30 @@
#include "Python.h"
#include "pycore_code.h" // stats
-#include "pycore_pystate.h" // _PyInterpreterState_GET
-
+#include "pycore_object.h" // _PyDebugAllocatorStats() definition
#include "pycore_obmalloc.h"
+#include "pycore_pyerrors.h" // _Py_FatalErrorFormat()
#include "pycore_pymem.h"
+#include "pycore_pystate.h" // _PyInterpreterState_GET
+#include "pycore_obmalloc_init.h"
#include <stdlib.h> // malloc()
#include <stdbool.h>
+#ifdef WITH_MIMALLOC
+// Forward declarations of functions used in our mimalloc modifications
+static void _PyMem_mi_page_clear_qsbr(mi_page_t *page);
+static bool _PyMem_mi_page_is_safe_to_free(mi_page_t *page);
+static bool _PyMem_mi_page_maybe_free(mi_page_t *page, mi_page_queue_t *pq, bool force);
+static void _PyMem_mi_page_reclaimed(mi_page_t *page);
+static void _PyMem_mi_heap_collect_qsbr(mi_heap_t *heap);
+# include "pycore_mimalloc.h"
+# include "mimalloc/static.c"
+# include "mimalloc/internal.h" // for stats
+#endif
+
+#if defined(Py_GIL_DISABLED) && !defined(WITH_MIMALLOC)
+# error "Py_GIL_DISABLED requires WITH_MIMALLOC"
+#endif
#undef uint
#define uint pymem_uint
@@ -73,25 +90,272 @@ _PyMem_RawFree(void *Py_UNUSED(ctx), void *ptr)
free(ptr);
}
+#ifdef WITH_MIMALLOC
+
+static void
+_PyMem_mi_page_clear_qsbr(mi_page_t *page)
+{
+#ifdef Py_GIL_DISABLED
+ // Clear the QSBR goal and remove the page from the QSBR linked list.
+ page->qsbr_goal = 0;
+ if (page->qsbr_node.next != NULL) {
+ llist_remove(&page->qsbr_node);
+ }
+#endif
+}
+
+// Check if an empty, newly reclaimed page is safe to free now.
+static bool
+_PyMem_mi_page_is_safe_to_free(mi_page_t *page)
+{
+ assert(mi_page_all_free(page));
+#ifdef Py_GIL_DISABLED
+ assert(page->qsbr_node.next == NULL);
+ if (page->use_qsbr && page->qsbr_goal != 0) {
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ if (tstate == NULL) {
+ return false;
+ }
+ return _Py_qbsr_goal_reached(tstate->qsbr, page->qsbr_goal);
+ }
+#endif
+ return true;
+
+}
+
+#ifdef Py_GIL_DISABLED
+
+// If we are deferring collection of more than this amount of memory for
+// mimalloc pages, advance the write sequence. Advancing allows these
+// pages to be re-used in a different thread or for a different size class.
+#define QSBR_PAGE_MEM_LIMIT 4096*20
+
+// Return true if the global write sequence should be advanced for a mimalloc
+// page that is deferred from collection.
+static bool
+should_advance_qsbr_for_page(struct _qsbr_thread_state *qsbr, mi_page_t *page)
+{
+ size_t bsize = mi_page_block_size(page);
+ size_t page_size = page->capacity*bsize;
+ if (page_size > QSBR_PAGE_MEM_LIMIT) {
+ qsbr->deferred_page_memory = 0;
+ return true;
+ }
+ qsbr->deferred_page_memory += page_size;
+ if (qsbr->deferred_page_memory > QSBR_PAGE_MEM_LIMIT) {
+ qsbr->deferred_page_memory = 0;
+ return true;
+ }
+ return false;
+}
+#endif
+
+static bool
+_PyMem_mi_page_maybe_free(mi_page_t *page, mi_page_queue_t *pq, bool force)
+{
+#ifdef Py_GIL_DISABLED
+ assert(mi_page_all_free(page));
+ if (page->use_qsbr) {
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)PyThreadState_GET();
+ if (page->qsbr_goal != 0 && _Py_qbsr_goal_reached(tstate->qsbr, page->qsbr_goal)) {
+ _PyMem_mi_page_clear_qsbr(page);
+ _mi_page_free(page, pq, force);
+ return true;
+ }
+
+ _PyMem_mi_page_clear_qsbr(page);
+ page->retire_expire = 0;
+
+ if (should_advance_qsbr_for_page(tstate->qsbr, page)) {
+ page->qsbr_goal = _Py_qsbr_advance(tstate->qsbr->shared);
+ }
+ else {
+ page->qsbr_goal = _Py_qsbr_shared_next(tstate->qsbr->shared);
+ }
+
+ llist_insert_tail(&tstate->mimalloc.page_list, &page->qsbr_node);
+ return false;
+ }
+#endif
+ _mi_page_free(page, pq, force);
+ return true;
+}
+
+static void
+_PyMem_mi_page_reclaimed(mi_page_t *page)
+{
+#ifdef Py_GIL_DISABLED
+ assert(page->qsbr_node.next == NULL);
+ if (page->qsbr_goal != 0) {
+ if (mi_page_all_free(page)) {
+ assert(page->qsbr_node.next == NULL);
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)PyThreadState_GET();
+ page->retire_expire = 0;
+ llist_insert_tail(&tstate->mimalloc.page_list, &page->qsbr_node);
+ }
+ else {
+ page->qsbr_goal = 0;
+ }
+ }
+#endif
+}
+
+static void
+_PyMem_mi_heap_collect_qsbr(mi_heap_t *heap)
+{
+#ifdef Py_GIL_DISABLED
+ if (!heap->page_use_qsbr) {
+ return;
+ }
+
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ struct llist_node *head = &tstate->mimalloc.page_list;
+ if (llist_empty(head)) {
+ return;
+ }
+
+ struct llist_node *node;
+ llist_for_each_safe(node, head) {
+ mi_page_t *page = llist_data(node, mi_page_t, qsbr_node);
+ if (!mi_page_all_free(page)) {
+ // We allocated from this page some point after the delayed free
+ _PyMem_mi_page_clear_qsbr(page);
+ continue;
+ }
+
+ if (!_Py_qsbr_poll(tstate->qsbr, page->qsbr_goal)) {
+ return;
+ }
+
+ _PyMem_mi_page_clear_qsbr(page);
+ _mi_page_free(page, mi_page_queue_of(page), false);
+ }
+#endif
+}
+
+void *
+_PyMem_MiMalloc(void *ctx, size_t size)
+{
+#ifdef Py_GIL_DISABLED
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ mi_heap_t *heap = &tstate->mimalloc.heaps[_Py_MIMALLOC_HEAP_MEM];
+ return mi_heap_malloc(heap, size);
+#else
+ return mi_malloc(size);
+#endif
+}
+
+void *
+_PyMem_MiCalloc(void *ctx, size_t nelem, size_t elsize)
+{
+#ifdef Py_GIL_DISABLED
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ mi_heap_t *heap = &tstate->mimalloc.heaps[_Py_MIMALLOC_HEAP_MEM];
+ return mi_heap_calloc(heap, nelem, elsize);
+#else
+ return mi_calloc(nelem, elsize);
+#endif
+}
+
+void *
+_PyMem_MiRealloc(void *ctx, void *ptr, size_t size)
+{
+#ifdef Py_GIL_DISABLED
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ mi_heap_t *heap = &tstate->mimalloc.heaps[_Py_MIMALLOC_HEAP_MEM];
+ return mi_heap_realloc(heap, ptr, size);
+#else
+ return mi_realloc(ptr, size);
+#endif
+}
+
+void
+_PyMem_MiFree(void *ctx, void *ptr)
+{
+ mi_free(ptr);
+}
+
+void *
+_PyObject_MiMalloc(void *ctx, size_t nbytes)
+{
+#ifdef Py_GIL_DISABLED
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ mi_heap_t *heap = tstate->mimalloc.current_object_heap;
+ return mi_heap_malloc(heap, nbytes);
+#else
+ return mi_malloc(nbytes);
+#endif
+}
+
+void *
+_PyObject_MiCalloc(void *ctx, size_t nelem, size_t elsize)
+{
+#ifdef Py_GIL_DISABLED
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ mi_heap_t *heap = tstate->mimalloc.current_object_heap;
+ return mi_heap_calloc(heap, nelem, elsize);
+#else
+ return mi_calloc(nelem, elsize);
+#endif
+}
+
+
+void *
+_PyObject_MiRealloc(void *ctx, void *ptr, size_t nbytes)
+{
+#ifdef Py_GIL_DISABLED
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ mi_heap_t *heap = tstate->mimalloc.current_object_heap;
+ return mi_heap_realloc(heap, ptr, nbytes);
+#else
+ return mi_realloc(ptr, nbytes);
+#endif
+}
+
+void
+_PyObject_MiFree(void *ctx, void *ptr)
+{
+ mi_free(ptr);
+}
+
+#endif // WITH_MIMALLOC
+
+
#define MALLOC_ALLOC {NULL, _PyMem_RawMalloc, _PyMem_RawCalloc, _PyMem_RawRealloc, _PyMem_RawFree}
-#define PYRAW_ALLOC MALLOC_ALLOC
-/* the default object allocator */
+
+#ifdef WITH_MIMALLOC
+# define MIMALLOC_ALLOC {NULL, _PyMem_MiMalloc, _PyMem_MiCalloc, _PyMem_MiRealloc, _PyMem_MiFree}
+# define MIMALLOC_OBJALLOC {NULL, _PyObject_MiMalloc, _PyObject_MiCalloc, _PyObject_MiRealloc, _PyObject_MiFree}
+#endif
+
+/* the pymalloc allocator */
// The actual implementation is further down.
-#ifdef WITH_PYMALLOC
+#if defined(WITH_PYMALLOC)
void* _PyObject_Malloc(void *ctx, size_t size);
void* _PyObject_Calloc(void *ctx, size_t nelem, size_t elsize);
void _PyObject_Free(void *ctx, void *p);
void* _PyObject_Realloc(void *ctx, void *ptr, size_t size);
# define PYMALLOC_ALLOC {NULL, _PyObject_Malloc, _PyObject_Calloc, _PyObject_Realloc, _PyObject_Free}
+#endif // WITH_PYMALLOC
+
+#if defined(Py_GIL_DISABLED)
+// Py_GIL_DISABLED requires using mimalloc for "mem" and "obj" domains.
+# define PYRAW_ALLOC MALLOC_ALLOC
+# define PYMEM_ALLOC MIMALLOC_ALLOC
+# define PYOBJ_ALLOC MIMALLOC_OBJALLOC
+#elif defined(WITH_PYMALLOC)
+# define PYRAW_ALLOC MALLOC_ALLOC
+# define PYMEM_ALLOC PYMALLOC_ALLOC
# define PYOBJ_ALLOC PYMALLOC_ALLOC
#else
+# define PYRAW_ALLOC MALLOC_ALLOC
+# define PYMEM_ALLOC MALLOC_ALLOC
# define PYOBJ_ALLOC MALLOC_ALLOC
-#endif // WITH_PYMALLOC
+#endif
-#define PYMEM_ALLOC PYOBJ_ALLOC
/* the default debug allocators */
@@ -156,8 +420,16 @@ _PyMem_ArenaFree(void *Py_UNUSED(ctx), void *ptr,
)
{
#ifdef MS_WINDOWS
+ /* Unlike free(), VirtualFree() does not special-case NULL to noop. */
+ if (ptr == NULL) {
+ return;
+ }
VirtualFree(ptr, 0, MEM_RELEASE);
#elif defined(ARENAS_USE_MMAP)
+ /* Unlike free(), munmap() does not special-case NULL to noop. */
+ if (ptr == NULL) {
+ return;
+ }
munmap(ptr, size);
#else
free(ptr);
@@ -258,13 +530,9 @@ int
_PyMem_SetDefaultAllocator(PyMemAllocatorDomain domain,
PyMemAllocatorEx *old_alloc)
{
- if (ALLOCATORS_MUTEX == NULL) {
- /* The runtime must be initializing. */
- return set_default_allocator_unlocked(domain, pydebug, old_alloc);
- }
- PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
+ PyMutex_Lock(&ALLOCATORS_MUTEX);
int res = set_default_allocator_unlocked(domain, pydebug, old_alloc);
- PyThread_release_lock(ALLOCATORS_MUTEX);
+ PyMutex_Unlock(&ALLOCATORS_MUTEX);
return res;
}
@@ -283,7 +551,7 @@ _PyMem_GetAllocatorName(const char *name, PyMemAllocatorName *allocator)
else if (strcmp(name, "debug") == 0) {
*allocator = PYMEM_ALLOCATOR_DEBUG;
}
-#ifdef WITH_PYMALLOC
+#if defined(WITH_PYMALLOC) && !defined(Py_GIL_DISABLED)
else if (strcmp(name, "pymalloc") == 0) {
*allocator = PYMEM_ALLOCATOR_PYMALLOC;
}
@@ -291,12 +559,22 @@ _PyMem_GetAllocatorName(const char *name, PyMemAllocatorName *allocator)
*allocator = PYMEM_ALLOCATOR_PYMALLOC_DEBUG;
}
#endif
+#ifdef WITH_MIMALLOC
+ else if (strcmp(name, "mimalloc") == 0) {
+ *allocator = PYMEM_ALLOCATOR_MIMALLOC;
+ }
+ else if (strcmp(name, "mimalloc_debug") == 0) {
+ *allocator = PYMEM_ALLOCATOR_MIMALLOC_DEBUG;
+ }
+#endif
+#ifndef Py_GIL_DISABLED
else if (strcmp(name, "malloc") == 0) {
*allocator = PYMEM_ALLOCATOR_MALLOC;
}
else if (strcmp(name, "malloc_debug") == 0) {
*allocator = PYMEM_ALLOCATOR_MALLOC_DEBUG;
}
+#endif
else {
/* unknown allocator */
return -1;
@@ -317,12 +595,14 @@ set_up_allocators_unlocked(PyMemAllocatorName allocator)
(void)set_default_allocator_unlocked(PYMEM_DOMAIN_RAW, pydebug, NULL);
(void)set_default_allocator_unlocked(PYMEM_DOMAIN_MEM, pydebug, NULL);
(void)set_default_allocator_unlocked(PYMEM_DOMAIN_OBJ, pydebug, NULL);
+ _PyRuntime.allocators.is_debug_enabled = pydebug;
break;
case PYMEM_ALLOCATOR_DEBUG:
(void)set_default_allocator_unlocked(PYMEM_DOMAIN_RAW, 1, NULL);
(void)set_default_allocator_unlocked(PYMEM_DOMAIN_MEM, 1, NULL);
(void)set_default_allocator_unlocked(PYMEM_DOMAIN_OBJ, 1, NULL);
+ _PyRuntime.allocators.is_debug_enabled = 1;
break;
#ifdef WITH_PYMALLOC
@@ -336,9 +616,33 @@ set_up_allocators_unlocked(PyMemAllocatorName allocator)
set_allocator_unlocked(PYMEM_DOMAIN_MEM, &pymalloc);
set_allocator_unlocked(PYMEM_DOMAIN_OBJ, &pymalloc);
- if (allocator == PYMEM_ALLOCATOR_PYMALLOC_DEBUG) {
+ int is_debug = (allocator == PYMEM_ALLOCATOR_PYMALLOC_DEBUG);
+ _PyRuntime.allocators.is_debug_enabled = is_debug;
+ if (is_debug) {
+ set_up_debug_hooks_unlocked();
+ }
+ break;
+ }
+#endif
+#ifdef WITH_MIMALLOC
+ case PYMEM_ALLOCATOR_MIMALLOC:
+ case PYMEM_ALLOCATOR_MIMALLOC_DEBUG:
+ {
+ PyMemAllocatorEx malloc_alloc = MALLOC_ALLOC;
+ set_allocator_unlocked(PYMEM_DOMAIN_RAW, &malloc_alloc);
+
+ PyMemAllocatorEx pymalloc = MIMALLOC_ALLOC;
+ set_allocator_unlocked(PYMEM_DOMAIN_MEM, &pymalloc);
+
+ PyMemAllocatorEx objmalloc = MIMALLOC_OBJALLOC;
+ set_allocator_unlocked(PYMEM_DOMAIN_OBJ, &objmalloc);
+
+ int is_debug = (allocator == PYMEM_ALLOCATOR_MIMALLOC_DEBUG);
+ _PyRuntime.allocators.is_debug_enabled = is_debug;
+ if (is_debug) {
set_up_debug_hooks_unlocked();
}
+
break;
}
#endif
@@ -351,7 +655,9 @@ set_up_allocators_unlocked(PyMemAllocatorName allocator)
set_allocator_unlocked(PYMEM_DOMAIN_MEM, &malloc_alloc);
set_allocator_unlocked(PYMEM_DOMAIN_OBJ, &malloc_alloc);
- if (allocator == PYMEM_ALLOCATOR_MALLOC_DEBUG) {
+ int is_debug = (allocator == PYMEM_ALLOCATOR_MALLOC_DEBUG);
+ _PyRuntime.allocators.is_debug_enabled = is_debug;
+ if (is_debug) {
set_up_debug_hooks_unlocked();
}
break;
@@ -368,9 +674,9 @@ set_up_allocators_unlocked(PyMemAllocatorName allocator)
int
_PyMem_SetupAllocators(PyMemAllocatorName allocator)
{
- PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
+ PyMutex_Lock(&ALLOCATORS_MUTEX);
int res = set_up_allocators_unlocked(allocator);
- PyThread_release_lock(ALLOCATORS_MUTEX);
+ PyMutex_Unlock(&ALLOCATORS_MUTEX);
return res;
}
@@ -389,6 +695,10 @@ get_current_allocator_name_unlocked(void)
#ifdef WITH_PYMALLOC
PyMemAllocatorEx pymalloc = PYMALLOC_ALLOC;
#endif
+#ifdef WITH_MIMALLOC
+ PyMemAllocatorEx mimalloc = MIMALLOC_ALLOC;
+ PyMemAllocatorEx mimalloc_obj = MIMALLOC_OBJALLOC;
+#endif
if (pymemallocator_eq(&_PyMem_Raw, &malloc_alloc) &&
pymemallocator_eq(&_PyMem, &malloc_alloc) &&
@@ -404,6 +714,14 @@ get_current_allocator_name_unlocked(void)
return "pymalloc";
}
#endif
+#ifdef WITH_MIMALLOC
+ if (pymemallocator_eq(&_PyMem_Raw, &malloc_alloc) &&
+ pymemallocator_eq(&_PyMem, &mimalloc) &&
+ pymemallocator_eq(&_PyObject, &mimalloc_obj))
+ {
+ return "mimalloc";
+ }
+#endif
PyMemAllocatorEx dbg_raw = PYDBGRAW_ALLOC;
PyMemAllocatorEx dbg_mem = PYDBGMEM_ALLOC;
@@ -428,6 +746,14 @@ get_current_allocator_name_unlocked(void)
return "pymalloc_debug";
}
#endif
+#ifdef WITH_MIMALLOC
+ if (pymemallocator_eq(&_PyMem_Debug.raw.alloc, &malloc_alloc) &&
+ pymemallocator_eq(&_PyMem_Debug.mem.alloc, &mimalloc) &&
+ pymemallocator_eq(&_PyMem_Debug.obj.alloc, &mimalloc_obj))
+ {
+ return "mimalloc_debug";
+ }
+#endif
}
return NULL;
}
@@ -435,20 +761,20 @@ get_current_allocator_name_unlocked(void)
const char*
_PyMem_GetCurrentAllocatorName(void)
{
- PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
+ PyMutex_Lock(&ALLOCATORS_MUTEX);
const char *name = get_current_allocator_name_unlocked();
- PyThread_release_lock(ALLOCATORS_MUTEX);
+ PyMutex_Unlock(&ALLOCATORS_MUTEX);
return name;
}
-#ifdef WITH_PYMALLOC
-static int
+int
_PyMem_DebugEnabled(void)
{
- return (_PyObject.malloc == _PyMem_DebugMalloc);
+ return _PyRuntime.allocators.is_debug_enabled;
}
+#ifdef WITH_PYMALLOC
static int
_PyMem_PymallocEnabled(void)
{
@@ -459,7 +785,25 @@ _PyMem_PymallocEnabled(void)
return (_PyObject.malloc == _PyObject_Malloc);
}
}
+
+#ifdef WITH_MIMALLOC
+static int
+_PyMem_MimallocEnabled(void)
+{
+#ifdef Py_GIL_DISABLED
+ return 1;
+#else
+ if (_PyMem_DebugEnabled()) {
+ return (_PyMem_Debug.obj.alloc.malloc == _PyObject_MiMalloc);
+ }
+ else {
+ return (_PyObject.malloc == _PyObject_MiMalloc);
+ }
#endif
+}
+#endif // WITH_MIMALLOC
+
+#endif // WITH_PYMALLOC
static void
@@ -515,19 +859,15 @@ set_up_debug_hooks_unlocked(void)
set_up_debug_hooks_domain_unlocked(PYMEM_DOMAIN_RAW);
set_up_debug_hooks_domain_unlocked(PYMEM_DOMAIN_MEM);
set_up_debug_hooks_domain_unlocked(PYMEM_DOMAIN_OBJ);
+ _PyRuntime.allocators.is_debug_enabled = 1;
}
void
PyMem_SetupDebugHooks(void)
{
- if (ALLOCATORS_MUTEX == NULL) {
- /* The runtime must not be completely initialized yet. */
- set_up_debug_hooks_unlocked();
- return;
- }
- PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
+ PyMutex_Lock(&ALLOCATORS_MUTEX);
set_up_debug_hooks_unlocked();
- PyThread_release_lock(ALLOCATORS_MUTEX);
+ PyMutex_Unlock(&ALLOCATORS_MUTEX);
}
static void
@@ -563,53 +903,33 @@ set_allocator_unlocked(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
void
PyMem_GetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
{
- if (ALLOCATORS_MUTEX == NULL) {
- /* The runtime must not be completely initialized yet. */
- get_allocator_unlocked(domain, allocator);
- return;
- }
- PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
+ PyMutex_Lock(&ALLOCATORS_MUTEX);
get_allocator_unlocked(domain, allocator);
- PyThread_release_lock(ALLOCATORS_MUTEX);
+ PyMutex_Unlock(&ALLOCATORS_MUTEX);
}
void
PyMem_SetAllocator(PyMemAllocatorDomain domain, PyMemAllocatorEx *allocator)
{
- if (ALLOCATORS_MUTEX == NULL) {
- /* The runtime must not be completely initialized yet. */
- set_allocator_unlocked(domain, allocator);
- return;
- }
- PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
+ PyMutex_Lock(&ALLOCATORS_MUTEX);
set_allocator_unlocked(domain, allocator);
- PyThread_release_lock(ALLOCATORS_MUTEX);
+ PyMutex_Unlock(&ALLOCATORS_MUTEX);
}
void
PyObject_GetArenaAllocator(PyObjectArenaAllocator *allocator)
{
- if (ALLOCATORS_MUTEX == NULL) {
- /* The runtime must not be completely initialized yet. */
- *allocator = _PyObject_Arena;
- return;
- }
- PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
+ PyMutex_Lock(&ALLOCATORS_MUTEX);
*allocator = _PyObject_Arena;
- PyThread_release_lock(ALLOCATORS_MUTEX);
+ PyMutex_Unlock(&ALLOCATORS_MUTEX);
}
void
PyObject_SetArenaAllocator(PyObjectArenaAllocator *allocator)
{
- if (ALLOCATORS_MUTEX == NULL) {
- /* The runtime must not be completely initialized yet. */
- _PyObject_Arena = *allocator;
- return;
- }
- PyThread_acquire_lock(ALLOCATORS_MUTEX, WAIT_LOCK);
+ PyMutex_Lock(&ALLOCATORS_MUTEX);
_PyObject_Arena = *allocator;
- PyThread_release_lock(ALLOCATORS_MUTEX);
+ PyMutex_Unlock(&ALLOCATORS_MUTEX);
}
@@ -783,6 +1103,285 @@ _PyMem_Strdup(const char *str)
return copy;
}
+/***********************************************/
+/* Delayed freeing support for Py_GIL_DISABLED */
+/***********************************************/
+
+// So that sizeof(struct _mem_work_chunk) is 4096 bytes on 64-bit platforms.
+#define WORK_ITEMS_PER_CHUNK 254
+
+// A pointer to be freed once the QSBR read sequence reaches qsbr_goal.
+struct _mem_work_item {
+ uintptr_t ptr; // lowest bit tagged 1 for objects freed with PyObject_Free
+ uint64_t qsbr_goal;
+};
+
+// A fixed-size buffer of pointers to be freed
+struct _mem_work_chunk {
+ // Linked list node of chunks in queue
+ struct llist_node node;
+
+ Py_ssize_t rd_idx; // index of next item to read
+ Py_ssize_t wr_idx; // index of next item to write
+ struct _mem_work_item array[WORK_ITEMS_PER_CHUNK];
+};
+
+static void
+free_work_item(uintptr_t ptr)
+{
+ if (ptr & 0x01) {
+ PyObject_Free((char *)(ptr - 1));
+ }
+ else {
+ PyMem_Free((void *)ptr);
+ }
+}
+
+
+#ifdef Py_GIL_DISABLED
+
+// For deferred advance on free: the number of deferred items before advancing
+// the write sequence. This is based on WORK_ITEMS_PER_CHUNK. We ideally
+// want to process a chunk before it overflows.
+#define QSBR_DEFERRED_LIMIT 127
+
+// If the deferred memory exceeds 1 MiB, advance the write sequence. This
+// helps limit memory usage due to QSBR delaying frees too long.
+#define QSBR_FREE_MEM_LIMIT 1024*1024
+
+// Return true if the global write sequence should be advanced for a deferred
+// memory free.
+static bool
+should_advance_qsbr_for_free(struct _qsbr_thread_state *qsbr, size_t size)
+{
+ if (size > QSBR_FREE_MEM_LIMIT) {
+ qsbr->deferred_count = 0;
+ qsbr->deferred_memory = 0;
+ qsbr->should_process = true;
+ return true;
+ }
+ qsbr->deferred_count++;
+ qsbr->deferred_memory += size;
+ if (qsbr->deferred_count > QSBR_DEFERRED_LIMIT ||
+ qsbr->deferred_memory > QSBR_FREE_MEM_LIMIT) {
+ qsbr->deferred_count = 0;
+ qsbr->deferred_memory = 0;
+ qsbr->should_process = true;
+ return true;
+ }
+ return false;
+}
+#endif
+
+static void
+free_delayed(uintptr_t ptr, size_t size)
+{
+#ifndef Py_GIL_DISABLED
+ free_work_item(ptr);
+#else
+ if (_PyRuntime.stoptheworld.world_stopped) {
+ // Free immediately if the world is stopped, including during
+ // interpreter shutdown.
+ free_work_item(ptr);
+ return;
+ }
+
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ struct llist_node *head = &tstate->mem_free_queue;
+
+ struct _mem_work_chunk *buf = NULL;
+ if (!llist_empty(head)) {
+ // Try to re-use the last buffer
+ buf = llist_data(head->prev, struct _mem_work_chunk, node);
+ if (buf->wr_idx == WORK_ITEMS_PER_CHUNK) {
+ // already full
+ buf = NULL;
+ }
+ }
+
+ if (buf == NULL) {
+ buf = PyMem_Calloc(1, sizeof(*buf));
+ if (buf != NULL) {
+ llist_insert_tail(head, &buf->node);
+ }
+ }
+
+ if (buf == NULL) {
+ // failed to allocate a buffer, free immediately
+ _PyEval_StopTheWorld(tstate->base.interp);
+ free_work_item(ptr);
+ _PyEval_StartTheWorld(tstate->base.interp);
+ return;
+ }
+
+ assert(buf != NULL && buf->wr_idx < WORK_ITEMS_PER_CHUNK);
+ uint64_t seq;
+ if (should_advance_qsbr_for_free(tstate->qsbr, size)) {
+ seq = _Py_qsbr_advance(tstate->qsbr->shared);
+ }
+ else {
+ seq = _Py_qsbr_shared_next(tstate->qsbr->shared);
+ }
+ buf->array[buf->wr_idx].ptr = ptr;
+ buf->array[buf->wr_idx].qsbr_goal = seq;
+ buf->wr_idx++;
+
+ if (buf->wr_idx == WORK_ITEMS_PER_CHUNK) {
+ // Normally the processing of delayed items is done from the eval
+ // breaker. Processing here is a safety measure to ensure too much
+ // work does not accumulate.
+ _PyMem_ProcessDelayed((PyThreadState *)tstate);
+ }
+#endif
+}
+
+void
+_PyMem_FreeDelayed(void *ptr, size_t size)
+{
+ assert(!((uintptr_t)ptr & 0x01));
+ if (ptr != NULL) {
+ free_delayed((uintptr_t)ptr, size);
+ }
+}
+
+void
+_PyObject_FreeDelayed(void *ptr)
+{
+ assert(!((uintptr_t)ptr & 0x01));
+ // We use 0 as the size since we don't have an easy way to know the
+ // actual size. If we are freeing many objects, the write sequence
+ // will be advanced due to QSBR_DEFERRED_LIMIT.
+ free_delayed(((uintptr_t)ptr)|0x01, 0);
+}
+
+static struct _mem_work_chunk *
+work_queue_first(struct llist_node *head)
+{
+ return llist_data(head->next, struct _mem_work_chunk, node);
+}
+
+static void
+process_queue(struct llist_node *head, struct _qsbr_thread_state *qsbr,
+ bool keep_empty)
+{
+ while (!llist_empty(head)) {
+ struct _mem_work_chunk *buf = work_queue_first(head);
+
+ while (buf->rd_idx < buf->wr_idx) {
+ struct _mem_work_item *item = &buf->array[buf->rd_idx];
+ if (!_Py_qsbr_poll(qsbr, item->qsbr_goal)) {
+ return;
+ }
+
+ free_work_item(item->ptr);
+ buf->rd_idx++;
+ }
+
+ assert(buf->rd_idx == buf->wr_idx);
+ if (keep_empty && buf->node.next == head) {
+ // Keep the last buffer in the queue to reduce re-allocations
+ buf->rd_idx = buf->wr_idx = 0;
+ return;
+ }
+
+ llist_remove(&buf->node);
+ PyMem_Free(buf);
+ }
+}
+
+static void
+process_interp_queue(struct _Py_mem_interp_free_queue *queue,
+ struct _qsbr_thread_state *qsbr)
+{
+ assert(PyMutex_IsLocked(&queue->mutex));
+ process_queue(&queue->head, qsbr, false);
+
+ int more_work = !llist_empty(&queue->head);
+ _Py_atomic_store_int_relaxed(&queue->has_work, more_work);
+}
+
+static void
+maybe_process_interp_queue(struct _Py_mem_interp_free_queue *queue,
+ struct _qsbr_thread_state *qsbr)
+{
+ if (!_Py_atomic_load_int_relaxed(&queue->has_work)) {
+ return;
+ }
+
+ // Try to acquire the lock, but don't block if it's already held.
+ if (_PyMutex_LockTimed(&queue->mutex, 0, 0) == PY_LOCK_ACQUIRED) {
+ process_interp_queue(queue, qsbr);
+ PyMutex_Unlock(&queue->mutex);
+ }
+}
+
+void
+_PyMem_ProcessDelayed(PyThreadState *tstate)
+{
+ PyInterpreterState *interp = tstate->interp;
+ _PyThreadStateImpl *tstate_impl = (_PyThreadStateImpl *)tstate;
+
+ tstate_impl->qsbr->should_process = false;
+
+ // Process thread-local work
+ process_queue(&tstate_impl->mem_free_queue, tstate_impl->qsbr, true);
+
+ // Process shared interpreter work
+ maybe_process_interp_queue(&interp->mem_free_queue, tstate_impl->qsbr);
+}
+
+void
+_PyMem_AbandonDelayed(PyThreadState *tstate)
+{
+ PyInterpreterState *interp = tstate->interp;
+ struct llist_node *queue = &((_PyThreadStateImpl *)tstate)->mem_free_queue;
+
+ if (llist_empty(queue)) {
+ return;
+ }
+
+ // Check if the queue contains one empty buffer
+ struct _mem_work_chunk *buf = work_queue_first(queue);
+ if (buf->rd_idx == buf->wr_idx) {
+ llist_remove(&buf->node);
+ PyMem_Free(buf);
+ assert(llist_empty(queue));
+ return;
+ }
+
+ PyMutex_Lock(&interp->mem_free_queue.mutex);
+
+ // Merge the thread's work queue into the interpreter's work queue.
+ llist_concat(&interp->mem_free_queue.head, queue);
+
+ // Process the merged queue now (see gh-130794).
+ _PyThreadStateImpl *this_tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ process_interp_queue(&interp->mem_free_queue, this_tstate->qsbr);
+
+ PyMutex_Unlock(&interp->mem_free_queue.mutex);
+
+ assert(llist_empty(queue)); // the thread's queue is now empty
+}
+
+void
+_PyMem_FiniDelayed(PyInterpreterState *interp)
+{
+ struct llist_node *head = &interp->mem_free_queue.head;
+ while (!llist_empty(head)) {
+ struct _mem_work_chunk *buf = work_queue_first(head);
+
+ while (buf->rd_idx < buf->wr_idx) {
+ // Free the remaining items immediately. There should be no other
+ // threads accessing the memory at this point during shutdown.
+ struct _mem_work_item *item = &buf->array[buf->rd_idx];
+ free_work_item(item->ptr);
+ buf->rd_idx++;
+ }
+
+ llist_remove(&buf->node);
+ PyMem_Free(buf);
+ }
+}
/**************************/
/* the "object" allocator */
@@ -852,6 +1451,13 @@ static int running_on_valgrind = -1;
typedef struct _obmalloc_state OMState;
+/* obmalloc state for main interpreter and shared by all interpreters without
+ * their own obmalloc state. By not explicitly initalizing this structure, it
+ * will be allocated in the BSS which is a small performance win. The radix
+ * tree arrays are fairly large but are sparsely used. */
+static struct _obmalloc_state obmalloc_state_main;
+static bool obmalloc_state_initialized;
+
static inline int
has_own_state(PyInterpreterState *interp)
{
@@ -864,10 +1470,8 @@ static inline OMState *
get_state(void)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
- if (!has_own_state(interp)) {
- interp = _PyInterpreterState_Main();
- }
- return &interp->obmalloc;
+ assert(interp->obmalloc != NULL); // otherwise not initialized or freed
+ return interp->obmalloc;
}
// These macros all rely on a local "state" variable.
@@ -882,9 +1486,51 @@ get_state(void)
#define narenas_highwater (state->mgmt.narenas_highwater)
#define raw_allocated_blocks (state->mgmt.raw_allocated_blocks)
+#ifdef WITH_MIMALLOC
+static bool count_blocks(
+ const mi_heap_t* heap, const mi_heap_area_t* area,
+ void* block, size_t block_size, void* allocated_blocks)
+{
+ *(size_t *)allocated_blocks += area->used;
+ return 1;
+}
+
+static Py_ssize_t
+get_mimalloc_allocated_blocks(PyInterpreterState *interp)
+{
+ size_t allocated_blocks = 0;
+#ifdef Py_GIL_DISABLED
+ for (PyThreadState *t = interp->threads.head; t != NULL; t = t->next) {
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)t;
+ for (int i = 0; i < _Py_MIMALLOC_HEAP_COUNT; i++) {
+ mi_heap_t *heap = &tstate->mimalloc.heaps[i];
+ mi_heap_visit_blocks(heap, false, &count_blocks, &allocated_blocks);
+ }
+ }
+
+ mi_abandoned_pool_t *pool = &interp->mimalloc.abandoned_pool;
+ for (uint8_t tag = 0; tag < _Py_MIMALLOC_HEAP_COUNT; tag++) {
+ _mi_abandoned_pool_visit_blocks(pool, tag, false, &count_blocks,
+ &allocated_blocks);
+ }
+#else
+ // TODO(sgross): this only counts the current thread's blocks.
+ mi_heap_t *heap = mi_heap_get_default();
+ mi_heap_visit_blocks(heap, false, &count_blocks, &allocated_blocks);
+#endif
+ return allocated_blocks;
+}
+#endif
+
Py_ssize_t
_PyInterpreterState_GetAllocatedBlocks(PyInterpreterState *interp)
{
+#ifdef WITH_MIMALLOC
+ if (_PyMem_MimallocEnabled()) {
+ return get_mimalloc_allocated_blocks(interp);
+ }
+#endif
+
#ifdef Py_DEBUG
assert(has_own_state(interp));
#else
@@ -893,7 +1539,11 @@ _PyInterpreterState_GetAllocatedBlocks(PyInterpreterState *interp)
"the interpreter doesn't have its own allocator");
}
#endif
- OMState *state = &interp->obmalloc;
+ OMState *state = interp->obmalloc;
+
+ if (state == NULL) {
+ return 0;
+ }
Py_ssize_t n = raw_allocated_blocks;
/* add up allocated blocks for used pools */
@@ -915,19 +1565,36 @@ _PyInterpreterState_GetAllocatedBlocks(PyInterpreterState *interp)
return n;
}
+static void free_obmalloc_arenas(PyInterpreterState *interp);
+
void
_PyInterpreterState_FinalizeAllocatedBlocks(PyInterpreterState *interp)
{
- if (has_own_state(interp)) {
+#ifdef WITH_MIMALLOC
+ if (_PyMem_MimallocEnabled()) {
+ return;
+ }
+#endif
+ if (has_own_state(interp) && interp->obmalloc != NULL) {
Py_ssize_t leaked = _PyInterpreterState_GetAllocatedBlocks(interp);
assert(has_own_state(interp) || leaked == 0);
interp->runtime->obmalloc.interpreter_leaks += leaked;
+ if (_PyMem_obmalloc_state_on_heap(interp) && leaked == 0) {
+ // free the obmalloc arenas and radix tree nodes. If leaked > 0
+ // then some of the memory allocated by obmalloc has not been
+ // freed. It might be safe to free the arenas in that case but
+ // it's possible that extension modules are still using that
+ // memory. So, it is safer to not free and to leak. Perhaps there
+ // should be warning when this happens. It should be possible to
+ // use a tool like "-fsanitize=address" to track down these leaks.
+ free_obmalloc_arenas(interp);
+ }
}
}
static Py_ssize_t get_num_global_allocated_blocks(_PyRuntimeState *);
-/* We preserve the number of blockss leaked during runtime finalization,
+/* We preserve the number of blocks leaked during runtime finalization,
so they can be reported if the runtime is initialized again. */
// XXX We don't lose any information by dropping this,
// so we should consider doing so.
@@ -961,6 +1628,7 @@ get_num_global_allocated_blocks(_PyRuntimeState *runtime)
}
}
else {
+ _PyEval_StopTheWorldAll(&_PyRuntime);
HEAD_LOCK(runtime);
PyInterpreterState *interp = PyInterpreterState_Head();
assert(interp != NULL);
@@ -980,6 +1648,7 @@ get_num_global_allocated_blocks(_PyRuntimeState *runtime)
}
}
HEAD_UNLOCK(runtime);
+ _PyEval_StartTheWorldAll(&_PyRuntime);
#ifdef Py_DEBUG
assert(got_main);
#endif
@@ -2034,6 +2703,33 @@ write_size_t(void *p, size_t n)
}
}
+static void
+fill_mem_debug(debug_alloc_api_t *api, void *data, int c, size_t nbytes,
+ bool is_alloc)
+{
+#ifdef Py_GIL_DISABLED
+ if (api->api_id == 'o') {
+ // Don't overwrite the first few bytes of a PyObject allocation in the
+ // free-threaded build
+ _PyThreadStateImpl *tstate = (_PyThreadStateImpl *)_PyThreadState_GET();
+ size_t debug_offset;
+ if (is_alloc) {
+ debug_offset = tstate->mimalloc.current_object_heap->debug_offset;
+ }
+ else {
+ char *alloc = (char *)data - 2*SST; // start of the allocation
+ debug_offset = _mi_ptr_page(alloc)->debug_offset;
+ }
+ debug_offset -= 2*SST; // account for pymalloc extra bytes
+ if (debug_offset < nbytes) {
+ memset((char *)data + debug_offset, c, nbytes - debug_offset);
+ }
+ return;
+ }
+#endif
+ memset(data, c, nbytes);
+}
+
/* Let S = sizeof(size_t). The debug malloc asks for 4 * S extra bytes and
fills them with useful stuff, here calling the underlying malloc's result p:
@@ -2110,7 +2806,7 @@ _PyMem_DebugRawAlloc(int use_calloc, void *ctx, size_t nbytes)
memset(p + SST + 1, PYMEM_FORBIDDENBYTE, SST-1);
if (nbytes > 0 && !use_calloc) {
- memset(data, PYMEM_CLEANBYTE, nbytes);
+ fill_mem_debug(api, data, PYMEM_CLEANBYTE, nbytes, true);
}
/* at tail, write pad (SST bytes) and serialno (SST bytes) */
@@ -2158,8 +2854,9 @@ _PyMem_DebugRawFree(void *ctx, void *p)
_PyMem_DebugCheckAddress(__func__, api->api_id, p);
nbytes = read_size_t(q);
- nbytes += PYMEM_DEBUG_EXTRA_BYTES;
- memset(q, PYMEM_DEADBYTE, nbytes);
+ nbytes += PYMEM_DEBUG_EXTRA_BYTES - 2*SST;
+ memset(q, PYMEM_DEADBYTE, 2*SST);
+ fill_mem_debug(api, p, PYMEM_DEADBYTE, nbytes, false);
api->alloc.free(api->alloc.ctx, q);
}
@@ -2179,7 +2876,6 @@ _PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes)
size_t total; /* 2 * SST + nbytes + 2 * SST */
size_t original_nbytes;
#define ERASED_SIZE 64
- uint8_t save[2*ERASED_SIZE]; /* A copy of erased bytes. */
_PyMem_DebugCheckAddress(__func__, api->api_id, p);
@@ -2196,9 +2892,11 @@ _PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes)
#ifdef PYMEM_DEBUG_SERIALNO
size_t block_serialno = read_size_t(tail + SST);
#endif
+#ifndef Py_GIL_DISABLED
/* Mark the header, the trailer, ERASED_SIZE bytes at the begin and
ERASED_SIZE bytes at the end as dead and save the copy of erased bytes.
*/
+ uint8_t save[2*ERASED_SIZE]; /* A copy of erased bytes. */
if (original_nbytes <= sizeof(save)) {
memcpy(save, data, original_nbytes);
memset(data - 2 * SST, PYMEM_DEADBYTE,
@@ -2211,6 +2909,7 @@ _PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes)
memset(tail - ERASED_SIZE, PYMEM_DEADBYTE,
ERASED_SIZE + PYMEM_DEBUG_EXTRA_BYTES - 2 * SST);
}
+#endif
/* Resize and add decorations. */
r = (uint8_t *)api->alloc.realloc(api->alloc.ctx, head, total);
@@ -2238,6 +2937,7 @@ _PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes)
write_size_t(tail + SST, block_serialno);
#endif
+#ifndef Py_GIL_DISABLED
/* Restore saved bytes. */
if (original_nbytes <= sizeof(save)) {
memcpy(data, save, Py_MIN(nbytes, original_nbytes));
@@ -2250,6 +2950,7 @@ _PyMem_DebugRawRealloc(void *ctx, void *p, size_t nbytes)
Py_MIN(nbytes - i, ERASED_SIZE));
}
}
+#endif
if (r == NULL) {
return NULL;
@@ -2511,9 +3212,96 @@ _PyDebugAllocatorStats(FILE *out,
(void)printone(out, buf2, num_blocks * sizeof_block);
}
+// Return true if the obmalloc state structure is heap allocated,
+// by PyMem_RawCalloc(). For the main interpreter, this structure
+// allocated in the BSS. Allocating that way gives some memory savings
+// and a small performance win (at least on a demand paged OS). On
+// 64-bit platforms, the obmalloc structure is 256 kB. Most of that
+// memory is for the arena_map_top array. Since normally only one entry
+// of that array is used, only one page of resident memory is actually
+// used, rather than the full 256 kB.
+bool _PyMem_obmalloc_state_on_heap(PyInterpreterState *interp)
+{
+#if WITH_PYMALLOC
+ return interp->obmalloc && interp->obmalloc != &obmalloc_state_main;
+#else
+ return false;
+#endif
+}
+
+#ifdef WITH_PYMALLOC
+static void
+init_obmalloc_pools(PyInterpreterState *interp)
+{
+ // initialize the obmalloc->pools structure. This must be done
+ // before the obmalloc alloc/free functions can be called.
+ poolp temp[OBMALLOC_USED_POOLS_SIZE] =
+ _obmalloc_pools_INIT(interp->obmalloc->pools);
+ memcpy(&interp->obmalloc->pools.used, temp, sizeof(temp));
+}
+#endif /* WITH_PYMALLOC */
+
+int _PyMem_init_obmalloc(PyInterpreterState *interp)
+{
+#ifdef WITH_PYMALLOC
+ /* Initialize obmalloc, but only for subinterpreters,
+ since the main interpreter is initialized statically. */
+ if (_Py_IsMainInterpreter(interp)
+ || _PyInterpreterState_HasFeature(interp,
+ Py_RTFLAGS_USE_MAIN_OBMALLOC)) {
+ interp->obmalloc = &obmalloc_state_main;
+ if (!obmalloc_state_initialized) {
+ init_obmalloc_pools(interp);
+ obmalloc_state_initialized = true;
+ }
+ } else {
+ interp->obmalloc = PyMem_RawCalloc(1, sizeof(struct _obmalloc_state));
+ if (interp->obmalloc == NULL) {
+ return -1;
+ }
+ init_obmalloc_pools(interp);
+ }
+#endif /* WITH_PYMALLOC */
+ return 0; // success
+}
+
#ifdef WITH_PYMALLOC
+static void
+free_obmalloc_arenas(PyInterpreterState *interp)
+{
+ OMState *state = interp->obmalloc;
+ for (uint i = 0; i < maxarenas; ++i) {
+ // free each obmalloc memory arena
+ struct arena_object *ao = &allarenas[i];
+ _PyObject_Arena.free(_PyObject_Arena.ctx,
+ (void *)ao->address, ARENA_SIZE);
+ }
+ // free the array containing pointers to all arenas
+ PyMem_RawFree(allarenas);
+#if WITH_PYMALLOC_RADIX_TREE
+#ifdef USE_INTERIOR_NODES
+ // Free the middle and bottom nodes of the radix tree. These are allocated
+ // by arena_map_mark_used() but not freed when arenas are freed.
+ for (int i1 = 0; i1 < MAP_TOP_LENGTH; i1++) {
+ arena_map_mid_t *mid = arena_map_root.ptrs[i1];
+ if (mid == NULL) {
+ continue;
+ }
+ for (int i2 = 0; i2 < MAP_MID_LENGTH; i2++) {
+ arena_map_bot_t *bot = arena_map_root.ptrs[i1]->ptrs[i2];
+ if (bot == NULL) {
+ continue;
+ }
+ PyMem_RawFree(bot);
+ }
+ PyMem_RawFree(mid);
+ }
+#endif
+#endif
+}
+
#ifdef Py_DEBUG
/* Is target in the list? The list is traversed via the nextpool pointers.
* The list may be NULL-terminated, or circular. Return 1 if target is in
@@ -2535,19 +3323,55 @@ pool_is_in_list(const poolp target, poolp list)
}
#endif
-/* Print summary info to "out" about the state of pymalloc's structures.
- * In Py_DEBUG mode, also perform some expensive internal consistency
- * checks.
- *
- * Return 0 if the memory debug hooks are not installed or no statistics was
- * written into out, return 1 otherwise.
- */
-int
-_PyObject_DebugMallocStats(FILE *out)
+#ifdef WITH_MIMALLOC
+struct _alloc_stats {
+ size_t allocated_blocks;
+ size_t allocated_bytes;
+ size_t allocated_with_overhead;
+ size_t bytes_reserved;
+ size_t bytes_committed;
+};
+
+static bool _collect_alloc_stats(
+ const mi_heap_t* heap, const mi_heap_area_t* area,
+ void* block, size_t block_size, void* arg)
+{
+ struct _alloc_stats *stats = (struct _alloc_stats *)arg;
+ stats->allocated_blocks += area->used;
+ stats->allocated_bytes += area->used * area->block_size;
+ stats->allocated_with_overhead += area->used * area->full_block_size;
+ stats->bytes_reserved += area->reserved;
+ stats->bytes_committed += area->committed;
+ return 1;
+}
+
+static void
+py_mimalloc_print_stats(FILE *out)
+{
+ fprintf(out, "Small block threshold = %zd, in %u size classes.\n",
+ MI_SMALL_OBJ_SIZE_MAX, MI_BIN_HUGE);
+ fprintf(out, "Medium block threshold = %zd\n",
+ MI_MEDIUM_OBJ_SIZE_MAX);
+ fprintf(out, "Large object max size = %zd\n",
+ MI_LARGE_OBJ_SIZE_MAX);
+
+ mi_heap_t *heap = mi_heap_get_default();
+ struct _alloc_stats stats;
+ memset(&stats, 0, sizeof(stats));
+ mi_heap_visit_blocks(heap, false, &_collect_alloc_stats, &stats);
+
+ fprintf(out, " Allocated Blocks: %zd\n", stats.allocated_blocks);
+ fprintf(out, " Allocated Bytes: %zd\n", stats.allocated_bytes);
+ fprintf(out, " Allocated Bytes w/ Overhead: %zd\n", stats.allocated_with_overhead);
+ fprintf(out, " Bytes Reserved: %zd\n", stats.bytes_reserved);
+ fprintf(out, " Bytes Committed: %zd\n", stats.bytes_committed);
+}
+#endif
+
+
+static void
+pymalloc_print_stats(FILE *out)
{
- if (!_PyMem_PymallocEnabled()) {
- return 0;
- }
OMState *state = get_state();
uint i;
@@ -2700,7 +3524,32 @@ _PyObject_DebugMallocStats(FILE *out)
#endif
#endif
- return 1;
+}
+
+/* Print summary info to "out" about the state of pymalloc's structures.
+ * In Py_DEBUG mode, also perform some expensive internal consistency
+ * checks.
+ *
+ * Return 0 if the memory debug hooks are not installed or no statistics was
+ * written into out, return 1 otherwise.
+ */
+int
+_PyObject_DebugMallocStats(FILE *out)
+{
+#ifdef WITH_MIMALLOC
+ if (_PyMem_MimallocEnabled()) {
+ py_mimalloc_print_stats(out);
+ return 1;
+ }
+ else
+#endif
+ if (_PyMem_PymallocEnabled()) {
+ pymalloc_print_stats(out);
+ return 1;
+ }
+ else {
+ return 0;
+ }
}
#endif /* #ifdef WITH_PYMALLOC */
diff --git a/contrib/tools/python3/Objects/odictobject.c b/contrib/tools/python3/Objects/odictobject.c
index cf364c13b91..ca5f7c8d3a8 100644
--- a/contrib/tools/python3/Objects/odictobject.c
+++ b/contrib/tools/python3/Objects/odictobject.c
@@ -465,10 +465,14 @@ later:
*/
#include "Python.h"
-#include "pycore_call.h" // _PyObject_CallNoArgs()
-#include "pycore_object.h" // _PyObject_GC_UNTRACK()
-#include "pycore_dict.h" // _Py_dict_lookup()
-#include <stddef.h> // offsetof()
+#include "pycore_call.h" // _PyObject_CallNoArgs()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
+#include "pycore_critical_section.h" //_Py_BEGIN_CRITICAL_SECTION
+#include "pycore_dict.h" // _Py_dict_lookup()
+#include "pycore_object.h" // _PyObject_GC_UNTRACK()
+#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
+#include <stddef.h> // offsetof()
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#include "clinic/odictobject.c.h"
@@ -532,8 +536,12 @@ _odict_get_index_raw(PyODictObject *od, PyObject *key, Py_hash_t hash)
PyObject *value = NULL;
PyDictKeysObject *keys = ((PyDictObject *)od)->ma_keys;
Py_ssize_t ix;
-
+#ifdef Py_GIL_DISABLED
+ ix = _Py_dict_lookup_threadsafe((PyDictObject *)od, key, hash, &value);
+ Py_XDECREF(value);
+#else
ix = _Py_dict_lookup((PyDictObject *)od, key, hash, &value);
+#endif
if (ix == DKIX_EMPTY) {
return keys->dk_nentries; /* index of new entry */
}
@@ -1054,6 +1062,8 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj,
{
PyObject *value = NULL;
+ Py_BEGIN_CRITICAL_SECTION(od);
+
_ODictNode *node = _odict_find_node_hash((PyODictObject *)od, key, hash);
if (node != NULL) {
/* Pop the node first to avoid a possible dict resize (due to
@@ -1061,10 +1071,13 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj,
resolution. */
int res = _odict_clear_node((PyODictObject *)od, node, key, hash);
if (res < 0) {
- return NULL;
+ goto done;
}
/* Now delete the value from the dict. */
- value = _PyDict_Pop_KnownHash(od, key, hash, failobj);
+ if (_PyDict_Pop_KnownHash((PyDictObject *)od, key, hash,
+ &value) == 0) {
+ value = Py_NewRef(failobj);
+ }
}
else if (value == NULL && !PyErr_Occurred()) {
/* Apply the fallback value, if necessary. */
@@ -1075,6 +1088,8 @@ _odict_popkey_hash(PyObject *od, PyObject *key, PyObject *failobj,
PyErr_SetObject(PyExc_KeyError, key);
}
}
+ Py_END_CRITICAL_SECTION();
+done:
return value;
}
@@ -1133,8 +1148,10 @@ OrderedDict_popitem_impl(PyODictObject *self, int last)
node = last ? _odict_LAST(self) : _odict_FIRST(self);
key = Py_NewRef(_odictnode_KEY(node));
value = _odict_popkey_hash((PyObject *)self, key, NULL, _odictnode_HASH(node));
- if (value == NULL)
+ if (value == NULL) {
+ Py_DECREF(key);
return NULL;
+ }
item = PyTuple_Pack(2, key, value);
Py_DECREF(key);
Py_DECREF(value);
@@ -1369,8 +1386,7 @@ odict_dealloc(PyODictObject *self)
Py_TRASHCAN_BEGIN(self, odict_dealloc)
Py_XDECREF(self->od_inst_dict);
- if (self->od_weakreflist != NULL)
- PyObject_ClearWeakRefs((PyObject *)self);
+ FT_CLEAR_WEAKREFS((PyObject*)self, self->od_weakreflist);
_odict_clear_nodes(self);
PyDict_Type.tp_dealloc((PyObject *)self);
@@ -1494,7 +1510,7 @@ odict_init(PyObject *self, PyObject *args, PyObject *kwds)
if (len == -1)
return -1;
if (len > 1) {
- const char *msg = "expected at most 1 arguments, got %zd";
+ const char *msg = "expected at most 1 argument, got %zd";
PyErr_Format(PyExc_TypeError, msg, len);
return -1;
}
@@ -2171,7 +2187,7 @@ mutablemapping_update_arg(PyObject *self, PyObject *arg)
return res;
}
PyObject *func;
- if (_PyObject_LookupAttr(arg, &_Py_ID(keys), &func) < 0) {
+ if (PyObject_GetOptionalAttr(arg, &_Py_ID(keys), &func) < 0) {
return -1;
}
if (func != NULL) {
@@ -2203,7 +2219,7 @@ mutablemapping_update_arg(PyObject *self, PyObject *arg)
}
return 0;
}
- if (_PyObject_LookupAttr(arg, &_Py_ID(items), &func) < 0) {
+ if (PyObject_GetOptionalAttr(arg, &_Py_ID(items), &func) < 0) {
return -1;
}
if (func != NULL) {
diff --git a/contrib/tools/python3/Objects/picklebufobject.c b/contrib/tools/python3/Objects/picklebufobject.c
index aaa852cfbb0..0de5245585e 100644
--- a/contrib/tools/python3/Objects/picklebufobject.c
+++ b/contrib/tools/python3/Objects/picklebufobject.c
@@ -1,7 +1,7 @@
/* PickleBuffer object implementation */
-#define PY_SSIZE_T_CLEAN
#include "Python.h"
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
#include <stddef.h>
typedef struct {
@@ -109,8 +109,7 @@ static void
picklebuf_dealloc(PyPickleBufferObject *self)
{
PyObject_GC_UnTrack(self);
- if (self->weakreflist != NULL)
- PyObject_ClearWeakRefs((PyObject *) self);
+ FT_CLEAR_WEAKREFS((PyObject*)self, self->weakreflist);
PyBuffer_Release(&self->view);
Py_TYPE(self)->tp_free((PyObject *) self);
}
diff --git a/contrib/tools/python3/Objects/rangeobject.c b/contrib/tools/python3/Objects/rangeobject.c
index beb86b9623b..57e1e008320 100644
--- a/contrib/tools/python3/Objects/rangeobject.c
+++ b/contrib/tools/python3/Objects/rangeobject.c
@@ -2,10 +2,12 @@
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
-#include "pycore_range.h"
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_long.h" // _PyLong_GetZero()
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
+#include "pycore_range.h"
#include "pycore_tuple.h" // _PyTuple_ITEMS()
-#include "structmember.h" // PyMemberDef
+
/* Support objects whose length is > PY_SSIZE_T_MAX.
@@ -105,8 +107,8 @@ range_from_array(PyTypeObject *type, PyObject *const *args, Py_ssize_t num_args)
if (!stop) {
return NULL;
}
- start = Py_NewRef(_PyLong_GetZero());
- step = Py_NewRef(_PyLong_GetOne());
+ start = _PyLong_GetZero();
+ step = _PyLong_GetOne();
break;
case 0:
PyErr_SetString(PyExc_TypeError,
@@ -749,16 +751,16 @@ PyDoc_STRVAR(index_doc,
static PyMethodDef range_methods[] = {
{"__reversed__", range_reverse, METH_NOARGS, reverse_doc},
- {"__reduce__", (PyCFunction)range_reduce, METH_VARARGS},
+ {"__reduce__", (PyCFunction)range_reduce, METH_NOARGS},
{"count", (PyCFunction)range_count, METH_O, count_doc},
{"index", (PyCFunction)range_index, METH_O, index_doc},
{NULL, NULL} /* sentinel */
};
static PyMemberDef range_members[] = {
- {"start", T_OBJECT_EX, offsetof(rangeobject, start), READONLY},
- {"stop", T_OBJECT_EX, offsetof(rangeobject, stop), READONLY},
- {"step", T_OBJECT_EX, offsetof(rangeobject, step), READONLY},
+ {"start", Py_T_OBJECT_EX, offsetof(rangeobject, start), Py_READONLY},
+ {"stop", Py_T_OBJECT_EX, offsetof(rangeobject, stop), Py_READONLY},
+ {"step", Py_T_OBJECT_EX, offsetof(rangeobject, step), Py_READONLY},
{0}
};
@@ -1012,6 +1014,11 @@ longrangeiter_reduce(longrangeiterobject *r, PyObject *Py_UNUSED(ignored))
static PyObject *
longrangeiter_setstate(longrangeiterobject *r, PyObject *state)
{
+ if (!PyLong_CheckExact(state)) {
+ PyErr_Format(PyExc_TypeError, "state must be an int, not %T", state);
+ return NULL;
+ }
+
PyObject *zero = _PyLong_GetZero(); // borrowed reference
int cmp;
diff --git a/contrib/tools/python3/Objects/setobject.c b/contrib/tools/python3/Objects/setobject.c
index 763f9a3d204..891987e3519 100644
--- a/contrib/tools/python3/Objects/setobject.c
+++ b/contrib/tools/python3/Objects/setobject.c
@@ -2,7 +2,7 @@
/* set object implementation
Written and maintained by Raymond D. Hettinger <[email protected]>
- Derived from Lib/sets.py and Objects/dictobject.c.
+ Derived from Objects/dictobject.c.
The basic lookup function used by all operations.
This is based on Algorithm D from Knuth Vol. 3, Sec. 6.4.
@@ -32,8 +32,29 @@
*/
#include "Python.h"
-#include "pycore_object.h" // _PyObject_GC_UNTRACK()
-#include <stddef.h> // offsetof()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
+#include "pycore_critical_section.h" // Py_BEGIN_CRITICAL_SECTION, Py_END_CRITICAL_SECTION
+#include "pycore_dict.h" // _PyDict_Contains_KnownHash()
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
+#include "pycore_object.h" // _PyObject_GC_UNTRACK()
+#include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_RELAXED()
+#include "pycore_pyerrors.h" // _PyErr_SetKeyError()
+#include "pycore_setobject.h" // _PySet_NextEntry() definition
+#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
+#include <stddef.h> // offsetof()
+#include "clinic/setobject.c.h"
+
+/*[clinic input]
+class set "PySetObject *" "&PySet_Type"
+class frozenset "PySetObject *" "&PyFrozenSet_Type"
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=97ad1d3e9f117079]*/
+
+/*[python input]
+class setobject_converter(self_converter):
+ type = "PySetObject *"
+[python start generated code]*/
+/*[python end generated code: output=da39a3ee5e6b4b0d input=33a44506d4d57793]*/
/* Object used as dummy key to fill deleted entries */
static PyObject _dummy_struct;
@@ -111,6 +132,8 @@ set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
int probes;
int cmp;
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+
/* Pre-increment is necessary to prevent arbitrary code in the rich
comparison from deallocating the key just before the insertion. */
Py_INCREF(key);
@@ -162,14 +185,17 @@ set_add_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
found_unused_or_dummy:
if (freeslot == NULL)
goto found_unused;
- so->used++;
+ if (freeslot->hash != -1) {
+ goto restart;
+ }
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
freeslot->key = key;
freeslot->hash = hash;
return 0;
found_unused:
so->fill++;
- so->used++;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used + 1);
entry->key = key;
entry->hash = hash;
if ((size_t)so->fill*5 < mask*3)
@@ -335,7 +361,7 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
old_key = entry->key;
entry->key = dummy;
entry->hash = -1;
- so->used--;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1);
Py_DECREF(old_key);
return DISCARD_FOUND;
}
@@ -343,13 +369,9 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash)
static int
set_add_key(PySetObject *so, PyObject *key)
{
- Py_hash_t hash;
-
- if (!PyUnicode_CheckExact(key) ||
- (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return -1;
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return -1;
}
return set_add_entry(so, key, hash);
}
@@ -357,13 +379,9 @@ set_add_key(PySetObject *so, PyObject *key)
static int
set_contains_key(PySetObject *so, PyObject *key)
{
- Py_hash_t hash;
-
- if (!PyUnicode_CheckExact(key) ||
- (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return -1;
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return -1;
}
return set_contains_entry(so, key, hash);
}
@@ -371,13 +389,9 @@ set_contains_key(PySetObject *so, PyObject *key)
static int
set_discard_key(PySetObject *so, PyObject *key)
{
- Py_hash_t hash;
-
- if (!PyUnicode_CheckExact(key) ||
- (hash = _PyASCIIObject_CAST(key)->hash) == -1) {
- hash = PyObject_Hash(key);
- if (hash == -1)
- return -1;
+ Py_hash_t hash = _PyObject_HashFast(key);
+ if (hash == -1) {
+ return -1;
}
return set_discard_entry(so, key, hash);
}
@@ -387,10 +401,10 @@ set_empty_to_minsize(PySetObject *so)
{
memset(so->smalltable, 0, sizeof(so->smalltable));
so->fill = 0;
- so->used = 0;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, 0);
so->mask = PySet_MINSIZE - 1;
so->table = so->smalltable;
- so->hash = -1;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->hash, -1);
}
static int
@@ -488,8 +502,7 @@ set_dealloc(PySetObject *so)
/* bpo-31095: UnTrack is needed before calling any callbacks */
PyObject_GC_UnTrack(so);
Py_TRASHCAN_BEGIN(so, set_dealloc)
- if (so->weakreflist != NULL)
- PyObject_ClearWeakRefs((PyObject *) so);
+ FT_CLEAR_WEAKREFS((PyObject *) so, so->weakreflist);
for (entry = so->table; used > 0; entry++) {
if (entry->key && entry->key != dummy) {
@@ -504,7 +517,7 @@ set_dealloc(PySetObject *so)
}
static PyObject *
-set_repr(PySetObject *so)
+set_repr_lock_held(PySetObject *so)
{
PyObject *result=NULL, *keys, *listrepr, *tmp;
int status = Py_ReprEnter((PyObject*)so);
@@ -521,9 +534,18 @@ set_repr(PySetObject *so)
return PyUnicode_FromFormat("%s()", Py_TYPE(so)->tp_name);
}
- keys = PySequence_List((PyObject *)so);
- if (keys == NULL)
+ // gh-129967: avoid PySequence_List because it might re-lock the object
+ // lock or the GIL and allow something to clear the set from underneath us.
+ keys = PyList_New(so->used);
+ if (keys == NULL) {
goto done;
+ }
+
+ Py_ssize_t pos = 0, idx = 0;
+ setentry *entry;
+ while (set_next(so, &pos, &entry)) {
+ PyList_SET_ITEM(keys, idx++, Py_NewRef(entry->key));
+ }
/* repr(keys)[1:-1] */
listrepr = PyObject_Repr(keys);
@@ -548,14 +570,24 @@ done:
return result;
}
+static PyObject *
+set_repr(PySetObject *so)
+{
+ PyObject *result;
+ Py_BEGIN_CRITICAL_SECTION(so);
+ result = set_repr_lock_held(so);
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
static Py_ssize_t
-set_len(PyObject *so)
+set_len(PySetObject *so)
{
- return ((PySetObject *)so)->used;
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(so->used);
}
static int
-set_merge(PySetObject *so, PyObject *otherset)
+set_merge_lock_held(PySetObject *so, PyObject *otherset)
{
PySetObject *other;
PyObject *key;
@@ -565,6 +597,8 @@ set_merge(PySetObject *so, PyObject *otherset)
assert (PyAnySet_Check(so));
assert (PyAnySet_Check(otherset));
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(otherset);
other = (PySetObject*)otherset;
if (other == so || other->used == 0)
@@ -593,7 +627,7 @@ set_merge(PySetObject *so, PyObject *otherset)
}
}
so->fill = other->fill;
- so->used = other->used;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, other->used);
return 0;
}
@@ -602,7 +636,7 @@ set_merge(PySetObject *so, PyObject *otherset)
setentry *newtable = so->table;
size_t newmask = (size_t)so->mask;
so->fill = other->used;
- so->used = other->used;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, other->used);
for (i = other->mask + 1; i > 0 ; i--, other_entry++) {
key = other_entry->key;
if (key != NULL && key != dummy) {
@@ -625,8 +659,19 @@ set_merge(PySetObject *so, PyObject *otherset)
return 0;
}
+/*[clinic input]
+@critical_section
+set.pop
+ so: setobject
+
+Remove and return an arbitrary set element.
+
+Raises KeyError if the set is empty.
+[clinic start generated code]*/
+
static PyObject *
-set_pop(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set_pop_impl(PySetObject *so)
+/*[clinic end generated code: output=4d65180f1271871b input=9296c84921125060]*/
{
/* Make sure the search finger is in bounds */
setentry *entry = so->table + (so->finger & so->mask);
@@ -645,14 +690,11 @@ set_pop(PySetObject *so, PyObject *Py_UNUSED(ignored))
key = entry->key;
entry->key = dummy;
entry->hash = -1;
- so->used--;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->used, so->used - 1);
so->finger = entry - so->table + 1; /* next place to start */
return key;
}
-PyDoc_STRVAR(pop_doc, "Remove and return an arbitrary set element.\n\
-Raises KeyError if the set is empty.");
-
static int
set_traverse(PySetObject *so, visitproc visit, void *arg)
{
@@ -679,18 +721,15 @@ _shuffle_bits(Py_uhash_t h)
large primes with "interesting bit patterns" and that passed tests
for good collision statistics on a variety of problematic datasets
including powersets and graph structures (such as David Eppstein's
- graph recipes in Lib/test/test_set.py) */
+ graph recipes in Lib/test/test_set.py). */
static Py_hash_t
-frozenset_hash(PyObject *self)
+frozenset_hash_impl(PyObject *self)
{
- PySetObject *so = (PySetObject *)self;
+ PySetObject *so = _PySet_CAST(self);
Py_uhash_t hash = 0;
setentry *entry;
- if (so->hash != -1)
- return so->hash;
-
/* Xor-in shuffled bits from every entry's hash field because xor is
commutative and a frozenset hash should be independent of order.
@@ -723,7 +762,21 @@ frozenset_hash(PyObject *self)
if (hash == (Py_uhash_t)-1)
hash = 590923713UL;
- so->hash = hash;
+ return (Py_hash_t)hash;
+}
+
+static Py_hash_t
+frozenset_hash(PyObject *self)
+{
+ PySetObject *so = _PySet_CAST(self);
+ Py_uhash_t hash;
+
+ if (FT_ATOMIC_LOAD_SSIZE_RELAXED(so->hash) != -1) {
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(so->hash);
+ }
+
+ hash = frozenset_hash_impl(self);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(so->hash, hash);
return hash;
}
@@ -792,7 +845,7 @@ static PyMethodDef setiter_methods[] = {
static PyObject *setiter_iternext(setiterobject *si)
{
- PyObject *key;
+ PyObject *key = NULL;
Py_ssize_t i, mask;
setentry *entry;
PySetObject *so = si->si_set;
@@ -801,30 +854,35 @@ static PyObject *setiter_iternext(setiterobject *si)
return NULL;
assert (PyAnySet_Check(so));
- if (si->si_used != so->used) {
+ Py_ssize_t so_used = FT_ATOMIC_LOAD_SSIZE(so->used);
+ Py_ssize_t si_used = FT_ATOMIC_LOAD_SSIZE(si->si_used);
+ if (si_used != so_used) {
PyErr_SetString(PyExc_RuntimeError,
"Set changed size during iteration");
si->si_used = -1; /* Make this state sticky */
return NULL;
}
+ Py_BEGIN_CRITICAL_SECTION(so);
i = si->si_pos;
assert(i>=0);
entry = so->table;
mask = so->mask;
- while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy))
+ while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy)) {
i++;
+ }
+ if (i <= mask) {
+ key = Py_NewRef(entry[i].key);
+ }
+ Py_END_CRITICAL_SECTION();
si->si_pos = i+1;
- if (i > mask)
- goto fail;
+ if (key == NULL) {
+ si->si_set = NULL;
+ Py_DECREF(so);
+ return NULL;
+ }
si->len--;
- key = entry[i].key;
- return Py_NewRef(key);
-
-fail:
- si->si_set = NULL;
- Py_DECREF(so);
- return NULL;
+ return key;
}
PyTypeObject PySetIter_Type = {
@@ -863,52 +921,60 @@ PyTypeObject PySetIter_Type = {
static PyObject *
set_iter(PySetObject *so)
{
+ Py_ssize_t size = set_len(so);
setiterobject *si = PyObject_GC_New(setiterobject, &PySetIter_Type);
if (si == NULL)
return NULL;
si->si_set = (PySetObject*)Py_NewRef(so);
- si->si_used = so->used;
+ si->si_used = size;
si->si_pos = 0;
- si->len = so->used;
+ si->len = size;
_PyObject_GC_TRACK(si);
return (PyObject *)si;
}
static int
-set_update_internal(PySetObject *so, PyObject *other)
+set_update_dict_lock_held(PySetObject *so, PyObject *other)
{
- PyObject *key, *it;
+ assert(PyDict_CheckExact(other));
- if (PyAnySet_Check(other))
- return set_merge(so, other);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
- if (PyDict_CheckExact(other)) {
- PyObject *value;
- Py_ssize_t pos = 0;
- Py_hash_t hash;
- Py_ssize_t dictsize = PyDict_GET_SIZE(other);
-
- /* Do one big resize at the start, rather than
- * incrementally resizing as we insert new keys. Expect
- * that there will be no (or few) overlapping keys.
- */
- if (dictsize < 0)
+ /* Do one big resize at the start, rather than
+ * incrementally resizing as we insert new keys. Expect
+ * that there will be no (or few) overlapping keys.
+ */
+ Py_ssize_t dictsize = PyDict_GET_SIZE(other);
+ if ((so->fill + dictsize)*5 >= so->mask*3) {
+ if (set_table_resize(so, (so->used + dictsize)*2) != 0) {
return -1;
- if ((so->fill + dictsize)*5 >= so->mask*3) {
- if (set_table_resize(so, (so->used + dictsize)*2) != 0)
- return -1;
}
- while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
- if (set_add_entry(so, key, hash))
- return -1;
+ }
+
+ Py_ssize_t pos = 0;
+ PyObject *key;
+ PyObject *value;
+ Py_hash_t hash;
+ while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
+ if (set_add_entry(so, key, hash)) {
+ return -1;
}
- return 0;
}
+ return 0;
+}
- it = PyObject_GetIter(other);
- if (it == NULL)
+static int
+set_update_iterable_lock_held(PySetObject *so, PyObject *other)
+{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+
+ PyObject *it = PyObject_GetIter(other);
+ if (it == NULL) {
return -1;
+ }
+ PyObject *key;
while ((key = PyIter_Next(it)) != NULL) {
if (set_add_key(so, key)) {
Py_DECREF(it);
@@ -923,8 +989,80 @@ set_update_internal(PySetObject *so, PyObject *other)
return 0;
}
+static int
+set_update_lock_held(PySetObject *so, PyObject *other)
+{
+ if (PyAnySet_Check(other)) {
+ return set_merge_lock_held(so, other);
+ }
+ else if (PyDict_CheckExact(other)) {
+ return set_update_dict_lock_held(so, other);
+ }
+ return set_update_iterable_lock_held(so, other);
+}
+
+// set_update for a `so` that is only visible to the current thread
+static int
+set_update_local(PySetObject *so, PyObject *other)
+{
+ assert(Py_REFCNT(so) == 1);
+ if (PyAnySet_Check(other)) {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(other);
+ rv = set_merge_lock_held(so, other);
+ Py_END_CRITICAL_SECTION();
+ return rv;
+ }
+ else if (PyDict_CheckExact(other)) {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(other);
+ rv = set_update_dict_lock_held(so, other);
+ Py_END_CRITICAL_SECTION();
+ return rv;
+ }
+ return set_update_iterable_lock_held(so, other);
+}
+
+static int
+set_update_internal(PySetObject *so, PyObject *other)
+{
+ if (PyAnySet_Check(other)) {
+ if (Py_Is((PyObject *)so, other)) {
+ return 0;
+ }
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_merge_lock_held(so, other);
+ Py_END_CRITICAL_SECTION2();
+ return rv;
+ }
+ else if (PyDict_CheckExact(other)) {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_update_dict_lock_held(so, other);
+ Py_END_CRITICAL_SECTION2();
+ return rv;
+ }
+ else {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(so);
+ rv = set_update_iterable_lock_held(so, other);
+ Py_END_CRITICAL_SECTION();
+ return rv;
+ }
+}
+
+/*[clinic input]
+set.update
+ so: setobject
+ *others as args: object
+
+Update the set, adding elements from all others.
+[clinic start generated code]*/
+
static PyObject *
-set_update(PySetObject *so, PyObject *args)
+set_update_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=34f6371704974c8a input=df4fe486e38cd337]*/
{
Py_ssize_t i;
@@ -936,9 +1074,6 @@ set_update(PySetObject *so, PyObject *args)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(update_doc,
-"Update a set with the union of itself and others.");
-
/* XXX Todo:
If aligned memory allocations become available, make the
set object 64 byte aligned so that most of the fields
@@ -964,7 +1099,7 @@ make_new_set(PyTypeObject *type, PyObject *iterable)
so->weakreflist = NULL;
if (iterable != NULL) {
- if (set_update_internal(so, iterable)) {
+ if (set_update_local(so, iterable)) {
Py_DECREF(so);
return NULL;
}
@@ -1060,7 +1195,9 @@ set_swap_bodies(PySetObject *a, PySetObject *b)
Py_hash_t h;
t = a->fill; a->fill = b->fill; b->fill = t;
- t = a->used; a->used = b->used; b->used = t;
+ t = a->used;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(a->used, b->used);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(b->used, t);
t = a->mask; a->mask = b->mask; b->mask = t;
u = a->table;
@@ -1079,41 +1216,84 @@ set_swap_bodies(PySetObject *a, PySetObject *b)
if (PyType_IsSubtype(Py_TYPE(a), &PyFrozenSet_Type) &&
PyType_IsSubtype(Py_TYPE(b), &PyFrozenSet_Type)) {
- h = a->hash; a->hash = b->hash; b->hash = h;
+ h = FT_ATOMIC_LOAD_SSIZE_RELAXED(a->hash);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(a->hash, FT_ATOMIC_LOAD_SSIZE_RELAXED(b->hash));
+ FT_ATOMIC_STORE_SSIZE_RELAXED(b->hash, h);
} else {
- a->hash = -1;
- b->hash = -1;
+ FT_ATOMIC_STORE_SSIZE_RELAXED(a->hash, -1);
+ FT_ATOMIC_STORE_SSIZE_RELAXED(b->hash, -1);
}
}
+/*[clinic input]
+@critical_section
+set.copy
+ so: setobject
+
+Return a shallow copy of a set.
+[clinic start generated code]*/
+
static PyObject *
-set_copy(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set_copy_impl(PySetObject *so)
+/*[clinic end generated code: output=c9223a1e1cc6b041 input=c169a4fbb8209257]*/
{
- return make_new_set_basetype(Py_TYPE(so), (PyObject *)so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ PyObject *copy = make_new_set_basetype(Py_TYPE(so), NULL);
+ if (copy == NULL) {
+ return NULL;
+ }
+ if (set_merge_lock_held((PySetObject *)copy, (PyObject *)so) < 0) {
+ Py_DECREF(copy);
+ return NULL;
+ }
+ return copy;
}
+/*[clinic input]
+@critical_section
+frozenset.copy
+ so: setobject
+
+Return a shallow copy of a set.
+[clinic start generated code]*/
+
static PyObject *
-frozenset_copy(PySetObject *so, PyObject *Py_UNUSED(ignored))
+frozenset_copy_impl(PySetObject *so)
+/*[clinic end generated code: output=b356263526af9e70 input=fbf5bef131268dd7]*/
{
if (PyFrozenSet_CheckExact(so)) {
return Py_NewRef(so);
}
- return set_copy(so, NULL);
+ return set_copy_impl(so);
}
-PyDoc_STRVAR(copy_doc, "Return a shallow copy of a set.");
+/*[clinic input]
+@critical_section
+set.clear
+ so: setobject
+
+Remove all elements from this set.
+[clinic start generated code]*/
static PyObject *
-set_clear(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set_clear_impl(PySetObject *so)
+/*[clinic end generated code: output=4e71d5a83904161a input=c6f831b366111950]*/
{
set_clear_internal(so);
Py_RETURN_NONE;
}
-PyDoc_STRVAR(clear_doc, "Remove all elements from this set.");
+/*[clinic input]
+set.union
+ so: setobject
+ *others as args: object
+
+Return a new set with elements from the set and all others.
+[clinic start generated code]*/
static PyObject *
-set_union(PySetObject *so, PyObject *args)
+set_union_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=2c83d05a446a1477 input=ddf088706e9577b2]*/
{
PySetObject *result;
PyObject *other;
@@ -1127,7 +1307,7 @@ set_union(PySetObject *so, PyObject *args)
other = PyTuple_GET_ITEM(args, i);
if ((PyObject *)so == other)
continue;
- if (set_update_internal(result, other)) {
+ if (set_update_local(result, other)) {
Py_DECREF(result);
return NULL;
}
@@ -1135,11 +1315,6 @@ set_union(PySetObject *so, PyObject *args)
return (PyObject *)result;
}
-PyDoc_STRVAR(union_doc,
- "Return the union of sets as a new set.\n\
-\n\
-(i.e. all elements that are in either set.)");
-
static PyObject *
set_or(PySetObject *so, PyObject *other)
{
@@ -1149,11 +1324,13 @@ set_or(PySetObject *so, PyObject *other)
Py_RETURN_NOTIMPLEMENTED;
result = (PySetObject *)set_copy(so, NULL);
- if (result == NULL)
+ if (result == NULL) {
return NULL;
- if ((PyObject *)so == other)
+ }
+ if (Py_Is((PyObject *)so, other)) {
return (PyObject *)result;
- if (set_update_internal(result, other)) {
+ }
+ if (set_update_local(result, other)) {
Py_DECREF(result);
return NULL;
}
@@ -1166,8 +1343,9 @@ set_ior(PySetObject *so, PyObject *other)
if (!PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
- if (set_update_internal(so, other))
+ if (set_update_internal(so, other)) {
return NULL;
+ }
return Py_NewRef(so);
}
@@ -1180,7 +1358,7 @@ set_intersection(PySetObject *so, PyObject *other)
int rv;
if ((PyObject *)so == other)
- return set_copy(so, NULL);
+ return set_copy_impl(so);
result = (PySetObject *)make_new_set_basetype(Py_TYPE(so), NULL);
if (result == NULL)
@@ -1254,18 +1432,31 @@ set_intersection(PySetObject *so, PyObject *other)
return NULL;
}
+/*[clinic input]
+set.intersection as set_intersection_multi
+ so: setobject
+ *others as args: object
+
+Return a new set with elements common to the set and all others.
+[clinic start generated code]*/
+
static PyObject *
-set_intersection_multi(PySetObject *so, PyObject *args)
+set_intersection_multi_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=2406ef3387adbe2f input=0d9f3805ccbba6a4]*/
{
Py_ssize_t i;
- if (PyTuple_GET_SIZE(args) == 0)
+ if (PyTuple_GET_SIZE(args) == 0) {
return set_copy(so, NULL);
+ }
PyObject *result = Py_NewRef(so);
for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
PyObject *other = PyTuple_GET_ITEM(args, i);
- PyObject *newresult = set_intersection((PySetObject *)result, other);
+ PyObject *newresult;
+ Py_BEGIN_CRITICAL_SECTION2(result, other);
+ newresult = set_intersection((PySetObject *)result, other);
+ Py_END_CRITICAL_SECTION2();
if (newresult == NULL) {
Py_DECREF(result);
return NULL;
@@ -1275,11 +1466,6 @@ set_intersection_multi(PySetObject *so, PyObject *args)
return result;
}
-PyDoc_STRVAR(intersection_doc,
-"Return the intersection of two sets as a new set.\n\
-\n\
-(i.e. all elements that are in both sets.)");
-
static PyObject *
set_intersection_update(PySetObject *so, PyObject *other)
{
@@ -1293,28 +1479,42 @@ set_intersection_update(PySetObject *so, PyObject *other)
Py_RETURN_NONE;
}
+/*[clinic input]
+set.intersection_update as set_intersection_update_multi
+ so: setobject
+ *others as args: object
+
+Update the set, keeping only elements found in it and all others.
+[clinic start generated code]*/
+
static PyObject *
-set_intersection_update_multi(PySetObject *so, PyObject *args)
+set_intersection_update_multi_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=251c1f729063609d input=223c1e086aa669a9]*/
{
PyObject *tmp;
- tmp = set_intersection_multi(so, args);
+ tmp = set_intersection_multi_impl(so, args);
if (tmp == NULL)
return NULL;
+ Py_BEGIN_CRITICAL_SECTION(so);
set_swap_bodies(so, (PySetObject *)tmp);
+ Py_END_CRITICAL_SECTION();
Py_DECREF(tmp);
Py_RETURN_NONE;
}
-PyDoc_STRVAR(intersection_update_doc,
-"Update a set with the intersection of itself and another.");
-
static PyObject *
set_and(PySetObject *so, PyObject *other)
{
if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
- return set_intersection(so, other);
+
+ PyObject *rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_intersection(so, other);
+ Py_END_CRITICAL_SECTION2();
+
+ return rv;
}
static PyObject *
@@ -1324,15 +1524,30 @@ set_iand(PySetObject *so, PyObject *other)
if (!PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
+
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
result = set_intersection_update(so, other);
+ Py_END_CRITICAL_SECTION2();
+
if (result == NULL)
return NULL;
Py_DECREF(result);
return Py_NewRef(so);
}
+/*[clinic input]
+@critical_section so other
+set.isdisjoint
+ so: setobject
+ other: object
+ /
+
+Return True if two sets have a null intersection.
+[clinic start generated code]*/
+
static PyObject *
-set_isdisjoint(PySetObject *so, PyObject *other)
+set_isdisjoint_impl(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=273493f2d57c565e input=32f8dcab5e0fc7d6]*/
{
PyObject *key, *it, *tmp;
int rv;
@@ -1390,12 +1605,12 @@ set_isdisjoint(PySetObject *so, PyObject *other)
Py_RETURN_TRUE;
}
-PyDoc_STRVAR(isdisjoint_doc,
-"Return True if two sets have a null intersection.");
-
static int
set_difference_update_internal(PySetObject *so, PyObject *other)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
+
if ((PyObject *)so == other)
return set_clear_internal(so);
@@ -1451,28 +1666,39 @@ set_difference_update_internal(PySetObject *so, PyObject *other)
return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
}
+/*[clinic input]
+set.difference_update
+ so: setobject
+ *others as args: object
+
+Update the set, removing elements found in others.
+[clinic start generated code]*/
+
static PyObject *
-set_difference_update(PySetObject *so, PyObject *args)
+set_difference_update_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=28685b2fc63e41c4 input=024e6baa6fbcbb3d]*/
{
Py_ssize_t i;
for (i=0 ; i<PyTuple_GET_SIZE(args) ; i++) {
PyObject *other = PyTuple_GET_ITEM(args, i);
- if (set_difference_update_internal(so, other))
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_difference_update_internal(so, other);
+ Py_END_CRITICAL_SECTION2();
+ if (rv) {
return NULL;
+ }
}
Py_RETURN_NONE;
}
-PyDoc_STRVAR(difference_update_doc,
-"Remove all elements of another set from this set.");
-
static PyObject *
set_copy_and_difference(PySetObject *so, PyObject *other)
{
PyObject *result;
- result = set_copy(so, NULL);
+ result = set_copy_impl(so);
if (result == NULL)
return NULL;
if (set_difference_update_internal((PySetObject *) result, other) == 0)
@@ -1557,23 +1783,39 @@ set_difference(PySetObject *so, PyObject *other)
return result;
}
+/*[clinic input]
+set.difference as set_difference_multi
+ so: setobject
+ *others as args: object
+
+Return a new set with elements in the set that are not in the others.
+[clinic start generated code]*/
+
static PyObject *
-set_difference_multi(PySetObject *so, PyObject *args)
+set_difference_multi_impl(PySetObject *so, PyObject *args)
+/*[clinic end generated code: output=3130c3bb3cac873d input=ba78ea5f099e58df]*/
{
Py_ssize_t i;
PyObject *result, *other;
- if (PyTuple_GET_SIZE(args) == 0)
+ if (PyTuple_GET_SIZE(args) == 0) {
return set_copy(so, NULL);
+ }
other = PyTuple_GET_ITEM(args, 0);
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
result = set_difference(so, other);
+ Py_END_CRITICAL_SECTION2();
if (result == NULL)
return NULL;
for (i=1 ; i<PyTuple_GET_SIZE(args) ; i++) {
other = PyTuple_GET_ITEM(args, i);
- if (set_difference_update_internal((PySetObject *)result, other)) {
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(other);
+ rv = set_difference_update_internal((PySetObject *)result, other);
+ Py_END_CRITICAL_SECTION();
+ if (rv) {
Py_DECREF(result);
return NULL;
}
@@ -1581,16 +1823,17 @@ set_difference_multi(PySetObject *so, PyObject *args)
return result;
}
-PyDoc_STRVAR(difference_doc,
-"Return the difference of two or more sets as a new set.\n\
-\n\
-(i.e. all elements that are in this set but not the others.)");
static PyObject *
set_sub(PySetObject *so, PyObject *other)
{
if (!PyAnySet_Check(so) || !PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
- return set_difference(so, other);
+
+ PyObject *rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_difference(so, other);
+ Py_END_CRITICAL_SECTION2();
+ return rv;
}
static PyObject *
@@ -1598,100 +1841,145 @@ set_isub(PySetObject *so, PyObject *other)
{
if (!PyAnySet_Check(other))
Py_RETURN_NOTIMPLEMENTED;
- if (set_difference_update_internal(so, other))
+
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_difference_update_internal(so, other);
+ Py_END_CRITICAL_SECTION2();
+ if (rv < 0) {
return NULL;
+ }
return Py_NewRef(so);
}
-static PyObject *
-set_symmetric_difference_update(PySetObject *so, PyObject *other)
+static int
+set_symmetric_difference_update_dict(PySetObject *so, PyObject *other)
{
- PySetObject *otherset;
- PyObject *key;
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
+
Py_ssize_t pos = 0;
+ PyObject *key, *value;
Py_hash_t hash;
- setentry *entry;
- int rv;
-
- if ((PyObject *)so == other)
- return set_clear(so, NULL);
-
- if (PyDict_CheckExact(other)) {
- PyObject *value;
- while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
- Py_INCREF(key);
- rv = set_discard_entry(so, key, hash);
- if (rv < 0) {
+ while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
+ Py_INCREF(key);
+ int rv = set_discard_entry(so, key, hash);
+ if (rv < 0) {
+ Py_DECREF(key);
+ return -1;
+ }
+ if (rv == DISCARD_NOTFOUND) {
+ if (set_add_entry(so, key, hash)) {
Py_DECREF(key);
- return NULL;
- }
- if (rv == DISCARD_NOTFOUND) {
- if (set_add_entry(so, key, hash)) {
- Py_DECREF(key);
- return NULL;
- }
+ return -1;
}
- Py_DECREF(key);
}
- Py_RETURN_NONE;
+ Py_DECREF(key);
}
+ return 0;
+}
- if (PyAnySet_Check(other)) {
- otherset = (PySetObject *)Py_NewRef(other);
- } else {
- otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
- if (otherset == NULL)
- return NULL;
- }
+static int
+set_symmetric_difference_update_set(PySetObject *so, PySetObject *other)
+{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so);
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other);
- while (set_next(otherset, &pos, &entry)) {
- key = entry->key;
- hash = entry->hash;
- Py_INCREF(key);
- rv = set_discard_entry(so, key, hash);
+ Py_ssize_t pos = 0;
+ setentry *entry;
+ while (set_next(other, &pos, &entry)) {
+ PyObject *key = Py_NewRef(entry->key);
+ Py_hash_t hash = entry->hash;
+ int rv = set_discard_entry(so, key, hash);
if (rv < 0) {
- Py_DECREF(otherset);
Py_DECREF(key);
- return NULL;
+ return -1;
}
if (rv == DISCARD_NOTFOUND) {
if (set_add_entry(so, key, hash)) {
- Py_DECREF(otherset);
Py_DECREF(key);
- return NULL;
+ return -1;
}
}
Py_DECREF(key);
}
- Py_DECREF(otherset);
- Py_RETURN_NONE;
+ return 0;
}
-PyDoc_STRVAR(symmetric_difference_update_doc,
-"Update a set with the symmetric difference of itself and another.");
+/*[clinic input]
+set.symmetric_difference_update
+ so: setobject
+ other: object
+ /
+
+Update the set, keeping only elements found in either set, but not in both.
+[clinic start generated code]*/
static PyObject *
-set_symmetric_difference(PySetObject *so, PyObject *other)
+set_symmetric_difference_update(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=fbb049c0806028de input=a50acf0365e1f0a5]*/
{
- PyObject *rv;
- PySetObject *otherset;
+ if (Py_Is((PyObject *)so, other)) {
+ return set_clear(so, NULL);
+ }
+
+ int rv;
+ if (PyDict_CheckExact(other)) {
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_symmetric_difference_update_dict(so, other);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else if (PyAnySet_Check(other)) {
+ Py_BEGIN_CRITICAL_SECTION2(so, other);
+ rv = set_symmetric_difference_update_set(so, (PySetObject *)other);
+ Py_END_CRITICAL_SECTION2();
+ }
+ else {
+ PySetObject *otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
+ if (otherset == NULL) {
+ return NULL;
+ }
+
+ Py_BEGIN_CRITICAL_SECTION(so);
+ rv = set_symmetric_difference_update_set(so, otherset);
+ Py_END_CRITICAL_SECTION();
- otherset = (PySetObject *)make_new_set_basetype(Py_TYPE(so), other);
- if (otherset == NULL)
- return NULL;
- rv = set_symmetric_difference_update(otherset, (PyObject *)so);
- if (rv == NULL) {
Py_DECREF(otherset);
+ }
+ if (rv < 0) {
return NULL;
}
- Py_DECREF(rv);
- return (PyObject *)otherset;
+ Py_RETURN_NONE;
}
-PyDoc_STRVAR(symmetric_difference_doc,
-"Return the symmetric difference of two sets as a new set.\n\
-\n\
-(i.e. all elements that are in exactly one of the sets.)");
+/*[clinic input]
+@critical_section so other
+set.symmetric_difference
+ so: setobject
+ other: object
+ /
+
+Return a new set with elements in either the set or other but not both.
+[clinic start generated code]*/
+
+static PyObject *
+set_symmetric_difference_impl(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=270ee0b5d42b0797 input=624f6e7bbdf70db1]*/
+{
+ PySetObject *result = (PySetObject *)make_new_set_basetype(Py_TYPE(so), NULL);
+ if (result == NULL) {
+ return NULL;
+ }
+ if (set_update_lock_held(result, other) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ if (set_symmetric_difference_update_set(result, so) < 0) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ return (PyObject *)result;
+}
static PyObject *
set_xor(PySetObject *so, PyObject *other)
@@ -1715,8 +2003,19 @@ set_ixor(PySetObject *so, PyObject *other)
return Py_NewRef(so);
}
+/*[clinic input]
+@critical_section so other
+set.issubset
+ so: setobject
+ other: object
+ /
+
+Report whether another set contains this set.
+[clinic start generated code]*/
+
static PyObject *
-set_issubset(PySetObject *so, PyObject *other)
+set_issubset_impl(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=b2b59d5f314555ce input=f2a4fd0f2537758b]*/
{
setentry *entry;
Py_ssize_t pos = 0;
@@ -1749,14 +2048,19 @@ set_issubset(PySetObject *so, PyObject *other)
Py_RETURN_TRUE;
}
-PyDoc_STRVAR(issubset_doc,
-"issubset($self, other, /)\n\
---\n\
-\n\
-Test whether every element in the set is in other.");
+/*[clinic input]
+@critical_section so other
+set.issuperset
+ so: setobject
+ other: object
+ /
+
+Report whether this set contains another set.
+[clinic start generated code]*/
static PyObject *
-set_issuperset(PySetObject *so, PyObject *other)
+set_issuperset_impl(PySetObject *so, PyObject *other)
+/*[clinic end generated code: output=ecf00ce552c09461 input=5f2e1f262e6e4ccc]*/
{
if (PyAnySet_Check(other)) {
return set_issubset((PySetObject *)other, (PyObject *)so);
@@ -1785,12 +2089,6 @@ set_issuperset(PySetObject *so, PyObject *other)
Py_RETURN_TRUE;
}
-PyDoc_STRVAR(issuperset_doc,
-"issuperset($self, other, /)\n\
---\n\
-\n\
-Test whether every element in other is in the set.");
-
static PyObject *
set_richcompare(PySetObject *v, PyObject *w, int op)
{
@@ -1804,9 +2102,9 @@ set_richcompare(PySetObject *v, PyObject *w, int op)
case Py_EQ:
if (PySet_GET_SIZE(v) != PySet_GET_SIZE(w))
Py_RETURN_FALSE;
- if (v->hash != -1 &&
- ((PySetObject *)w)->hash != -1 &&
- v->hash != ((PySetObject *)w)->hash)
+ Py_hash_t v_hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(v->hash);
+ Py_hash_t w_hash = FT_ATOMIC_LOAD_SSIZE_RELAXED(((PySetObject *)w)->hash);
+ if (v_hash != -1 && w_hash != -1 && v_hash != w_hash)
Py_RETURN_FALSE;
return set_issubset(v, w);
case Py_NE:
@@ -1834,21 +2132,29 @@ set_richcompare(PySetObject *v, PyObject *w, int op)
Py_RETURN_NOTIMPLEMENTED;
}
+/*[clinic input]
+@critical_section
+set.add
+ so: setobject
+ object as key: object
+ /
+
+Add an element to a set.
+
+This has no effect if the element is already present.
+[clinic start generated code]*/
+
static PyObject *
-set_add(PySetObject *so, PyObject *key)
+set_add_impl(PySetObject *so, PyObject *key)
+/*[clinic end generated code: output=4cc4a937f1425c96 input=03baf62cb0e66514]*/
{
if (set_add_key(so, key))
return NULL;
Py_RETURN_NONE;
}
-PyDoc_STRVAR(add_doc,
-"Add an element to a set.\n\
-\n\
-This has no effect if the element is already present.");
-
static int
-set_contains(PySetObject *so, PyObject *key)
+set_contains_lock_held(PySetObject *so, PyObject *key)
{
PyObject *tmpkey;
int rv;
@@ -1867,21 +2173,54 @@ set_contains(PySetObject *so, PyObject *key)
return rv;
}
+int
+_PySet_Contains(PySetObject *so, PyObject *key)
+{
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(so);
+ rv = set_contains_lock_held(so, key);
+ Py_END_CRITICAL_SECTION();
+ return rv;
+}
+
+/*[clinic input]
+@critical_section
+@coexist
+set.__contains__
+ so: setobject
+ object as key: object
+ /
+
+x.__contains__(y) <==> y in x.
+[clinic start generated code]*/
+
static PyObject *
-set_direct_contains(PySetObject *so, PyObject *key)
+set___contains___impl(PySetObject *so, PyObject *key)
+/*[clinic end generated code: output=b44863d034b3c70e input=4a7d568459617f24]*/
{
long result;
- result = set_contains(so, key);
+ result = set_contains_lock_held(so, key);
if (result < 0)
return NULL;
return PyBool_FromLong(result);
}
-PyDoc_STRVAR(contains_doc, "x.__contains__(y) <==> y in x.");
+/*[clinic input]
+@critical_section
+set.remove
+ so: setobject
+ object as key: object
+ /
+
+Remove an element from a set; it must be a member.
+
+If the element is not a member, raise a KeyError.
+[clinic start generated code]*/
static PyObject *
-set_remove(PySetObject *so, PyObject *key)
+set_remove_impl(PySetObject *so, PyObject *key)
+/*[clinic end generated code: output=0b9134a2a2200363 input=893e1cb1df98227a]*/
{
PyObject *tmpkey;
int rv;
@@ -1907,13 +2246,22 @@ set_remove(PySetObject *so, PyObject *key)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(remove_doc,
-"Remove an element from a set; it must be a member.\n\
-\n\
-If the element is not a member, raise a KeyError.");
+/*[clinic input]
+@critical_section
+set.discard
+ so: setobject
+ object as key: object
+ /
+
+Remove an element from a set if it is a member.
+
+Unlike set.remove(), the discard() method does not raise
+an exception when an element is missing from the set.
+[clinic start generated code]*/
static PyObject *
-set_discard(PySetObject *so, PyObject *key)
+set_discard_impl(PySetObject *so, PyObject *key)
+/*[clinic end generated code: output=eec3b687bf32759e input=861cb7fb69b4def0]*/
{
PyObject *tmpkey;
int rv;
@@ -1934,14 +2282,17 @@ set_discard(PySetObject *so, PyObject *key)
Py_RETURN_NONE;
}
-PyDoc_STRVAR(discard_doc,
-"Remove an element from a set if it is a member.\n\
-\n\
-Unlike set.remove(), the discard() method does not raise\n\
-an exception when an element is missing from the set.");
+/*[clinic input]
+@critical_section
+set.__reduce__
+ so: setobject
+
+Return state information for pickling.
+[clinic start generated code]*/
static PyObject *
-set_reduce(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set___reduce___impl(PySetObject *so)
+/*[clinic end generated code: output=9af7d0e029df87ee input=59405a4249e82f71]*/
{
PyObject *keys=NULL, *args=NULL, *result=NULL, *state=NULL;
@@ -1962,8 +2313,17 @@ done:
return result;
}
+/*[clinic input]
+@critical_section
+set.__sizeof__
+ so: setobject
+
+S.__sizeof__() -> size of S in memory, in bytes.
+[clinic start generated code]*/
+
static PyObject *
-set_sizeof(PySetObject *so, PyObject *Py_UNUSED(ignored))
+set___sizeof___impl(PySetObject *so)
+/*[clinic end generated code: output=4bfa3df7bd38ed88 input=09e1a09f168eaa23]*/
{
size_t res = _PyObject_SIZE(Py_TYPE(so));
if (so->table != so->smalltable) {
@@ -1972,19 +2332,29 @@ set_sizeof(PySetObject *so, PyObject *Py_UNUSED(ignored))
return PyLong_FromSize_t(res);
}
-PyDoc_STRVAR(sizeof_doc, "S.__sizeof__() -> size of S in memory, in bytes");
static int
set_init(PySetObject *self, PyObject *args, PyObject *kwds)
{
PyObject *iterable = NULL;
- if (!_PyArg_NoKeywords("set", kwds))
+ if (!_PyArg_NoKeywords("set", kwds))
return -1;
if (!PyArg_UnpackTuple(args, Py_TYPE(self)->tp_name, 0, 1, &iterable))
return -1;
+
+ if (Py_REFCNT(self) == 1 && self->fill == 0) {
+ self->hash = -1;
+ if (iterable == NULL) {
+ return 0;
+ }
+ return set_update_local(self, iterable);
+ }
+ Py_BEGIN_CRITICAL_SECTION(self);
if (self->fill)
set_clear_internal(self);
self->hash = -1;
+ Py_END_CRITICAL_SECTION();
+
if (iterable == NULL)
return 0;
return set_update_internal(self, iterable);
@@ -2013,70 +2383,39 @@ set_vectorcall(PyObject *type, PyObject * const*args,
}
static PySequenceMethods set_as_sequence = {
- set_len, /* sq_length */
+ (lenfunc)set_len, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
- (objobjproc)set_contains, /* sq_contains */
+ (objobjproc)_PySet_Contains, /* sq_contains */
};
/* set object ********************************************************/
-#ifdef Py_DEBUG
-static PyObject *test_c_api(PySetObject *so, PyObject *Py_UNUSED(ignored));
-
-PyDoc_STRVAR(test_c_api_doc, "Exercises C API. Returns True.\n\
-All is well if assertions don't fail.");
-#endif
-
static PyMethodDef set_methods[] = {
- {"add", (PyCFunction)set_add, METH_O,
- add_doc},
- {"clear", (PyCFunction)set_clear, METH_NOARGS,
- clear_doc},
- {"__contains__",(PyCFunction)set_direct_contains, METH_O | METH_COEXIST,
- contains_doc},
- {"copy", (PyCFunction)set_copy, METH_NOARGS,
- copy_doc},
- {"discard", (PyCFunction)set_discard, METH_O,
- discard_doc},
- {"difference", (PyCFunction)set_difference_multi, METH_VARARGS,
- difference_doc},
- {"difference_update", (PyCFunction)set_difference_update, METH_VARARGS,
- difference_update_doc},
- {"intersection",(PyCFunction)set_intersection_multi, METH_VARARGS,
- intersection_doc},
- {"intersection_update",(PyCFunction)set_intersection_update_multi, METH_VARARGS,
- intersection_update_doc},
- {"isdisjoint", (PyCFunction)set_isdisjoint, METH_O,
- isdisjoint_doc},
- {"issubset", (PyCFunction)set_issubset, METH_O,
- issubset_doc},
- {"issuperset", (PyCFunction)set_issuperset, METH_O,
- issuperset_doc},
- {"pop", (PyCFunction)set_pop, METH_NOARGS,
- pop_doc},
- {"__reduce__", (PyCFunction)set_reduce, METH_NOARGS,
- reduce_doc},
- {"remove", (PyCFunction)set_remove, METH_O,
- remove_doc},
- {"__sizeof__", (PyCFunction)set_sizeof, METH_NOARGS,
- sizeof_doc},
- {"symmetric_difference",(PyCFunction)set_symmetric_difference, METH_O,
- symmetric_difference_doc},
- {"symmetric_difference_update",(PyCFunction)set_symmetric_difference_update, METH_O,
- symmetric_difference_update_doc},
-#ifdef Py_DEBUG
- {"test_c_api", (PyCFunction)test_c_api, METH_NOARGS,
- test_c_api_doc},
-#endif
- {"union", (PyCFunction)set_union, METH_VARARGS,
- union_doc},
- {"update", (PyCFunction)set_update, METH_VARARGS,
- update_doc},
+ SET_ADD_METHODDEF
+ SET_CLEAR_METHODDEF
+ SET___CONTAINS___METHODDEF
+ SET_COPY_METHODDEF
+ SET_DISCARD_METHODDEF
+ SET_DIFFERENCE_MULTI_METHODDEF
+ SET_DIFFERENCE_UPDATE_METHODDEF
+ SET_INTERSECTION_MULTI_METHODDEF
+ SET_INTERSECTION_UPDATE_MULTI_METHODDEF
+ SET_ISDISJOINT_METHODDEF
+ SET_ISSUBSET_METHODDEF
+ SET_ISSUPERSET_METHODDEF
+ SET_POP_METHODDEF
+ SET___REDUCE___METHODDEF
+ SET_REMOVE_METHODDEF
+ SET___SIZEOF___METHODDEF
+ SET_SYMMETRIC_DIFFERENCE_METHODDEF
+ SET_SYMMETRIC_DIFFERENCE_UPDATE_METHODDEF
+ SET_UNION_METHODDEF
+ SET_UPDATE_METHODDEF
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* sentinel */
};
@@ -2114,8 +2453,8 @@ static PyNumberMethods set_as_number = {
};
PyDoc_STRVAR(set_doc,
-"set() -> new empty set object\n\
-set(iterable) -> new set object\n\
+"set(iterable=(), /)\n\
+--\n\
\n\
Build an unordered collection of unique elements.");
@@ -2169,28 +2508,17 @@ PyTypeObject PySet_Type = {
static PyMethodDef frozenset_methods[] = {
- {"__contains__",(PyCFunction)set_direct_contains, METH_O | METH_COEXIST,
- contains_doc},
- {"copy", (PyCFunction)frozenset_copy, METH_NOARGS,
- copy_doc},
- {"difference", (PyCFunction)set_difference_multi, METH_VARARGS,
- difference_doc},
- {"intersection", (PyCFunction)set_intersection_multi, METH_VARARGS,
- intersection_doc},
- {"isdisjoint", (PyCFunction)set_isdisjoint, METH_O,
- isdisjoint_doc},
- {"issubset", (PyCFunction)set_issubset, METH_O,
- issubset_doc},
- {"issuperset", (PyCFunction)set_issuperset, METH_O,
- issuperset_doc},
- {"__reduce__", (PyCFunction)set_reduce, METH_NOARGS,
- reduce_doc},
- {"__sizeof__", (PyCFunction)set_sizeof, METH_NOARGS,
- sizeof_doc},
- {"symmetric_difference",(PyCFunction)set_symmetric_difference, METH_O,
- symmetric_difference_doc},
- {"union", (PyCFunction)set_union, METH_VARARGS,
- union_doc},
+ SET___CONTAINS___METHODDEF
+ FROZENSET_COPY_METHODDEF
+ SET_DIFFERENCE_MULTI_METHODDEF
+ SET_INTERSECTION_MULTI_METHODDEF
+ SET_ISDISJOINT_METHODDEF
+ SET_ISSUBSET_METHODDEF
+ SET_ISSUPERSET_METHODDEF
+ SET___REDUCE___METHODDEF
+ SET___SIZEOF___METHODDEF
+ SET_SYMMETRIC_DIFFERENCE_METHODDEF
+ SET_UNION_METHODDEF
{"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* sentinel */
};
@@ -2215,8 +2543,8 @@ static PyNumberMethods frozenset_as_number = {
};
PyDoc_STRVAR(frozenset_doc,
-"frozenset() -> empty frozenset object\n\
-frozenset(iterable) -> frozenset object\n\
+"frozenset(iterable=(), /)\n\
+--\n\
\n\
Build an immutable unordered collection of unique elements.");
@@ -2288,7 +2616,7 @@ PySet_Size(PyObject *anyset)
PyErr_BadInternalCall();
return -1;
}
- return PySet_GET_SIZE(anyset);
+ return set_len((PySetObject *)anyset);
}
int
@@ -2298,7 +2626,14 @@ PySet_Clear(PyObject *set)
PyErr_BadInternalCall();
return -1;
}
- return set_clear_internal((PySetObject *)set);
+ (void)set_clear((PySetObject *)set, NULL);
+ return 0;
+}
+
+void
+_PySet_ClearInternal(PySetObject *so)
+{
+ (void)set_clear_internal(so);
}
int
@@ -2308,7 +2643,12 @@ PySet_Contains(PyObject *anyset, PyObject *key)
PyErr_BadInternalCall();
return -1;
}
- return set_contains_key((PySetObject *)anyset, key);
+
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(anyset);
+ rv = set_contains_key((PySetObject *)anyset, key);
+ Py_END_CRITICAL_SECTION();
+ return rv;
}
int
@@ -2318,7 +2658,12 @@ PySet_Discard(PyObject *set, PyObject *key)
PyErr_BadInternalCall();
return -1;
}
- return set_discard_key((PySetObject *)set, key);
+
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(set);
+ rv = set_discard_key((PySetObject *)set, key);
+ Py_END_CRITICAL_SECTION();
+ return rv;
}
int
@@ -2329,7 +2674,12 @@ PySet_Add(PyObject *anyset, PyObject *key)
PyErr_BadInternalCall();
return -1;
}
- return set_add_key((PySetObject *)anyset, key);
+
+ int rv;
+ Py_BEGIN_CRITICAL_SECTION(anyset);
+ rv = set_add_key((PySetObject *)anyset, key);
+ Py_END_CRITICAL_SECTION();
+ return rv;
}
int
@@ -2348,6 +2698,23 @@ _PySet_NextEntry(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash
return 1;
}
+int
+_PySet_NextEntryRef(PyObject *set, Py_ssize_t *pos, PyObject **key, Py_hash_t *hash)
+{
+ setentry *entry;
+
+ if (!PyAnySet_Check(set)) {
+ PyErr_BadInternalCall();
+ return -1;
+ }
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(set);
+ if (set_next((PySetObject *)set, pos, &entry) == 0)
+ return 0;
+ *key = Py_NewRef(entry->key);
+ *hash = entry->hash;
+ return 1;
+}
+
PyObject *
PySet_Pop(PyObject *set)
{
@@ -2371,148 +2738,6 @@ _PySet_Update(PyObject *set, PyObject *iterable)
/* Exported for the gdb plugin's benefit. */
PyObject *_PySet_Dummy = dummy;
-#ifdef Py_DEBUG
-
-/* Test code to be called with any three element set.
- Returns True and original set is restored. */
-
-#define assertRaises(call_return_value, exception) \
- do { \
- assert(call_return_value); \
- assert(PyErr_ExceptionMatches(exception)); \
- PyErr_Clear(); \
- } while(0)
-
-static PyObject *
-test_c_api(PySetObject *so, PyObject *Py_UNUSED(ignored))
-{
- Py_ssize_t count;
- const char *s;
- Py_ssize_t i;
- PyObject *elem=NULL, *dup=NULL, *t, *f, *dup2, *x=NULL;
- PyObject *ob = (PyObject *)so;
- Py_hash_t hash;
- PyObject *str;
-
- /* Verify preconditions */
- assert(PyAnySet_Check(ob));
- assert(PyAnySet_CheckExact(ob));
- assert(!PyFrozenSet_CheckExact(ob));
-
- /* so.clear(); so |= set("abc"); */
- str = PyUnicode_FromString("abc");
- if (str == NULL)
- return NULL;
- set_clear_internal(so);
- if (set_update_internal(so, str)) {
- Py_DECREF(str);
- return NULL;
- }
- Py_DECREF(str);
-
- /* Exercise type/size checks */
- assert(PySet_Size(ob) == 3);
- assert(PySet_GET_SIZE(ob) == 3);
-
- /* Raise TypeError for non-iterable constructor arguments */
- assertRaises(PySet_New(Py_None) == NULL, PyExc_TypeError);
- assertRaises(PyFrozenSet_New(Py_None) == NULL, PyExc_TypeError);
-
- /* Raise TypeError for unhashable key */
- dup = PySet_New(ob);
- assertRaises(PySet_Discard(ob, dup) == -1, PyExc_TypeError);
- assertRaises(PySet_Contains(ob, dup) == -1, PyExc_TypeError);
- assertRaises(PySet_Add(ob, dup) == -1, PyExc_TypeError);
-
- /* Exercise successful pop, contains, add, and discard */
- elem = PySet_Pop(ob);
- assert(PySet_Contains(ob, elem) == 0);
- assert(PySet_GET_SIZE(ob) == 2);
- assert(PySet_Add(ob, elem) == 0);
- assert(PySet_Contains(ob, elem) == 1);
- assert(PySet_GET_SIZE(ob) == 3);
- assert(PySet_Discard(ob, elem) == 1);
- assert(PySet_GET_SIZE(ob) == 2);
- assert(PySet_Discard(ob, elem) == 0);
- assert(PySet_GET_SIZE(ob) == 2);
-
- /* Exercise clear */
- dup2 = PySet_New(dup);
- assert(PySet_Clear(dup2) == 0);
- assert(PySet_Size(dup2) == 0);
- Py_DECREF(dup2);
-
- /* Raise SystemError on clear or update of frozen set */
- f = PyFrozenSet_New(dup);
- assertRaises(PySet_Clear(f) == -1, PyExc_SystemError);
- assertRaises(_PySet_Update(f, dup) == -1, PyExc_SystemError);
- assert(PySet_Add(f, elem) == 0);
- Py_INCREF(f);
- assertRaises(PySet_Add(f, elem) == -1, PyExc_SystemError);
- Py_DECREF(f);
- Py_DECREF(f);
-
- /* Exercise direct iteration */
- i = 0, count = 0;
- while (_PySet_NextEntry((PyObject *)dup, &i, &x, &hash)) {
- s = PyUnicode_AsUTF8(x);
- assert(s && (s[0] == 'a' || s[0] == 'b' || s[0] == 'c'));
- count++;
- }
- assert(count == 3);
-
- /* Exercise updates */
- dup2 = PySet_New(NULL);
- assert(_PySet_Update(dup2, dup) == 0);
- assert(PySet_Size(dup2) == 3);
- assert(_PySet_Update(dup2, dup) == 0);
- assert(PySet_Size(dup2) == 3);
- Py_DECREF(dup2);
-
- /* Raise SystemError when self argument is not a set or frozenset. */
- t = PyTuple_New(0);
- assertRaises(PySet_Size(t) == -1, PyExc_SystemError);
- assertRaises(PySet_Contains(t, elem) == -1, PyExc_SystemError);
- Py_DECREF(t);
-
- /* Raise SystemError when self argument is not a set. */
- f = PyFrozenSet_New(dup);
- assert(PySet_Size(f) == 3);
- assert(PyFrozenSet_CheckExact(f));
- assertRaises(PySet_Discard(f, elem) == -1, PyExc_SystemError);
- assertRaises(PySet_Pop(f) == NULL, PyExc_SystemError);
- Py_DECREF(f);
-
- /* Raise KeyError when popping from an empty set */
- assert(PyNumber_InPlaceSubtract(ob, ob) == ob);
- Py_DECREF(ob);
- assert(PySet_GET_SIZE(ob) == 0);
- assertRaises(PySet_Pop(ob) == NULL, PyExc_KeyError);
-
- /* Restore the set from the copy using the PyNumber API */
- assert(PyNumber_InPlaceOr(ob, dup) == ob);
- Py_DECREF(ob);
-
- /* Verify constructors accept NULL arguments */
- f = PySet_New(NULL);
- assert(f != NULL);
- assert(PySet_GET_SIZE(f) == 0);
- Py_DECREF(f);
- f = PyFrozenSet_New(NULL);
- assert(f != NULL);
- assert(PyFrozenSet_CheckExact(f));
- assert(PySet_GET_SIZE(f) == 0);
- Py_DECREF(f);
-
- Py_DECREF(elem);
- Py_DECREF(dup);
- Py_RETURN_TRUE;
-}
-
-#undef assertRaises
-
-#endif
-
/***** Dummy Struct *************************************************/
static PyObject *
@@ -2550,8 +2775,4 @@ static PyTypeObject _PySetDummy_Type = {
Py_TPFLAGS_DEFAULT, /*tp_flags */
};
-static PyObject _dummy_struct = {
- _PyObject_EXTRA_INIT
- { _Py_IMMORTAL_REFCNT },
- &_PySetDummy_Type
-};
+static PyObject _dummy_struct = _PyObject_HEAD_INIT(&_PySetDummy_Type);
diff --git a/contrib/tools/python3/Objects/sliceobject.c b/contrib/tools/python3/Objects/sliceobject.c
index e6776ac92b6..245bea98d58 100644
--- a/contrib/tools/python3/Objects/sliceobject.c
+++ b/contrib/tools/python3/Objects/sliceobject.c
@@ -16,8 +16,9 @@ this type and there is exactly one in existence.
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
#include "pycore_long.h" // _PyLong_GetZero()
+#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_object.h" // _PyObject_GC_TRACK()
-#include "structmember.h" // PyMemberDef
+
static PyObject *
ellipsis_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
@@ -26,7 +27,7 @@ ellipsis_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
PyErr_SetString(PyExc_TypeError, "EllipsisType takes no arguments");
return NULL;
}
- return Py_NewRef(Py_Ellipsis);
+ return Py_Ellipsis;
}
static void
@@ -56,6 +57,11 @@ static PyMethodDef ellipsis_methods[] = {
{NULL, NULL}
};
+PyDoc_STRVAR(ellipsis_doc,
+"ellipsis()\n"
+"--\n\n"
+"The type of the Ellipsis singleton.");
+
PyTypeObject PyEllipsis_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"ellipsis", /* tp_name */
@@ -77,7 +83,7 @@ PyTypeObject PyEllipsis_Type = {
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
- 0, /* tp_doc */
+ ellipsis_doc, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
@@ -97,23 +103,23 @@ PyTypeObject PyEllipsis_Type = {
ellipsis_new, /* tp_new */
};
-PyObject _Py_EllipsisObject = {
- _PyObject_EXTRA_INIT
- { _Py_IMMORTAL_REFCNT },
- &PyEllipsis_Type
-};
+PyObject _Py_EllipsisObject = _PyObject_HEAD_INIT(&PyEllipsis_Type);
/* Slice object implementation */
-
-void _PySlice_Fini(PyInterpreterState *interp)
+void _PySlice_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization)
{
- PySliceObject *obj = interp->slice_cache;
+ if (!is_finalization) {
+ return;
+ }
+#ifdef WITH_FREELISTS
+ PySliceObject *obj = freelists->slices.slice_cache;
if (obj != NULL) {
- interp->slice_cache = NULL;
+ freelists->slices.slice_cache = NULL;
PyObject_GC_Del(obj);
}
+#endif
}
/* start, stop, and step are python objects with None indicating no
@@ -124,15 +130,17 @@ static PySliceObject *
_PyBuildSlice_Consume2(PyObject *start, PyObject *stop, PyObject *step)
{
assert(start != NULL && stop != NULL && step != NULL);
-
- PyInterpreterState *interp = _PyInterpreterState_GET();
PySliceObject *obj;
- if (interp->slice_cache != NULL) {
- obj = interp->slice_cache;
- interp->slice_cache = NULL;
+#ifdef WITH_FREELISTS
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
+ if (freelists->slices.slice_cache != NULL) {
+ obj = freelists->slices.slice_cache;
+ freelists->slices.slice_cache = NULL;
_Py_NewReference((PyObject *)obj);
}
- else {
+ else
+#endif
+ {
obj = PyObject_GC_New(PySliceObject, &PySlice_Type);
if (obj == NULL) {
goto error;
@@ -357,15 +365,18 @@ Create a slice object. This is used for extended slicing (e.g. a[0:10:2]).");
static void
slice_dealloc(PySliceObject *r)
{
- PyInterpreterState *interp = _PyInterpreterState_GET();
_PyObject_GC_UNTRACK(r);
Py_DECREF(r->step);
Py_DECREF(r->start);
Py_DECREF(r->stop);
- if (interp->slice_cache == NULL) {
- interp->slice_cache = r;
+#ifdef WITH_FREELISTS
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
+ if (freelists->slices.slice_cache == NULL) {
+ freelists->slices.slice_cache = r;
}
- else {
+ else
+#endif
+ {
PyObject_GC_Del(r);
}
}
@@ -377,9 +388,9 @@ slice_repr(PySliceObject *r)
}
static PyMemberDef slice_members[] = {
- {"start", T_OBJECT, offsetof(PySliceObject, start), READONLY},
- {"stop", T_OBJECT, offsetof(PySliceObject, stop), READONLY},
- {"step", T_OBJECT, offsetof(PySliceObject, step), READONLY},
+ {"start", _Py_T_OBJECT, offsetof(PySliceObject, start), Py_READONLY},
+ {"stop", _Py_T_OBJECT, offsetof(PySliceObject, stop), Py_READONLY},
+ {"step", _Py_T_OBJECT, offsetof(PySliceObject, step), Py_READONLY},
{0}
};
@@ -415,7 +426,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
/* Convert step to an integer; raise for zero step. */
if (self->step == Py_None) {
- step = Py_NewRef(_PyLong_GetOne());
+ step = _PyLong_GetOne();
step_is_negative = 0;
}
else {
@@ -443,7 +454,7 @@ _PySlice_GetLongIndices(PySliceObject *self, PyObject *length,
goto error;
}
else {
- lower = Py_NewRef(_PyLong_GetZero());
+ lower = _PyLong_GetZero();
upper = Py_NewRef(length);
}
diff --git a/contrib/tools/python3/Objects/stringlib/README.txt b/contrib/tools/python3/Objects/stringlib/README.txt
index e1e329290ac..26f3d02b0ef 100644
--- a/contrib/tools/python3/Objects/stringlib/README.txt
+++ b/contrib/tools/python3/Objects/stringlib/README.txt
@@ -9,7 +9,7 @@ the following defines used by the different modules:
STRINGLIB_CHAR
- the type used to hold a character (char or Py_UNICODE)
+ the type used to hold a character (char, Py_UCS1, Py_UCS2 or Py_UCS4)
STRINGLIB_GET_EMPTY()
diff --git a/contrib/tools/python3/Objects/stringlib/clinic/transmogrify.h.h b/contrib/tools/python3/Objects/stringlib/clinic/transmogrify.h.h
index 49388cf043c..3a985ab5c7a 100644
--- a/contrib/tools/python3/Objects/stringlib/clinic/transmogrify.h.h
+++ b/contrib/tools/python3/Objects/stringlib/clinic/transmogrify.h.h
@@ -3,10 +3,11 @@ preserve
[clinic start generated code]*/
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
-# include "pycore_gc.h" // PyGC_Head
-# include "pycore_runtime.h" // _Py_ID()
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
#endif
-
+#include "pycore_abstract.h" // _PyNumber_Index()
+#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
PyDoc_STRVAR(stringlib_expandtabs__doc__,
"expandtabs($self, /, tabsize=8)\n"
@@ -62,7 +63,7 @@ stringlib_expandtabs(PyObject *self, PyObject *const *args, Py_ssize_t nargs, Py
if (!noptargs) {
goto skip_optional_pos;
}
- tabsize = _PyLong_AsInt(args[0]);
+ tabsize = PyLong_AsInt(args[0]);
if (tabsize == -1 && PyErr_Occurred()) {
goto exit;
}
@@ -278,4 +279,4 @@ stringlib_zfill(PyObject *self, PyObject *arg)
exit:
return return_value;
}
-/*[clinic end generated code: output=d44a269805f6739e input=a9049054013a1b77]*/
+/*[clinic end generated code: output=b409bdf9ab68d5a6 input=a9049054013a1b77]*/
diff --git a/contrib/tools/python3/Objects/stringlib/codecs.h b/contrib/tools/python3/Objects/stringlib/codecs.h
index 958cc861478..f98e71c3fc6 100644
--- a/contrib/tools/python3/Objects/stringlib/codecs.h
+++ b/contrib/tools/python3/Objects/stringlib/codecs.h
@@ -408,9 +408,6 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer,
}
else {
/* rep is unicode */
- if (PyUnicode_READY(rep) < 0)
- goto error;
-
if (!PyUnicode_IS_ASCII(rep)) {
raise_encode_exception(&exc, "utf-8", unicode,
startpos, endpos,
diff --git a/contrib/tools/python3/Objects/stringlib/fastsearch.h b/contrib/tools/python3/Objects/stringlib/fastsearch.h
index 257b7bd6788..76ddbf720f2 100644
--- a/contrib/tools/python3/Objects/stringlib/fastsearch.h
+++ b/contrib/tools/python3/Objects/stringlib/fastsearch.h
@@ -69,8 +69,8 @@ STRINGLIB(find_char)(const STRINGLIB_CHAR* s, Py_ssize_t n, STRINGLIB_CHAR ch)
and UCS4 representations. */
if (needle != 0) {
do {
- void *candidate = memchr(p, needle,
- (e - p) * sizeof(STRINGLIB_CHAR));
+ const void *candidate = memchr(p, needle,
+ (e - p) * sizeof(STRINGLIB_CHAR));
if (candidate == NULL)
return -1;
s1 = p;
@@ -588,7 +588,7 @@ STRINGLIB(default_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
continue;
}
/* miss: check if next character is part of pattern */
- if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
+ if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
i = i + m;
}
else {
@@ -597,7 +597,7 @@ STRINGLIB(default_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
}
else {
/* skip: check if next character is part of pattern */
- if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
+ if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
i = i + m;
}
}
@@ -661,7 +661,7 @@ STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
}
}
/* miss: check if next character is part of pattern */
- if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
+ if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
i = i + m;
}
else {
@@ -670,7 +670,7 @@ STRINGLIB(adaptive_find)(const STRINGLIB_CHAR* s, Py_ssize_t n,
}
else {
/* skip: check if next character is part of pattern */
- if (!STRINGLIB_BLOOM(mask, ss[i+1])) {
+ if (i + 1 <= w && !STRINGLIB_BLOOM(mask, ss[i+1])) {
i = i + m;
}
}
diff --git a/contrib/tools/python3/Objects/stringlib/find.h b/contrib/tools/python3/Objects/stringlib/find.h
index 509b9297396..c385718a5b2 100644
--- a/contrib/tools/python3/Objects/stringlib/find.h
+++ b/contrib/tools/python3/Objects/stringlib/find.h
@@ -70,50 +70,3 @@ STRINGLIB(contains_obj)(PyObject* str, PyObject* sub)
}
#endif /* STRINGLIB_WANT_CONTAINS_OBJ */
-
-/*
-This function is a helper for the "find" family (find, rfind, index,
-rindex) and for count, startswith and endswith, because they all have
-the same behaviour for the arguments.
-
-It does not touch the variables received until it knows everything
-is ok.
-*/
-
-#define FORMAT_BUFFER_SIZE 50
-
-Py_LOCAL_INLINE(int)
-STRINGLIB(parse_args_finds)(const char * function_name, PyObject *args,
- PyObject **subobj,
- Py_ssize_t *start, Py_ssize_t *end)
-{
- PyObject *tmp_subobj;
- Py_ssize_t tmp_start = 0;
- Py_ssize_t tmp_end = PY_SSIZE_T_MAX;
- PyObject *obj_start=Py_None, *obj_end=Py_None;
- char format[FORMAT_BUFFER_SIZE] = "O|OO:";
- size_t len = strlen(format);
-
- strncpy(format + len, function_name, FORMAT_BUFFER_SIZE - len - 1);
- format[FORMAT_BUFFER_SIZE - 1] = '\0';
-
- if (!PyArg_ParseTuple(args, format, &tmp_subobj, &obj_start, &obj_end))
- return 0;
-
- /* To support None in "start" and "end" arguments, meaning
- the same as if they were not passed.
- */
- if (obj_start != Py_None)
- if (!_PyEval_SliceIndex(obj_start, &tmp_start))
- return 0;
- if (obj_end != Py_None)
- if (!_PyEval_SliceIndex(obj_end, &tmp_end))
- return 0;
-
- *start = tmp_start;
- *end = tmp_end;
- *subobj = tmp_subobj;
- return 1;
-}
-
-#undef FORMAT_BUFFER_SIZE
diff --git a/contrib/tools/python3/Objects/stringlib/unicode_format.h b/contrib/tools/python3/Objects/stringlib/unicode_format.h
index ccd7c77c0a0..91c71ab5736 100644
--- a/contrib/tools/python3/Objects/stringlib/unicode_format.h
+++ b/contrib/tools/python3/Objects/stringlib/unicode_format.h
@@ -2,6 +2,7 @@
unicode_format.h -- implementation of str.format().
*/
+#include "pycore_complexobject.h" // _PyComplex_FormatAdvancedWriter()
#include "pycore_floatobject.h" // _PyFloat_FormatAdvancedWriter()
/************************************************************************/
@@ -820,7 +821,7 @@ output_markup(SubString *field_name, SubString *format_spec,
if (conversion != '\0') {
tmp = do_conversion(fieldobj, conversion);
- if (tmp == NULL || PyUnicode_READY(tmp) == -1)
+ if (tmp == NULL)
goto done;
/* do the assignment, transferring ownership: fieldobj = tmp */
@@ -832,7 +833,7 @@ output_markup(SubString *field_name, SubString *format_spec,
if (format_spec_needs_expanding) {
tmp = build_string(format_spec, args, kwargs, recursion_depth-1,
auto_number);
- if (tmp == NULL || PyUnicode_READY(tmp) == -1)
+ if (tmp == NULL)
goto done;
/* note that in the case we're expanding the format string,
@@ -948,10 +949,6 @@ do_string_format(PyObject *self, PyObject *args, PyObject *kwargs)
int recursion_depth = 2;
AutoNumber auto_number;
-
- if (PyUnicode_READY(self) == -1)
- return NULL;
-
AutoNumber_Init(&auto_number);
SubString_init(&input, self, 0, PyUnicode_GET_LENGTH(self));
return build_string(&input, args, kwargs, recursion_depth, &auto_number);
@@ -1110,9 +1107,6 @@ formatter_parser(PyObject *ignored, PyObject *self)
return NULL;
}
- if (PyUnicode_READY(self) == -1)
- return NULL;
-
it = PyObject_New(formatteriterobject, &PyFormatterIter_Type);
if (it == NULL)
return NULL;
@@ -1252,9 +1246,6 @@ formatter_field_name_split(PyObject *ignored, PyObject *self)
return NULL;
}
- if (PyUnicode_READY(self) == -1)
- return NULL;
-
it = PyObject_New(fieldnameiterobject, &PyFieldNameIter_Type);
if (it == NULL)
return NULL;
diff --git a/contrib/tools/python3/Objects/structseq.c b/contrib/tools/python3/Objects/structseq.c
index 246d4c7630c..efcadf50078 100644
--- a/contrib/tools/python3/Objects/structseq.c
+++ b/contrib/tools/python3/Objects/structseq.c
@@ -8,11 +8,11 @@
*/
#include "Python.h"
-#include "pycore_tuple.h" // _PyTuple_FromArray()
+#include "pycore_initconfig.h" // _PyStatus_OK()
+#include "pycore_modsupport.h" // _PyArg_NoPositional()
#include "pycore_object.h" // _PyObject_GC_TRACK()
-#include "structmember.h" // PyMemberDef
#include "pycore_structseq.h" // PyStructSequence_InitType()
-#include "pycore_initconfig.h" // _PyStatus_OK()
+#include "pycore_tuple.h" // _PyTuple_FromArray()
static const char visible_length_key[] = "n_sequence_fields";
static const char real_length_key[] = "n_fields";
@@ -82,15 +82,28 @@ PyStructSequence_New(PyTypeObject *type)
}
void
-PyStructSequence_SetItem(PyObject* op, Py_ssize_t i, PyObject* v)
+PyStructSequence_SetItem(PyObject *op, Py_ssize_t index, PyObject *value)
{
- PyStructSequence_SET_ITEM(op, i, v);
+ PyTupleObject *tuple = _PyTuple_CAST(op);
+ assert(0 <= index);
+#ifndef NDEBUG
+ Py_ssize_t n_fields = REAL_SIZE(op);
+ assert(n_fields >= 0);
+ assert(index < n_fields);
+#endif
+ tuple->ob_item[index] = value;
}
PyObject*
-PyStructSequence_GetItem(PyObject* op, Py_ssize_t i)
+PyStructSequence_GetItem(PyObject *op, Py_ssize_t index)
{
- return PyStructSequence_GET_ITEM(op, i);
+ assert(0 <= index);
+#ifndef NDEBUG
+ Py_ssize_t n_fields = REAL_SIZE(op);
+ assert(n_fields >= 0);
+ assert(index < n_fields);
+#endif
+ return PyTuple_GET_ITEM(op, index);
}
@@ -146,7 +159,6 @@ static PyObject *
structseq_new_impl(PyTypeObject *type, PyObject *arg, PyObject *dict)
/*[clinic end generated code: output=baa082e788b171da input=90532511101aa3fb]*/
{
- PyObject *ob;
PyStructSequence *res = NULL;
Py_ssize_t len, min_len, max_len, i, n_unnamed_fields;
@@ -215,22 +227,34 @@ structseq_new_impl(PyTypeObject *type, PyObject *arg, PyObject *dict)
res->ob_item[i] = Py_NewRef(v);
}
Py_DECREF(arg);
- for (; i < max_len; ++i) {
- if (dict == NULL) {
- ob = Py_None;
- }
- else {
- ob = _PyDict_GetItemStringWithError(dict,
- type->tp_members[i-n_unnamed_fields].name);
+ if (dict != NULL && PyDict_GET_SIZE(dict) > 0) {
+ Py_ssize_t n_found_keys = 0;
+ for (i = len; i < max_len; ++i) {
+ PyObject *ob = NULL;
+ const char *name = type->tp_members[i - n_unnamed_fields].name;
+ if (PyDict_GetItemStringRef(dict, name, &ob) < 0) {
+ Py_DECREF(res);
+ return NULL;
+ }
if (ob == NULL) {
- if (PyErr_Occurred()) {
- Py_DECREF(res);
- return NULL;
- }
- ob = Py_None;
+ ob = Py_NewRef(Py_None);
+ }
+ else {
+ ++n_found_keys;
}
+ res->ob_item[i] = ob;
+ }
+ if (PyDict_GET_SIZE(dict) > n_found_keys) {
+ PyErr_Format(PyExc_TypeError,
+ "%.500s() got duplicate or unexpected field name(s)",
+ type->tp_name);
+ Py_DECREF(res);
+ return NULL;
+ }
+ } else {
+ for (i = len; i < max_len; ++i) {
+ res->ob_item[i] = Py_NewRef(Py_None);
}
- res->ob_item[i] = Py_NewRef(ob);
}
_PyObject_GC_TRACK(res);
@@ -298,7 +322,7 @@ structseq_repr(PyStructSequence *obj)
goto error;
}
- PyObject *value = PyStructSequence_GET_ITEM(obj, i);
+ PyObject *value = PyStructSequence_GetItem((PyObject*)obj, i);
assert(value != NULL);
PyObject *repr = PyObject_Repr(value);
if (repr == NULL) {
@@ -367,9 +391,84 @@ error:
return NULL;
}
+
+static PyObject *
+structseq_replace(PyStructSequence *self, PyObject *args, PyObject *kwargs)
+{
+ PyStructSequence *result = NULL;
+ Py_ssize_t n_fields, n_unnamed_fields, i;
+
+ if (!_PyArg_NoPositional("__replace__", args)) {
+ return NULL;
+ }
+
+ n_fields = REAL_SIZE(self);
+ if (n_fields < 0) {
+ return NULL;
+ }
+ n_unnamed_fields = UNNAMED_FIELDS(self);
+ if (n_unnamed_fields < 0) {
+ return NULL;
+ }
+ if (n_unnamed_fields > 0) {
+ PyErr_Format(PyExc_TypeError,
+ "__replace__() is not supported for %.500s "
+ "because it has unnamed field(s)",
+ Py_TYPE(self)->tp_name);
+ return NULL;
+ }
+
+ result = (PyStructSequence *) PyStructSequence_New(Py_TYPE(self));
+ if (!result) {
+ return NULL;
+ }
+
+ if (kwargs != NULL) {
+ // We do not support types with unnamed fields, so we can iterate over
+ // i >= n_visible_fields case without slicing with (i - n_unnamed_fields).
+ for (i = 0; i < n_fields; ++i) {
+ PyObject *ob;
+ if (PyDict_PopString(kwargs, Py_TYPE(self)->tp_members[i].name,
+ &ob) < 0) {
+ goto error;
+ }
+ if (ob == NULL) {
+ ob = Py_NewRef(self->ob_item[i]);
+ }
+ result->ob_item[i] = ob;
+ }
+ // Check if there are any unexpected fields.
+ if (PyDict_GET_SIZE(kwargs) > 0) {
+ PyObject *names = PyDict_Keys(kwargs);
+ if (names) {
+ PyErr_Format(PyExc_TypeError, "Got unexpected field name(s): %R", names);
+ Py_DECREF(names);
+ }
+ goto error;
+ }
+ }
+ else
+ {
+ // Just create a copy of the original.
+ for (i = 0; i < n_fields; ++i) {
+ result->ob_item[i] = Py_NewRef(self->ob_item[i]);
+ }
+ }
+
+ _PyObject_GC_TRACK(result);
+ return (PyObject *)result;
+
+error:
+ Py_DECREF(result);
+ return NULL;
+}
+
static PyMethodDef structseq_methods[] = {
{"__reduce__", (PyCFunction)structseq_reduce, METH_NOARGS, NULL},
- {NULL, NULL}
+ {"__replace__", _PyCFunction_CAST(structseq_replace), METH_VARARGS | METH_KEYWORDS,
+ PyDoc_STR("__replace__($self, /, **changes)\n--\n\n"
+ "Return a copy of the structure with new values for the specified fields.")},
+ {NULL, NULL} // sentinel
};
static Py_ssize_t
@@ -463,10 +562,10 @@ initialize_members(PyStructSequence_Desc *desc,
/* The names and docstrings in these MemberDefs are statically */
/* allocated so it is expected that they'll outlive the MemberDef */
members[k].name = desc->fields[i].name;
- members[k].type = T_OBJECT;
+ members[k].type = _Py_T_OBJECT;
members[k].offset = offsetof(PyStructSequence, ob_item)
+ i * sizeof(PyObject*);
- members[k].flags = READONLY;
+ members[k].flags = Py_READONLY;
members[k].doc = desc->fields[i].doc;
k++;
}
@@ -522,6 +621,9 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp,
PyStructSequence_Desc *desc,
unsigned long tp_flags)
{
+ if (Py_TYPE(type) == NULL) {
+ Py_SET_TYPE(type, &PyType_Type);
+ }
Py_ssize_t n_unnamed_members;
Py_ssize_t n_members = count_members(desc, &n_unnamed_members);
PyMemberDef *members = NULL;
@@ -537,7 +639,7 @@ _PyStructSequence_InitBuiltinWithFlags(PyInterpreterState *interp,
}
initialize_static_fields(type, desc, members, n_members, tp_flags);
- _Py_SetImmortal(type);
+ _Py_SetImmortal((PyObject *)type);
}
#ifndef NDEBUG
else {
@@ -579,9 +681,10 @@ PyStructSequence_InitType2(PyTypeObject *type, PyStructSequence_Desc *desc)
Py_ssize_t n_members, n_unnamed_members;
#ifdef Py_TRACE_REFS
- /* if the type object was chained, unchain it first
+ /* if the type object was traced, remove it first
before overwriting its storage */
- if (type->ob_base.ob_base._ob_next) {
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (_PyRefchain_IsTraced(interp, (PyObject *)type)) {
_Py_ForgetReference((PyObject *)type);
}
#endif
@@ -623,7 +726,7 @@ _PyStructSequence_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type)
assert(type->tp_name != NULL);
assert(type->tp_base == &PyTuple_Type);
assert((type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
- assert(_Py_IsImmortal(type));
+ assert(_Py_IsImmortalLoose(type));
// Cannot delete a type if it still has subclasses
if (_PyType_HasSubclasses(type)) {
@@ -631,7 +734,7 @@ _PyStructSequence_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type)
return;
}
- _PyStaticType_Dealloc(interp, type);
+ _PyStaticType_FiniBuiltin(interp, type);
if (_Py_IsMainInterpreter(interp)) {
// Undo _PyStructSequence_InitBuiltinWithFlags().
diff --git a/contrib/tools/python3/Objects/tupleobject.c b/contrib/tools/python3/Objects/tupleobject.c
index 918654fae89..818814b663d 100644
--- a/contrib/tools/python3/Objects/tupleobject.c
+++ b/contrib/tools/python3/Objects/tupleobject.c
@@ -3,9 +3,11 @@
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_gc.h" // _PyObject_GC_IS_TRACKED()
#include "pycore_initconfig.h" // _PyStatus_OK()
-#include "pycore_object.h" // _PyObject_GC_TRACK(), _Py_FatalRefcountError()
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
+#include "pycore_object.h" // _PyObject_GC_TRACK(), _Py_FatalRefcountError(), _PyDebugAllocatorStats()
/*[clinic input]
class tuple "PyTupleObject *" "&PyTuple_Type"
@@ -61,7 +63,7 @@ tuple_alloc(Py_ssize_t size)
static inline PyObject *
tuple_get_empty(void)
{
- return Py_NewRef(&_Py_SINGLETON(tuple_empty));
+ return (PyObject *)&_Py_SINGLETON(tuple_empty);
}
PyObject *
@@ -940,11 +942,12 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
for (i = newsize; i < oldsize; i++) {
Py_CLEAR(v->ob_item[i]);
}
+ _PyReftracerTrack((PyObject *)v, PyRefTracer_DESTROY);
sv = PyObject_GC_Resize(PyTupleObject, v, newsize);
if (sv == NULL) {
*pv = NULL;
#ifdef Py_REF_DEBUG
- _Py_DecRefTotal(_PyInterpreterState_GET());
+ _Py_DecRefTotal(_PyThreadState_GET());
#endif
PyObject_GC_Del(v);
return -1;
@@ -960,18 +963,13 @@ _PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
}
-static void maybe_freelist_clear(PyInterpreterState *, int);
+static void maybe_freelist_clear(struct _Py_object_freelists *, int);
-void
-_PyTuple_Fini(PyInterpreterState *interp)
-{
- maybe_freelist_clear(interp, 1);
-}
void
-_PyTuple_ClearFreeList(PyInterpreterState *interp)
+_PyTuple_ClearFreeList(struct _Py_object_freelists *freelists, int is_finalization)
{
- maybe_freelist_clear(interp, 0);
+ maybe_freelist_clear(freelists, is_finalization);
}
/*********************** Tuple Iterator **************************/
@@ -1123,30 +1121,26 @@ tuple_iter(PyObject *seq)
* freelists *
*************/
-#define STATE (interp->tuple)
-#define FREELIST_FINALIZED (STATE.numfree[0] < 0)
+#define TUPLE_FREELIST (freelists->tuples)
+#define FREELIST_FINALIZED (TUPLE_FREELIST.numfree[0] < 0)
static inline PyTupleObject *
maybe_freelist_pop(Py_ssize_t size)
{
-#if PyTuple_NFREELISTS > 0
- PyInterpreterState *interp = _PyInterpreterState_GET();
-#ifdef Py_DEBUG
- /* maybe_freelist_pop() must not be called after maybe_freelist_fini(). */
- assert(!FREELIST_FINALIZED);
-#endif
+#ifdef WITH_FREELISTS
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
if (size == 0) {
return NULL;
}
assert(size > 0);
if (size <= PyTuple_MAXSAVESIZE) {
Py_ssize_t index = size - 1;
- PyTupleObject *op = STATE.free_list[index];
+ PyTupleObject *op = TUPLE_FREELIST.items[index];
if (op != NULL) {
/* op is the head of a linked list, with the first item
pointing to the next node. Here we pop off the old head. */
- STATE.free_list[index] = (PyTupleObject *) op->ob_item[0];
- STATE.numfree[index]--;
+ TUPLE_FREELIST.items[index] = (PyTupleObject *) op->ob_item[0];
+ TUPLE_FREELIST.numfree[index]--;
/* Inlined _PyObject_InitVar() without _PyType_HasFeature() test */
#ifdef Py_TRACE_REFS
/* maybe_freelist_push() ensures these were already set. */
@@ -1167,25 +1161,22 @@ maybe_freelist_pop(Py_ssize_t size)
static inline int
maybe_freelist_push(PyTupleObject *op)
{
-#if PyTuple_NFREELISTS > 0
- PyInterpreterState *interp = _PyInterpreterState_GET();
-#ifdef Py_DEBUG
- /* maybe_freelist_push() must not be called after maybe_freelist_fini(). */
- assert(!FREELIST_FINALIZED);
-#endif
+#ifdef WITH_FREELISTS
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
if (Py_SIZE(op) == 0) {
return 0;
}
Py_ssize_t index = Py_SIZE(op) - 1;
if (index < PyTuple_NFREELISTS
- && STATE.numfree[index] < PyTuple_MAXFREELIST
+ && TUPLE_FREELIST.numfree[index] < PyTuple_MAXFREELIST
+ && TUPLE_FREELIST.numfree[index] >= 0
&& Py_IS_TYPE(op, &PyTuple_Type))
{
/* op is the head of a linked list, with the first item
pointing to the next node. Here we set op as the new head. */
- op->ob_item[0] = (PyObject *) STATE.free_list[index];
- STATE.free_list[index] = op;
- STATE.numfree[index]++;
+ op->ob_item[0] = (PyObject *) TUPLE_FREELIST.items[index];
+ TUPLE_FREELIST.items[index] = op;
+ TUPLE_FREELIST.numfree[index]++;
OBJECT_STAT_INC(to_freelist);
return 1;
}
@@ -1194,13 +1185,13 @@ maybe_freelist_push(PyTupleObject *op)
}
static void
-maybe_freelist_clear(PyInterpreterState *interp, int fini)
+maybe_freelist_clear(struct _Py_object_freelists *freelists, int fini)
{
-#if PyTuple_NFREELISTS > 0
+#ifdef WITH_FREELISTS
for (Py_ssize_t i = 0; i < PyTuple_NFREELISTS; i++) {
- PyTupleObject *p = STATE.free_list[i];
- STATE.free_list[i] = NULL;
- STATE.numfree[i] = fini ? -1 : 0;
+ PyTupleObject *p = TUPLE_FREELIST.items[i];
+ TUPLE_FREELIST.items[i] = NULL;
+ TUPLE_FREELIST.numfree[i] = fini ? -1 : 0;
while (p) {
PyTupleObject *q = p;
p = (PyTupleObject *)(p->ob_item[0]);
@@ -1214,14 +1205,14 @@ maybe_freelist_clear(PyInterpreterState *interp, int fini)
void
_PyTuple_DebugMallocStats(FILE *out)
{
-#if PyTuple_NFREELISTS > 0
- PyInterpreterState *interp = _PyInterpreterState_GET();
+#ifdef WITH_FREELISTS
+ struct _Py_object_freelists *freelists = _Py_object_freelists_GET();
for (int i = 0; i < PyTuple_NFREELISTS; i++) {
int len = i + 1;
char buf[128];
PyOS_snprintf(buf, sizeof(buf),
"free %d-sized PyTupleObject", len);
- _PyDebugAllocatorStats(out, buf, STATE.numfree[i],
+ _PyDebugAllocatorStats(out, buf, TUPLE_FREELIST.numfree[i],
_PyObject_VAR_SIZE(&PyTuple_Type, len));
}
#endif
diff --git a/contrib/tools/python3/Objects/typeobject.c b/contrib/tools/python3/Objects/typeobject.c
index 012920fcf83..098f0a34c6e 100644
--- a/contrib/tools/python3/Objects/typeobject.c
+++ b/contrib/tools/python3/Objects/typeobject.c
@@ -1,24 +1,26 @@
/* Type object implementation */
#include "Python.h"
-#include "pycore_call.h"
+#include "pycore_abstract.h" // _PySequence_IterSearch()
+#include "pycore_call.h" // _PyObject_VectorcallTstate()
#include "pycore_code.h" // CO_FAST_FREE
-#include "pycore_symtable.h" // _Py_Mangle()
#include "pycore_dict.h" // _PyDict_KeysSize()
-#include "pycore_initconfig.h" // _PyStatus_OK()
+#include "pycore_frame.h" // _PyInterpreterFrame
+#include "pycore_lock.h" // _PySeqLock_*
+#include "pycore_long.h" // _PyLong_IsNegative()
#include "pycore_memoryobject.h" // _PyMemoryView_FromBufferProc()
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_HasFeature()
-#include "pycore_long.h" // _PyLong_IsNegative()
+#include "pycore_object_alloc.h" // _PyObject_MallocWithType()
#include "pycore_pyerrors.h" // _PyErr_Occurred()
#include "pycore_pystate.h" // _PyThreadState_GET()
+#include "pycore_symtable.h" // _Py_Mangle()
#include "pycore_typeobject.h" // struct type_cache
#include "pycore_unionobject.h" // _Py_union_type_or
-#include "pycore_frame.h" // _PyInterpreterFrame
+#include "pycore_weakref.h" // _PyWeakref_GET_REF()
#include "opcode.h" // MAKE_CELL
-#include "structmember.h" // PyMemberDef
-#include <ctype.h>
#include <stddef.h> // ptrdiff_t
/*[clinic input]
@@ -41,7 +43,8 @@ class object "PyObject *" "&PyBaseObject_Type"
& ((1 << MCACHE_SIZE_EXP) - 1))
#define MCACHE_HASH_METHOD(type, name) \
- MCACHE_HASH((type)->tp_version_tag, ((Py_ssize_t)(name)) >> 3)
+ MCACHE_HASH(FT_ATOMIC_LOAD_UINT32_RELAXED((type)->tp_version_tag), \
+ ((Py_ssize_t)(name)) >> 3)
#define MCACHE_CACHEABLE_NAME(name) \
PyUnicode_CheckExact(name) && \
PyUnicode_IS_READY(name) && \
@@ -51,6 +54,36 @@ class object "PyObject *" "&PyBaseObject_Type"
#define NEXT_VERSION_TAG(interp) \
(interp)->types.next_version_tag
+#ifdef Py_GIL_DISABLED
+
+// There's a global lock for mutation of types. This avoids having to take
+// additional locks while doing various subclass processing which may result
+// in odd behaviors w.r.t. running with the GIL as the outer type lock could
+// be released and reacquired during a subclass update if there's contention
+// on the subclass lock.
+#define TYPE_LOCK &PyInterpreterState_Get()->types.mutex
+#define BEGIN_TYPE_LOCK() Py_BEGIN_CRITICAL_SECTION_MUT(TYPE_LOCK)
+#define END_TYPE_LOCK() Py_END_CRITICAL_SECTION()
+
+#define BEGIN_TYPE_DICT_LOCK(d) \
+ Py_BEGIN_CRITICAL_SECTION2_MUT(TYPE_LOCK, &_PyObject_CAST(d)->ob_mutex)
+
+#define END_TYPE_DICT_LOCK() Py_END_CRITICAL_SECTION2()
+
+#define ASSERT_TYPE_LOCK_HELD() \
+ _Py_CRITICAL_SECTION_ASSERT_MUTEX_LOCKED(TYPE_LOCK)
+
+#else
+
+#define BEGIN_TYPE_LOCK()
+#define END_TYPE_LOCK()
+#define BEGIN_TYPE_DICT_LOCK(d)
+#define END_TYPE_DICT_LOCK()
+#define ASSERT_TYPE_LOCK_HELD()
+
+#endif
+
+
typedef struct PySlot_Offset {
short subslot_offset;
short slot_offset;
@@ -75,102 +108,213 @@ slot_tp_setattro(PyObject *self, PyObject *name, PyObject *value);
static inline PyTypeObject *
type_from_ref(PyObject *ref)
{
- assert(PyWeakref_CheckRef(ref));
- PyObject *obj = PyWeakref_GET_OBJECT(ref); // borrowed ref
- assert(obj != NULL);
- if (obj == Py_None) {
+ PyObject *obj = _PyWeakref_GET_REF(ref);
+ if (obj == NULL) {
return NULL;
}
- assert(PyType_Check(obj));
return _PyType_CAST(obj);
}
/* helpers for for static builtin types */
+#ifndef NDEBUG
static inline int
-static_builtin_index_is_set(PyTypeObject *self)
+managed_static_type_index_is_set(PyTypeObject *self)
{
return self->tp_subclasses != NULL;
}
+#endif
static inline size_t
-static_builtin_index_get(PyTypeObject *self)
+managed_static_type_index_get(PyTypeObject *self)
{
- assert(static_builtin_index_is_set(self));
+ assert(managed_static_type_index_is_set(self));
/* We store a 1-based index so 0 can mean "not initialized". */
return (size_t)self->tp_subclasses - 1;
}
static inline void
-static_builtin_index_set(PyTypeObject *self, size_t index)
+managed_static_type_index_set(PyTypeObject *self, size_t index)
{
- assert(index < _Py_MAX_STATIC_BUILTIN_TYPES);
+ assert(index < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES);
/* We store a 1-based index so 0 can mean "not initialized". */
self->tp_subclasses = (PyObject *)(index + 1);
}
static inline void
-static_builtin_index_clear(PyTypeObject *self)
+managed_static_type_index_clear(PyTypeObject *self)
{
self->tp_subclasses = NULL;
}
+static PyTypeObject *
+static_ext_type_lookup(PyInterpreterState *interp, size_t index,
+ int64_t *p_interp_count)
+{
+ assert(interp->runtime == &_PyRuntime);
+ assert(index < _Py_MAX_MANAGED_STATIC_EXT_TYPES);
+
+ size_t full_index = index + _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES;
+ int64_t interp_count =
+ _PyRuntime.types.managed_static.types[full_index].interp_count;
+ assert((interp_count == 0) ==
+ (_PyRuntime.types.managed_static.types[full_index].type == NULL));
+ *p_interp_count = interp_count;
+
+ PyTypeObject *type = interp->types.for_extensions.initialized[index].type;
+ if (type == NULL) {
+ return NULL;
+ }
+ assert(!interp->types.for_extensions.initialized[index].isbuiltin);
+ assert(type == _PyRuntime.types.managed_static.types[full_index].type);
+ assert(managed_static_type_index_is_set(type));
+ return type;
+}
-static inline static_builtin_state *
-static_builtin_state_get(PyInterpreterState *interp, PyTypeObject *self)
+static managed_static_type_state *
+managed_static_type_state_get(PyInterpreterState *interp, PyTypeObject *self)
{
- return &(interp->types.builtins[static_builtin_index_get(self)]);
+ // It's probably a builtin type.
+ size_t index = managed_static_type_index_get(self);
+ managed_static_type_state *state =
+ &(interp->types.builtins.initialized[index]);
+ if (state->type == self) {
+ return state;
+ }
+ if (index > _Py_MAX_MANAGED_STATIC_EXT_TYPES) {
+ return state;
+ }
+ return &(interp->types.for_extensions.initialized[index]);
}
/* For static types we store some state in an array on each interpreter. */
-static_builtin_state *
+managed_static_type_state *
_PyStaticType_GetState(PyInterpreterState *interp, PyTypeObject *self)
{
assert(self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
- return static_builtin_state_get(interp, self);
+ return managed_static_type_state_get(interp, self);
}
/* Set the type's per-interpreter state. */
static void
-static_builtin_state_init(PyInterpreterState *interp, PyTypeObject *self)
+managed_static_type_state_init(PyInterpreterState *interp, PyTypeObject *self,
+ int isbuiltin, int initial)
{
- if (!static_builtin_index_is_set(self)) {
- static_builtin_index_set(self, interp->types.num_builtins_initialized);
+ assert(interp->runtime == &_PyRuntime);
+
+ size_t index;
+ if (initial) {
+ assert(!managed_static_type_index_is_set(self));
+ if (isbuiltin) {
+ index = interp->types.builtins.num_initialized;
+ assert(index < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES);
+ }
+ else {
+ PyMutex_Lock(&interp->types.mutex);
+ index = interp->types.for_extensions.next_index;
+ interp->types.for_extensions.next_index++;
+ PyMutex_Unlock(&interp->types.mutex);
+ assert(index < _Py_MAX_MANAGED_STATIC_EXT_TYPES);
+ }
+ managed_static_type_index_set(self, index);
}
- static_builtin_state *state = static_builtin_state_get(interp, self);
+ else {
+ index = managed_static_type_index_get(self);
+ if (isbuiltin) {
+ assert(index == interp->types.builtins.num_initialized);
+ assert(index < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES);
+ }
+ else {
+ assert(index < _Py_MAX_MANAGED_STATIC_EXT_TYPES);
+ }
+ }
+ size_t full_index = isbuiltin
+ ? index
+ : index + _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES;
- /* It should only be called once for each builtin type. */
+ assert((initial == 1) ==
+ (_PyRuntime.types.managed_static.types[full_index].interp_count == 0));
+ (void)_Py_atomic_add_int64(
+ &_PyRuntime.types.managed_static.types[full_index].interp_count, 1);
+
+ if (initial) {
+ assert(_PyRuntime.types.managed_static.types[full_index].type == NULL);
+ _PyRuntime.types.managed_static.types[full_index].type = self;
+ }
+ else {
+ assert(_PyRuntime.types.managed_static.types[full_index].type == self);
+ }
+
+ managed_static_type_state *state = isbuiltin
+ ? &(interp->types.builtins.initialized[index])
+ : &(interp->types.for_extensions.initialized[index]);
+
+ /* It should only be called once for each builtin type per interpreter. */
assert(state->type == NULL);
state->type = self;
+ state->isbuiltin = isbuiltin;
/* state->tp_subclasses is left NULL until init_subclasses() sets it. */
/* state->tp_weaklist is left NULL until insert_head() or insert_after()
(in weakrefobject.c) sets it. */
- interp->types.num_builtins_initialized++;
+ if (isbuiltin) {
+ interp->types.builtins.num_initialized++;
+ }
+ else {
+ interp->types.for_extensions.num_initialized++;
+ }
}
/* Reset the type's per-interpreter state.
- This basically undoes what static_builtin_state_init() did. */
+ This basically undoes what managed_static_type_state_init() did. */
static void
-static_builtin_state_clear(PyInterpreterState *interp, PyTypeObject *self)
+managed_static_type_state_clear(PyInterpreterState *interp, PyTypeObject *self,
+ int isbuiltin, int final)
{
- static_builtin_state *state = static_builtin_state_get(interp, self);
+ size_t index = managed_static_type_index_get(self);
+ size_t full_index = isbuiltin
+ ? index
+ : index + _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES;
+
+ managed_static_type_state *state = isbuiltin
+ ? &(interp->types.builtins.initialized[index])
+ : &(interp->types.for_extensions.initialized[index]);
+ assert(state != NULL);
+
+ assert(_PyRuntime.types.managed_static.types[full_index].interp_count > 0);
+ assert(_PyRuntime.types.managed_static.types[full_index].type == state->type);
assert(state->type != NULL);
state->type = NULL;
assert(state->tp_weaklist == NULL); // It was already cleared out.
- if (_Py_IsMainInterpreter(interp)) {
- static_builtin_index_clear(self);
+ (void)_Py_atomic_add_int64(
+ &_PyRuntime.types.managed_static.types[full_index].interp_count, -1);
+ if (final) {
+ assert(!_PyRuntime.types.managed_static.types[full_index].interp_count);
+ _PyRuntime.types.managed_static.types[full_index].type = NULL;
+
+ managed_static_type_index_clear(self);
}
- assert(interp->types.num_builtins_initialized > 0);
- interp->types.num_builtins_initialized--;
+ if (isbuiltin) {
+ assert(interp->types.builtins.num_initialized > 0);
+ interp->types.builtins.num_initialized--;
+ }
+ else {
+ PyMutex_Lock(&interp->types.mutex);
+ assert(interp->types.for_extensions.num_initialized > 0);
+ interp->types.for_extensions.num_initialized--;
+ if (interp->types.for_extensions.num_initialized == 0) {
+ interp->types.for_extensions.next_index = 0;
+ }
+ PyMutex_Unlock(&interp->types.mutex);
+ }
}
-// Also see _PyStaticType_InitBuiltin() and _PyStaticType_Dealloc().
+// Also see _PyStaticType_InitBuiltin() and _PyStaticType_FiniBuiltin().
/* end static builtin helpers */
@@ -180,7 +324,7 @@ start_readying(PyTypeObject *type)
{
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = static_builtin_state_get(interp, type);
+ managed_static_type_state *state = managed_static_type_state_get(interp, type);
assert(state != NULL);
assert(!state->readying);
state->readying = 1;
@@ -195,7 +339,7 @@ stop_readying(PyTypeObject *type)
{
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = static_builtin_state_get(interp, type);
+ managed_static_type_state *state = managed_static_type_state_get(interp, type);
assert(state != NULL);
assert(state->readying);
state->readying = 0;
@@ -210,7 +354,7 @@ is_readying(PyTypeObject *type)
{
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = static_builtin_state_get(interp, type);
+ managed_static_type_state *state = managed_static_type_state_get(interp, type);
assert(state != NULL);
return state->readying;
}
@@ -225,7 +369,7 @@ lookup_tp_dict(PyTypeObject *self)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
+ managed_static_type_state *state = _PyStaticType_GetState(interp, self);
assert(state != NULL);
return state->tp_dict;
}
@@ -251,7 +395,7 @@ set_tp_dict(PyTypeObject *self, PyObject *dict)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
+ managed_static_type_state *state = _PyStaticType_GetState(interp, self);
assert(state != NULL);
state->tp_dict = dict;
return;
@@ -264,7 +408,7 @@ clear_tp_dict(PyTypeObject *self)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
+ managed_static_type_state *state = _PyStaticType_GetState(interp, self);
assert(state != NULL);
Py_CLEAR(state->tp_dict);
return;
@@ -282,18 +426,25 @@ lookup_tp_bases(PyTypeObject *self)
PyObject *
_PyType_GetBases(PyTypeObject *self)
{
- /* It returns a borrowed reference. */
- return lookup_tp_bases(self);
+ PyObject *res;
+
+ BEGIN_TYPE_LOCK();
+ res = lookup_tp_bases(self);
+ Py_INCREF(res);
+ END_TYPE_LOCK();
+
+ return res;
}
static inline void
-set_tp_bases(PyTypeObject *self, PyObject *bases)
+set_tp_bases(PyTypeObject *self, PyObject *bases, int initial)
{
- assert(PyTuple_CheckExact(bases));
+ assert(PyTuple_Check(bases));
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
// XXX tp_bases can probably be statically allocated for each
// static builtin type.
- assert(_Py_IsMainInterpreter(_PyInterpreterState_GET()));
+ assert(PyTuple_CheckExact(bases));
+ assert(initial);
assert(self->tp_bases == NULL);
if (PyTuple_GET_SIZE(bases) == 0) {
assert(self->tp_base == NULL);
@@ -302,7 +453,7 @@ set_tp_bases(PyTypeObject *self, PyObject *bases)
assert(PyTuple_GET_SIZE(bases) == 1);
assert(PyTuple_GET_ITEM(bases, 0) == (PyObject *)self->tp_base);
assert(self->tp_base->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
- assert(_Py_IsImmortal(self->tp_base));
+ assert(_Py_IsImmortalLoose(self->tp_base));
}
_Py_SetImmortal(bases);
}
@@ -310,16 +461,16 @@ set_tp_bases(PyTypeObject *self, PyObject *bases)
}
static inline void
-clear_tp_bases(PyTypeObject *self)
+clear_tp_bases(PyTypeObject *self, int final)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
+ if (final) {
if (self->tp_bases != NULL) {
if (PyTuple_GET_SIZE(self->tp_bases) == 0) {
Py_CLEAR(self->tp_bases);
}
else {
- assert(_Py_IsImmortal(self->tp_bases));
+ assert(_Py_IsImmortalLoose(self->tp_bases));
_Py_ClearImmortal(self->tp_bases);
}
}
@@ -333,24 +484,40 @@ clear_tp_bases(PyTypeObject *self)
static inline PyObject *
lookup_tp_mro(PyTypeObject *self)
{
+ ASSERT_TYPE_LOCK_HELD();
return self->tp_mro;
}
PyObject *
_PyType_GetMRO(PyTypeObject *self)
{
- /* It returns a borrowed reference. */
- return lookup_tp_mro(self);
+#ifdef Py_GIL_DISABLED
+ PyObject *mro = _Py_atomic_load_ptr_relaxed(&self->tp_mro);
+ if (mro == NULL) {
+ return NULL;
+ }
+ if (_Py_TryIncrefCompare(&self->tp_mro, mro)) {
+ return mro;
+ }
+
+ BEGIN_TYPE_LOCK();
+ mro = lookup_tp_mro(self);
+ Py_XINCREF(mro);
+ END_TYPE_LOCK();
+ return mro;
+#else
+ return Py_XNewRef(lookup_tp_mro(self));
+#endif
}
static inline void
-set_tp_mro(PyTypeObject *self, PyObject *mro)
+set_tp_mro(PyTypeObject *self, PyObject *mro, int initial)
{
assert(PyTuple_CheckExact(mro));
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
// XXX tp_mro can probably be statically allocated for each
// static builtin type.
- assert(_Py_IsMainInterpreter(_PyInterpreterState_GET()));
+ assert(initial);
assert(self->tp_mro == NULL);
/* Other checks are done via set_tp_bases. */
_Py_SetImmortal(mro);
@@ -359,16 +526,16 @@ set_tp_mro(PyTypeObject *self, PyObject *mro)
}
static inline void
-clear_tp_mro(PyTypeObject *self)
+clear_tp_mro(PyTypeObject *self, int final)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- if (_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
+ if (final) {
if (self->tp_mro != NULL) {
if (PyTuple_GET_SIZE(self->tp_mro) == 0) {
Py_CLEAR(self->tp_mro);
}
else {
- assert(_Py_IsImmortal(self->tp_mro));
+ assert(_Py_IsImmortalLoose(self->tp_mro));
_Py_ClearImmortal(self->tp_mro);
}
}
@@ -388,7 +555,7 @@ init_tp_subclasses(PyTypeObject *self)
}
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
+ managed_static_type_state *state = _PyStaticType_GetState(interp, self);
state->tp_subclasses = subclasses;
return subclasses;
}
@@ -404,7 +571,7 @@ clear_tp_subclasses(PyTypeObject *self)
has no subclass. */
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
+ managed_static_type_state *state = _PyStaticType_GetState(interp, self);
Py_CLEAR(state->tp_subclasses);
return;
}
@@ -416,7 +583,7 @@ lookup_tp_subclasses(PyTypeObject *self)
{
if (self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
PyInterpreterState *interp = _PyInterpreterState_GET();
- static_builtin_state *state = _PyStaticType_GetState(interp, self);
+ managed_static_type_state *state = _PyStaticType_GetState(interp, self);
assert(state != NULL);
return state->tp_subclasses;
}
@@ -458,15 +625,17 @@ _PyType_GetSubclasses(PyTypeObject *self)
Py_ssize_t i = 0;
PyObject *ref; // borrowed ref
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
- PyTypeObject *subclass = type_from_ref(ref); // borrowed
+ PyTypeObject *subclass = type_from_ref(ref);
if (subclass == NULL) {
continue;
}
if (PyList_Append(list, _PyObject_CAST(subclass)) < 0) {
Py_DECREF(list);
+ Py_DECREF(subclass);
return NULL;
}
+ Py_DECREF(subclass);
}
return list;
}
@@ -586,8 +755,29 @@ _PyType_GetDocFromInternalDoc(const char *name, const char *internal_doc)
return PyUnicode_FromString(doc);
}
+static const char *
+signature_from_flags(int flags)
+{
+ switch (flags & ~METH_COEXIST) {
+ case METH_NOARGS:
+ return "($self, /)";
+ case METH_NOARGS|METH_CLASS:
+ return "($type, /)";
+ case METH_NOARGS|METH_STATIC:
+ return "()";
+ case METH_O:
+ return "($self, object, /)";
+ case METH_O|METH_CLASS:
+ return "($type, object, /)";
+ case METH_O|METH_STATIC:
+ return "(object, /)";
+ default:
+ return NULL;
+ }
+}
+
PyObject *
-_PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_doc)
+_PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_doc, int flags)
{
const char *start = find_signature(name, internal_doc);
const char *end;
@@ -597,6 +787,10 @@ _PyType_GetTextSignatureFromInternalDoc(const char *name, const char *internal_d
else
end = NULL;
if (!end) {
+ start = signature_from_flags(flags);
+ if (start) {
+ return PyUnicode_FromString(start);
+ }
Py_RETURN_NONE;
}
@@ -622,9 +816,15 @@ type_cache_clear(struct type_cache *cache, PyObject *value)
{
for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
struct type_cache_entry *entry = &cache->hashtable[i];
+#ifdef Py_GIL_DISABLED
+ _PySeqLock_LockWrite(&entry->sequence);
+#endif
entry->version = 0;
Py_XSETREF(entry->name, _Py_XNewRef(value));
entry->value = NULL;
+#ifdef Py_GIL_DISABLED
+ _PySeqLock_UnlockWrite(&entry->sequence);
+#endif
}
}
@@ -638,7 +838,7 @@ _PyType_InitCache(PyInterpreterState *interp)
assert(entry->name == NULL);
entry->version = 0;
- // Set to None so _PyType_Lookup() can use Py_SETREF(),
+ // Set to None so _PyType_LookupRef() can use Py_SETREF(),
// rather than using slower Py_XSETREF().
entry->name = Py_None;
entry->value = NULL;
@@ -650,7 +850,7 @@ static unsigned int
_PyType_ClearCache(PyInterpreterState *interp)
{
struct type_cache *cache = &interp->types.type_cache;
- // Set to None, rather than NULL, so _PyType_Lookup() can
+ // Set to None, rather than NULL, so _PyType_LookupRef() can
// use Py_SETREF() rather than using slower Py_XSETREF().
type_cache_clear(cache, Py_None);
@@ -672,10 +872,14 @@ _PyTypes_Fini(PyInterpreterState *interp)
struct type_cache *cache = &interp->types.type_cache;
type_cache_clear(cache, NULL);
- assert(interp->types.num_builtins_initialized == 0);
- // All the static builtin types should have been finalized already.
- for (size_t i = 0; i < _Py_MAX_STATIC_BUILTIN_TYPES; i++) {
- assert(interp->types.builtins[i].type == NULL);
+ // All the managed static types should have been finalized already.
+ assert(interp->types.for_extensions.num_initialized == 0);
+ for (size_t i = 0; i < _Py_MAX_MANAGED_STATIC_EXT_TYPES; i++) {
+ assert(interp->types.for_extensions.initialized[i].type == NULL);
+ }
+ assert(interp->types.builtins.num_initialized == 0);
+ for (size_t i = 0; i < _Py_MAX_MANAGED_STATIC_BUILTIN_TYPES; i++) {
+ assert(interp->types.builtins.initialized[i].type == NULL);
}
}
@@ -736,8 +940,10 @@ PyType_Watch(int watcher_id, PyObject* obj)
return -1;
}
// ensure we will get a callback on the next modification
+ BEGIN_TYPE_LOCK();
assign_version_tag(interp, type);
type->tp_watched |= (1 << watcher_id);
+ END_TYPE_LOCK();
return 0;
}
@@ -757,8 +963,25 @@ PyType_Unwatch(int watcher_id, PyObject* obj)
return 0;
}
-void
-PyType_Modified(PyTypeObject *type)
+static void
+set_version_unlocked(PyTypeObject *tp, unsigned int version)
+{
+ ASSERT_TYPE_LOCK_HELD();
+ assert(version == 0 || (tp->tp_versions_used != _Py_ATTR_CACHE_UNUSED));
+#ifndef Py_GIL_DISABLED
+ if (version) {
+ tp->tp_versions_used++;
+ }
+#else
+ if (version) {
+ _Py_atomic_add_uint16(&tp->tp_versions_used, 1);
+ }
+#endif
+ FT_ATOMIC_STORE_UINT_RELAXED(tp->tp_version_tag, version);
+}
+
+static void
+type_modified_unlocked(PyTypeObject *type)
{
/* Invalidate any cached data for the specified type and all
subclasses. This function is called after the base
@@ -766,18 +989,21 @@ PyType_Modified(PyTypeObject *type)
Invariants:
- - before Py_TPFLAGS_VALID_VERSION_TAG can be set on a type,
+ - before tp_version_tag can be set on a type,
it must first be set on all super types.
- This function clears the Py_TPFLAGS_VALID_VERSION_TAG of a
+ This function clears the tp_version_tag of a
type (so it must first clear it on all subclasses). The
- tp_version_tag value is meaningless unless this flag is set.
+ tp_version_tag value is meaningless when equal to zero.
We don't assign new version tags eagerly, but only as
needed.
*/
- if (!_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
+ ASSERT_TYPE_LOCK_HELD();
+ if (type->tp_version_tag == 0) {
return;
}
+ // Cannot modify static builtin types.
+ assert((type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) == 0);
PyObject *subclasses = lookup_tp_subclasses(type);
if (subclasses != NULL) {
@@ -786,11 +1012,12 @@ PyType_Modified(PyTypeObject *type)
Py_ssize_t i = 0;
PyObject *ref;
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
- PyTypeObject *subclass = type_from_ref(ref); // borrowed
+ PyTypeObject *subclass = type_from_ref(ref);
if (subclass == NULL) {
continue;
}
- PyType_Modified(subclass);
+ type_modified_unlocked(subclass);
+ Py_DECREF(subclass);
}
}
@@ -804,7 +1031,9 @@ PyType_Modified(PyTypeObject *type)
if (bits & 1) {
PyType_WatchCallback cb = interp->type_watchers[i];
if (cb && (cb(type) < 0)) {
- PyErr_WriteUnraisable((PyObject *)type);
+ PyErr_FormatUnraisable(
+ "Exception ignored in type watcher callback #%d for %R",
+ i, type);
}
}
i++;
@@ -812,15 +1041,31 @@ PyType_Modified(PyTypeObject *type)
}
}
- type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
- type->tp_version_tag = 0; /* 0 is not a valid version tag */
+ set_version_unlocked(type, 0); /* 0 is not a valid version tag */
if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
// This field *must* be invalidated if the type is modified (see the
// comment on struct _specialization_cache):
- ((PyHeapTypeObject *)type)->_spec_cache.getitem = NULL;
+ FT_ATOMIC_STORE_PTR_RELAXED(
+ ((PyHeapTypeObject *)type)->_spec_cache.getitem, NULL);
+ }
+}
+
+void
+PyType_Modified(PyTypeObject *type)
+{
+ // Quick check without the lock held
+ if (FT_ATOMIC_LOAD_UINT_RELAXED(type->tp_version_tag) == 0) {
+ return;
}
+
+ BEGIN_TYPE_LOCK();
+ type_modified_unlocked(type);
+ END_TYPE_LOCK();
}
+static int
+is_subtype_with_mro(PyObject *a_mro, PyTypeObject *a, PyTypeObject *b);
+
static void
type_mro_modified(PyTypeObject *type, PyObject *bases) {
/*
@@ -839,6 +1084,7 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
int custom = !Py_IS_TYPE(type, &PyType_Type);
int unbound;
+ ASSERT_TYPE_LOCK_HELD();
if (custom) {
PyObject *mro_meth, *type_mro_meth;
mro_meth = lookup_maybe_method(
@@ -864,7 +1110,11 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
PyObject *b = PyTuple_GET_ITEM(bases, i);
PyTypeObject *cls = _PyType_CAST(b);
- if (!PyType_IsSubtype(type, cls)) {
+ if (cls->tp_versions_used >= _Py_ATTR_CACHE_UNUSED) {
+ goto clear;
+ }
+
+ if (!is_subtype_with_mro(lookup_tp_mro(type), type, cls)) {
goto clear;
}
}
@@ -872,37 +1122,56 @@ type_mro_modified(PyTypeObject *type, PyObject *bases) {
clear:
assert(!(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
- type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
- type->tp_version_tag = 0; /* 0 is not a valid version tag */
+ set_version_unlocked(type, 0); /* 0 is not a valid version tag */
+ type->tp_versions_used = _Py_ATTR_CACHE_UNUSED;
if (PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
// This field *must* be invalidated if the type is modified (see the
// comment on struct _specialization_cache):
- ((PyHeapTypeObject *)type)->_spec_cache.getitem = NULL;
+ FT_ATOMIC_STORE_PTR_RELAXED(
+ ((PyHeapTypeObject *)type)->_spec_cache.getitem, NULL);
}
}
+#define MAX_VERSIONS_PER_CLASS 1000
+#if _Py_ATTR_CACHE_UNUSED < MAX_VERSIONS_PER_CLASS
+#error "_Py_ATTR_CACHE_UNUSED must be bigger than max"
+#endif
+
static int
assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
{
- /* Ensure that the tp_version_tag is valid and set
- Py_TPFLAGS_VALID_VERSION_TAG. To respect the invariant, this
- must first be done on all super classes. Return 0 if this
- cannot be done, 1 if Py_TPFLAGS_VALID_VERSION_TAG.
+ ASSERT_TYPE_LOCK_HELD();
+
+ /* Ensure that the tp_version_tag is valid.
+ * To respect the invariant, this must first be done on all super classes.
+ * Return 0 if this cannot be done, 1 if tp_version_tag is set.
*/
- if (_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG)) {
+ if (type->tp_version_tag != 0) {
return 1;
}
if (!_PyType_HasFeature(type, Py_TPFLAGS_READY)) {
return 0;
}
+ if (type->tp_versions_used >= MAX_VERSIONS_PER_CLASS) {
+ /* (this includes `tp_versions_used == _Py_ATTR_CACHE_UNUSED`) */
+ return 0;
+ }
+ PyObject *bases = lookup_tp_bases(type);
+ Py_ssize_t n = PyTuple_GET_SIZE(bases);
+ for (Py_ssize_t i = 0; i < n; i++) {
+ PyObject *b = PyTuple_GET_ITEM(bases, i);
+ if (!assign_version_tag(interp, _PyType_CAST(b))) {
+ return 0;
+ }
+ }
if (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) {
/* static types */
if (NEXT_GLOBAL_VERSION_TAG > _Py_MAX_GLOBAL_TYPE_VERSION_TAG) {
/* We have run out of version numbers */
return 0;
}
- type->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++;
+ set_version_unlocked(type, NEXT_GLOBAL_VERSION_TAG++);
assert (type->tp_version_tag <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
}
else {
@@ -911,39 +1180,34 @@ assign_version_tag(PyInterpreterState *interp, PyTypeObject *type)
/* We have run out of version numbers */
return 0;
}
- type->tp_version_tag = NEXT_VERSION_TAG(interp)++;
+ set_version_unlocked(type, NEXT_VERSION_TAG(interp)++);
assert (type->tp_version_tag != 0);
}
-
- PyObject *bases = lookup_tp_bases(type);
- Py_ssize_t n = PyTuple_GET_SIZE(bases);
- for (Py_ssize_t i = 0; i < n; i++) {
- PyObject *b = PyTuple_GET_ITEM(bases, i);
- if (!assign_version_tag(interp, _PyType_CAST(b)))
- return 0;
- }
- type->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG;
return 1;
}
int PyUnstable_Type_AssignVersionTag(PyTypeObject *type)
{
PyInterpreterState *interp = _PyInterpreterState_GET();
- return assign_version_tag(interp, type);
+ int assigned;
+ BEGIN_TYPE_LOCK();
+ assigned = assign_version_tag(interp, type);
+ END_TYPE_LOCK();
+ return assigned;
}
static PyMemberDef type_members[] = {
- {"__basicsize__", T_PYSSIZET, offsetof(PyTypeObject,tp_basicsize),READONLY},
- {"__itemsize__", T_PYSSIZET, offsetof(PyTypeObject, tp_itemsize), READONLY},
- {"__flags__", T_ULONG, offsetof(PyTypeObject, tp_flags), READONLY},
+ {"__basicsize__", Py_T_PYSSIZET, offsetof(PyTypeObject,tp_basicsize),Py_READONLY},
+ {"__itemsize__", Py_T_PYSSIZET, offsetof(PyTypeObject, tp_itemsize), Py_READONLY},
+ {"__flags__", Py_T_ULONG, offsetof(PyTypeObject, tp_flags), Py_READONLY},
/* Note that this value is misleading for static builtin types,
since the memory at this offset will always be NULL. */
- {"__weakrefoffset__", T_PYSSIZET,
- offsetof(PyTypeObject, tp_weaklistoffset), READONLY},
- {"__base__", T_OBJECT, offsetof(PyTypeObject, tp_base), READONLY},
- {"__dictoffset__", T_PYSSIZET,
- offsetof(PyTypeObject, tp_dictoffset), READONLY},
+ {"__weakrefoffset__", Py_T_PYSSIZET,
+ offsetof(PyTypeObject, tp_weaklistoffset), Py_READONLY},
+ {"__base__", _Py_T_OBJECT, offsetof(PyTypeObject, tp_base), Py_READONLY},
+ {"__dictoffset__", Py_T_PYSSIZET,
+ offsetof(PyTypeObject, tp_dictoffset), Py_READONLY},
{0}
};
@@ -1060,20 +1324,14 @@ type_set_qualname(PyTypeObject *type, PyObject *value, void *context)
}
static PyObject *
-type_module(PyTypeObject *type, void *context)
+type_module(PyTypeObject *type)
{
PyObject *mod;
-
if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) {
PyObject *dict = lookup_tp_dict(type);
- mod = PyDict_GetItemWithError(dict, &_Py_ID(__module__));
- if (mod == NULL) {
- if (!PyErr_Occurred()) {
- PyErr_Format(PyExc_AttributeError, "__module__");
- }
- return NULL;
+ if (PyDict_GetItemRef(dict, &_Py_ID(__module__), &mod) == 0) {
+ PyErr_Format(PyExc_AttributeError, "__module__");
}
- Py_INCREF(mod);
}
else {
const char *s = strrchr(type->tp_name, '.');
@@ -1086,12 +1344,18 @@ type_module(PyTypeObject *type, void *context)
}
}
else {
- mod = Py_NewRef(&_Py_ID(builtins));
+ mod = &_Py_ID(builtins);
}
}
return mod;
}
+static PyObject *
+type_get_module(PyTypeObject *type, void *context)
+{
+ return type_module(type);
+}
+
static int
type_set_module(PyTypeObject *type, PyObject *value, void *context)
{
@@ -1101,26 +1365,69 @@ type_set_module(PyTypeObject *type, PyObject *value, void *context)
PyType_Modified(type);
PyObject *dict = lookup_tp_dict(type);
+ if (PyDict_Pop(dict, &_Py_ID(__firstlineno__), NULL) < 0) {
+ return -1;
+ }
return PyDict_SetItem(dict, &_Py_ID(__module__), value);
}
+
+PyObject *
+_PyType_GetFullyQualifiedName(PyTypeObject *type, char sep)
+{
+ if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
+ return PyUnicode_FromString(type->tp_name);
+ }
+
+ PyObject *qualname = type_qualname(type, NULL);
+ if (qualname == NULL) {
+ return NULL;
+ }
+
+ PyObject *module = type_module(type);
+ if (module == NULL) {
+ Py_DECREF(qualname);
+ return NULL;
+ }
+
+ PyObject *result;
+ if (PyUnicode_Check(module)
+ && !_PyUnicode_Equal(module, &_Py_ID(builtins))
+ && !_PyUnicode_Equal(module, &_Py_ID(__main__)))
+ {
+ result = PyUnicode_FromFormat("%U%c%U", module, sep, qualname);
+ }
+ else {
+ result = Py_NewRef(qualname);
+ }
+ Py_DECREF(module);
+ Py_DECREF(qualname);
+ return result;
+}
+
+PyObject *
+PyType_GetFullyQualifiedName(PyTypeObject *type)
+{
+ return _PyType_GetFullyQualifiedName(type, '.');
+}
+
+
static PyObject *
type_abstractmethods(PyTypeObject *type, void *context)
{
PyObject *mod = NULL;
/* type itself has an __abstractmethods__ descriptor (this). Don't return
that. */
- if (type != &PyType_Type) {
- PyObject *dict = lookup_tp_dict(type);
- mod = PyDict_GetItemWithError(dict, &_Py_ID(__abstractmethods__));
+ if (type == &PyType_Type) {
+ PyErr_SetObject(PyExc_AttributeError, &_Py_ID(__abstractmethods__));
}
- if (!mod) {
- if (!PyErr_Occurred()) {
+ else {
+ PyObject *dict = lookup_tp_dict(type);
+ if (PyDict_GetItemRef(dict, &_Py_ID(__abstractmethods__), &mod) == 0) {
PyErr_SetObject(PyExc_AttributeError, &_Py_ID(__abstractmethods__));
}
- return NULL;
}
- return Py_NewRef(mod);
+ return mod;
}
static int
@@ -1140,40 +1447,49 @@ type_set_abstractmethods(PyTypeObject *type, PyObject *value, void *context)
}
else {
abstract = 0;
- res = PyDict_DelItem(dict, &_Py_ID(__abstractmethods__));
- if (res && PyErr_ExceptionMatches(PyExc_KeyError)) {
+ res = PyDict_Pop(dict, &_Py_ID(__abstractmethods__), NULL);
+ if (res == 0) {
PyErr_SetObject(PyExc_AttributeError, &_Py_ID(__abstractmethods__));
return -1;
}
}
- if (res == 0) {
- PyType_Modified(type);
- if (abstract)
- type->tp_flags |= Py_TPFLAGS_IS_ABSTRACT;
- else
- type->tp_flags &= ~Py_TPFLAGS_IS_ABSTRACT;
+ if (res < 0) {
+ return -1;
}
- return res;
+
+ PyType_Modified(type);
+ if (abstract)
+ type->tp_flags |= Py_TPFLAGS_IS_ABSTRACT;
+ else
+ type->tp_flags &= ~Py_TPFLAGS_IS_ABSTRACT;
+ return 0;
}
static PyObject *
type_get_bases(PyTypeObject *type, void *context)
{
- PyObject *bases = lookup_tp_bases(type);
+ PyObject *bases = _PyType_GetBases(type);
if (bases == NULL) {
Py_RETURN_NONE;
}
- return Py_NewRef(bases);
+ return bases;
}
static PyObject *
type_get_mro(PyTypeObject *type, void *context)
{
- PyObject *mro = lookup_tp_mro(type);
+ PyObject *mro;
+
+ BEGIN_TYPE_LOCK();
+ mro = lookup_tp_mro(type);
if (mro == NULL) {
- Py_RETURN_NONE;
+ mro = Py_None;
+ } else {
+ Py_INCREF(mro);
}
- return Py_NewRef(mro);
+
+ END_TYPE_LOCK();
+ return mro;
}
static PyTypeObject *best_base(PyObject *);
@@ -1193,8 +1509,10 @@ static int recurse_down_subclasses(PyTypeObject *type, PyObject *name,
update_callback callback, void *data);
static int
-mro_hierarchy(PyTypeObject *type, PyObject *temp)
+mro_hierarchy_for_complete_type(PyTypeObject *type, PyObject *temp)
{
+ ASSERT_TYPE_LOCK_HELD();
+
PyObject *old_mro;
int res = mro_internal(type, &old_mro);
if (res <= 0) {
@@ -1202,6 +1520,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
return res;
}
PyObject *new_mro = lookup_tp_mro(type);
+ assert(new_mro != NULL);
PyObject *tuple;
if (old_mro != NULL) {
@@ -1220,7 +1539,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
Py_XDECREF(tuple);
if (res < 0) {
- set_tp_mro(type, old_mro);
+ set_tp_mro(type, old_mro, 0);
Py_DECREF(new_mro);
return -1;
}
@@ -1246,7 +1565,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
Py_ssize_t n = PyList_GET_SIZE(subclasses);
for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *subclass = _PyType_CAST(PyList_GET_ITEM(subclasses, i));
- res = mro_hierarchy(subclass, temp);
+ res = mro_hierarchy_for_complete_type(subclass, temp);
if (res < 0) {
break;
}
@@ -1258,7 +1577,7 @@ mro_hierarchy(PyTypeObject *type, PyObject *temp)
}
static int
-type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
+type_set_bases_unlocked(PyTypeObject *type, PyObject *new_bases, void *context)
{
// Check arguments
if (!check_set_special_type_attr(type, new_bases, "__bases__")) {
@@ -1289,7 +1608,7 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
}
PyTypeObject *base = (PyTypeObject*)ob;
- if (PyType_IsSubtype(base, type) ||
+ if (is_subtype_with_mro(lookup_tp_mro(base), base, type) ||
/* In case of reentering here again through a custom mro()
the above check is not enough since it relies on
base->tp_mro which would gonna be updated inside
@@ -1321,14 +1640,14 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
assert(old_bases != NULL);
PyTypeObject *old_base = type->tp_base;
- set_tp_bases(type, Py_NewRef(new_bases));
+ set_tp_bases(type, Py_NewRef(new_bases), 0);
type->tp_base = (PyTypeObject *)Py_NewRef(new_base);
PyObject *temp = PyList_New(0);
if (temp == NULL) {
goto bail;
}
- if (mro_hierarchy(type, temp) < 0) {
+ if (mro_hierarchy_for_complete_type(type, temp) < 0) {
goto undo;
}
Py_DECREF(temp);
@@ -1352,6 +1671,7 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
res = 0;
}
+ RARE_EVENT_INC(set_bases);
Py_DECREF(old_bases);
Py_DECREF(old_base);
@@ -1368,7 +1688,7 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
"", 2, 3, &cls, &new_mro, &old_mro);
/* Do not rollback if cls has a newer version of MRO. */
if (lookup_tp_mro(cls) == new_mro) {
- set_tp_mro(cls, Py_XNewRef(old_mro));
+ set_tp_mro(cls, Py_XNewRef(old_mro), 0);
Py_DECREF(new_mro);
}
}
@@ -1378,7 +1698,7 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
if (lookup_tp_bases(type) == new_bases) {
assert(type->tp_base == new_base);
- set_tp_bases(type, old_bases);
+ set_tp_bases(type, old_bases, 0);
type->tp_base = old_base;
Py_DECREF(new_bases);
@@ -1393,6 +1713,16 @@ type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
return -1;
}
+static int
+type_set_bases(PyTypeObject *type, PyObject *new_bases, void *context)
+{
+ int res;
+ BEGIN_TYPE_LOCK();
+ res = type_set_bases_unlocked(type, new_bases, context);
+ END_TYPE_LOCK();
+ return res;
+}
+
static PyObject *
type_dict(PyTypeObject *type, void *context)
{
@@ -1411,18 +1741,14 @@ type_get_doc(PyTypeObject *type, void *context)
return _PyType_GetDocFromInternalDoc(type->tp_name, type->tp_doc);
}
PyObject *dict = lookup_tp_dict(type);
- result = PyDict_GetItemWithError(dict, &_Py_ID(__doc__));
- if (result == NULL) {
- if (!PyErr_Occurred()) {
- result = Py_NewRef(Py_None);
- }
- }
- else if (Py_TYPE(result)->tp_descr_get) {
- result = Py_TYPE(result)->tp_descr_get(result, NULL,
- (PyObject *)type);
+ if (PyDict_GetItemRef(dict, &_Py_ID(__doc__), &result) == 0) {
+ result = Py_NewRef(Py_None);
}
- else {
- Py_INCREF(result);
+ else if (result) {
+ descrgetfunc descr_get = Py_TYPE(result)->tp_descr_get;
+ if (descr_get) {
+ Py_SETREF(result, descr_get(result, NULL, (PyObject *)type));
+ }
}
return result;
}
@@ -1430,7 +1756,7 @@ type_get_doc(PyTypeObject *type, void *context)
static PyObject *
type_get_text_signature(PyTypeObject *type, void *context)
{
- return _PyType_GetTextSignatureFromInternalDoc(type->tp_name, type->tp_doc);
+ return _PyType_GetTextSignatureFromInternalDoc(type->tp_name, type->tp_doc, 0);
}
static int
@@ -1453,16 +1779,16 @@ type_get_annotations(PyTypeObject *type, void *context)
PyObject *annotations;
PyObject *dict = lookup_tp_dict(type);
- annotations = PyDict_GetItemWithError(dict, &_Py_ID(__annotations__));
+ if (PyDict_GetItemRef(dict, &_Py_ID(__annotations__), &annotations) < 0) {
+ return NULL;
+ }
if (annotations) {
- if (Py_TYPE(annotations)->tp_descr_get) {
- annotations = Py_TYPE(annotations)->tp_descr_get(
- annotations, NULL, (PyObject *)type);
- } else {
- Py_INCREF(annotations);
+ descrgetfunc get = Py_TYPE(annotations)->tp_descr_get;
+ if (get) {
+ Py_SETREF(annotations, get(annotations, NULL, (PyObject *)type));
}
}
- else if (!PyErr_Occurred()) {
+ else {
annotations = PyDict_New();
if (annotations) {
int result = PyDict_SetItem(
@@ -1494,16 +1820,18 @@ type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
result = PyDict_SetItem(dict, &_Py_ID(__annotations__), value);
} else {
/* delete */
- result = PyDict_DelItem(dict, &_Py_ID(__annotations__));
- if (result < 0 && PyErr_ExceptionMatches(PyExc_KeyError)) {
+ result = PyDict_Pop(dict, &_Py_ID(__annotations__), NULL);
+ if (result == 0) {
PyErr_SetString(PyExc_AttributeError, "__annotations__");
+ return -1;
}
}
-
- if (result == 0) {
- PyType_Modified(type);
+ if (result < 0) {
+ return -1;
}
- return result;
+
+ PyType_Modified(type);
+ return 0;
}
static PyObject *
@@ -1513,15 +1841,11 @@ type_get_type_params(PyTypeObject *type, void *context)
return PyTuple_New(0);
}
- PyObject *params = PyDict_GetItemWithError(lookup_tp_dict(type), &_Py_ID(__type_params__));
- if (params) {
- return Py_NewRef(params);
- }
- if (PyErr_Occurred()) {
- return NULL;
+ PyObject *params;
+ if (PyDict_GetItemRef(lookup_tp_dict(type), &_Py_ID(__type_params__), &params) == 0) {
+ return PyTuple_New(0);
}
-
- return PyTuple_New(0);
+ return params;
}
static int
@@ -1579,7 +1903,7 @@ static PyGetSetDef type_getsets[] = {
{"__qualname__", (getter)type_qualname, (setter)type_set_qualname, NULL},
{"__bases__", (getter)type_get_bases, (setter)type_set_bases, NULL},
{"__mro__", (getter)type_get_mro, NULL, NULL},
- {"__module__", (getter)type_module, (setter)type_set_module, NULL},
+ {"__module__", (getter)type_get_module, (setter)type_set_module, NULL},
{"__abstractmethods__", (getter)type_abstractmethods,
(setter)type_set_abstractmethods, NULL},
{"__dict__", (getter)type_dict, NULL, NULL},
@@ -1591,41 +1915,46 @@ static PyGetSetDef type_getsets[] = {
};
static PyObject *
-type_repr(PyTypeObject *type)
+type_repr(PyObject *self)
{
+ PyTypeObject *type = (PyTypeObject *)self;
if (type->tp_name == NULL) {
// type_repr() called before the type is fully initialized
// by PyType_Ready().
return PyUnicode_FromFormat("<class at %p>", type);
}
- PyObject *mod, *name, *rtn;
-
- mod = type_module(type, NULL);
- if (mod == NULL)
+ PyObject *mod = type_module(type);
+ if (mod == NULL) {
PyErr_Clear();
+ }
else if (!PyUnicode_Check(mod)) {
- Py_SETREF(mod, NULL);
+ Py_CLEAR(mod);
}
- name = type_qualname(type, NULL);
+
+ PyObject *name = type_qualname(type, NULL);
if (name == NULL) {
Py_XDECREF(mod);
return NULL;
}
- if (mod != NULL && !_PyUnicode_Equal(mod, &_Py_ID(builtins)))
- rtn = PyUnicode_FromFormat("<class '%U.%U'>", mod, name);
- else
- rtn = PyUnicode_FromFormat("<class '%s'>", type->tp_name);
-
+ PyObject *result;
+ if (mod != NULL && !_PyUnicode_Equal(mod, &_Py_ID(builtins))) {
+ result = PyUnicode_FromFormat("<class '%U.%U'>", mod, name);
+ }
+ else {
+ result = PyUnicode_FromFormat("<class '%s'>", type->tp_name);
+ }
Py_XDECREF(mod);
Py_DECREF(name);
- return rtn;
+
+ return result;
}
static PyObject *
-type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
+type_call(PyObject *self, PyObject *args, PyObject *kwds)
{
+ PyTypeObject *type = (PyTypeObject *)self;
PyObject *obj;
PyThreadState *tstate = _PyThreadState_GET();
@@ -1689,6 +2018,21 @@ type_call(PyTypeObject *type, PyObject *args, PyObject *kwds)
}
PyObject *
+_PyType_NewManagedObject(PyTypeObject *type)
+{
+ assert(type->tp_flags & Py_TPFLAGS_INLINE_VALUES);
+ assert(_PyType_IS_GC(type));
+ assert(type->tp_new == PyBaseObject_Type.tp_new);
+ assert(type->tp_alloc == PyType_GenericAlloc);
+ assert(type->tp_itemsize == 0);
+ PyObject *obj = PyType_GenericAlloc(type, 0);
+ if (obj == NULL) {
+ return PyErr_NoMemory();
+ }
+ return obj;
+}
+
+PyObject *
_PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems)
{
PyObject *obj;
@@ -1698,10 +2042,14 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems)
* flag to indicate when that is safe) it does not seem worth the memory
* savings. An example type that doesn't need the +1 is a subclass of
* tuple. See GH-100659 and GH-81381. */
- const size_t size = _PyObject_VAR_SIZE(type, nitems+1);
+ size_t size = _PyObject_VAR_SIZE(type, nitems+1);
const size_t presize = _PyType_PreHeaderSize(type);
- char *alloc = PyObject_Malloc(size + presize);
+ if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
+ assert(type->tp_itemsize == 0);
+ size += _PyInlineValuesSize(type);
+ }
+ char *alloc = _PyObject_MallocWithType(type, size + presize);
if (alloc == NULL) {
return PyErr_NoMemory();
}
@@ -1709,6 +2057,8 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems)
if (presize) {
((PyObject **)alloc)[0] = NULL;
((PyObject **)alloc)[1] = NULL;
+ }
+ if (PyType_IS_GC(type)) {
_PyObject_GC_Link(obj);
}
memset(obj, '\0', size);
@@ -1719,6 +2069,9 @@ _PyType_AllocNoTrack(PyTypeObject *type, Py_ssize_t nitems)
else {
_PyObject_InitVar((PyVarObject *)obj, type, nitems);
}
+ if (type->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
+ _PyObject_InitInlineValues(obj, type);
+ }
return obj;
}
@@ -1759,7 +2112,7 @@ traverse_slots(PyTypeObject *type, PyObject *self, visitproc visit, void *arg)
n = Py_SIZE(type);
mp = _PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type);
for (i = 0; i < n; i++, mp++) {
- if (mp->type == T_OBJECT_EX) {
+ if (mp->type == Py_T_OBJECT_EX) {
char *addr = (char *)self + mp->offset;
PyObject *obj = *(PyObject **)addr;
if (obj != NULL) {
@@ -1796,7 +2149,7 @@ subtype_traverse(PyObject *self, visitproc visit, void *arg)
assert(base->tp_dictoffset == 0);
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
assert(type->tp_dictoffset == -1);
- int err = _PyObject_VisitManagedDict(self, visit, arg);
+ int err = PyObject_VisitManagedDict(self, visit, arg);
if (err) {
return err;
}
@@ -1834,7 +2187,7 @@ clear_slots(PyTypeObject *type, PyObject *self)
n = Py_SIZE(type);
mp = _PyHeapType_GET_MEMBERS((PyHeapTypeObject *)type);
for (i = 0; i < n; i++, mp++) {
- if (mp->type == T_OBJECT_EX && !(mp->flags & READONLY)) {
+ if (mp->type == Py_T_OBJECT_EX && !(mp->flags & Py_READONLY)) {
char *addr = (char *)self + mp->offset;
PyObject *obj = *(PyObject **)addr;
if (obj != NULL) {
@@ -1866,7 +2219,11 @@ subtype_clear(PyObject *self)
__dict__ slots (as in the case 'self.__dict__ is self'). */
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
if ((base->tp_flags & Py_TPFLAGS_MANAGED_DICT) == 0) {
- _PyObject_ClearManagedDict(self);
+ PyObject_ClearManagedDict(self);
+ }
+ else {
+ assert((base->tp_flags & Py_TPFLAGS_INLINE_VALUES) ==
+ (type->tp_flags & Py_TPFLAGS_INLINE_VALUES));
}
}
else if (type->tp_dictoffset != base->tp_dictoffset) {
@@ -1995,15 +2352,7 @@ subtype_dealloc(PyObject *self)
finalizers since they might rely on part of the object
being finalized that has already been destroyed. */
if (type->tp_weaklistoffset && !base->tp_weaklistoffset) {
- /* Modeled after GET_WEAKREFS_LISTPTR().
-
- This is never triggered for static types so we can avoid the
- (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR(). */
- PyWeakReference **list = \
- _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(self);
- while (*list) {
- _PyWeakref_ClearRef(*list);
- }
+ _PyWeakref_ClearWeakRefsNoCallbacks(self);
}
}
@@ -2018,14 +2367,7 @@ subtype_dealloc(PyObject *self)
/* If we added a dict, DECREF it, or free inline values. */
if (type->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
- PyDictOrValues *dorv_ptr = _PyObject_DictOrValuesPointer(self);
- if (_PyDictOrValues_IsValues(*dorv_ptr)) {
- _PyObject_FreeInstanceAttributes(self);
- }
- else {
- Py_XDECREF(_PyDictOrValues_GetDict(*dorv_ptr));
- }
- dorv_ptr->values = NULL;
+ PyObject_ClearManagedDict(self);
}
else if (type->tp_dictoffset && !base->tp_dictoffset) {
PyObject **dictptr = _PyObject_ComputedDictPointer(self);
@@ -2121,27 +2463,35 @@ type_is_subtype_base_chain(PyTypeObject *a, PyTypeObject *b)
return (b == &PyBaseObject_Type);
}
-int
-PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
+static int
+is_subtype_with_mro(PyObject *a_mro, PyTypeObject *a, PyTypeObject *b)
{
- PyObject *mro;
-
- mro = lookup_tp_mro(a);
- if (mro != NULL) {
+ int res;
+ if (a_mro != NULL) {
/* Deal with multiple inheritance without recursion
by walking the MRO tuple */
Py_ssize_t i, n;
- assert(PyTuple_Check(mro));
- n = PyTuple_GET_SIZE(mro);
+ assert(PyTuple_Check(a_mro));
+ n = PyTuple_GET_SIZE(a_mro);
+ res = 0;
for (i = 0; i < n; i++) {
- if (PyTuple_GET_ITEM(mro, i) == (PyObject *)b)
- return 1;
+ if (PyTuple_GET_ITEM(a_mro, i) == (PyObject *)b) {
+ res = 1;
+ break;
+ }
}
- return 0;
}
- else
+ else {
/* a is not completely initialized yet; follow tp_base */
- return type_is_subtype_base_chain(a, b);
+ res = type_is_subtype_base_chain(a, b);
+ }
+ return res;
+}
+
+int
+PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
+{
+ return is_subtype_with_mro(a->tp_mro, a, b);
}
/* Routines to do a method lookup in the type without looking in the
@@ -2151,7 +2501,7 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
Variants:
- _PyObject_LookupSpecial() returns NULL without raising an exception
- when the _PyType_Lookup() call fails;
+ when the _PyType_LookupRef() call fails;
- lookup_maybe_method() and lookup_method() are internal routines similar
to _PyObject_LookupSpecial(), but can return unbound PyFunction
@@ -2164,13 +2514,12 @@ _PyObject_LookupSpecial(PyObject *self, PyObject *attr)
{
PyObject *res;
- res = _PyType_Lookup(Py_TYPE(self), attr);
+ res = _PyType_LookupRef(Py_TYPE(self), attr);
if (res != NULL) {
descrgetfunc f;
- if ((f = Py_TYPE(res)->tp_descr_get) == NULL)
- Py_INCREF(res);
- else
- res = f(res, self, (PyObject *)(Py_TYPE(self)));
+ if ((f = Py_TYPE(res)->tp_descr_get) != NULL) {
+ Py_SETREF(res, f(res, self, (PyObject *)(Py_TYPE(self))));
+ }
}
return res;
}
@@ -2187,7 +2536,7 @@ _PyObject_LookupSpecialId(PyObject *self, _Py_Identifier *attrid)
static PyObject *
lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound)
{
- PyObject *res = _PyType_Lookup(Py_TYPE(self), attr);
+ PyObject *res = _PyType_LookupRef(Py_TYPE(self), attr);
if (res == NULL) {
return NULL;
}
@@ -2195,16 +2544,12 @@ lookup_maybe_method(PyObject *self, PyObject *attr, int *unbound)
if (_PyType_HasFeature(Py_TYPE(res), Py_TPFLAGS_METHOD_DESCRIPTOR)) {
/* Avoid temporary PyMethodObject */
*unbound = 1;
- Py_INCREF(res);
}
else {
*unbound = 0;
descrgetfunc f = Py_TYPE(res)->tp_descr_get;
- if (f == NULL) {
- Py_INCREF(res);
- }
- else {
- res = f(res, self, (PyObject *)(Py_TYPE(self)));
+ if (f != NULL) {
+ Py_SETREF(res, f(res, self, (PyObject *)(Py_TYPE(self))));
}
}
return res;
@@ -2335,7 +2680,7 @@ static PyObject *
class_name(PyObject *cls)
{
PyObject *name;
- if (_PyObject_LookupAttr(cls, &_Py_ID(__name__), &name) == 0) {
+ if (PyObject_GetOptionalAttr(cls, &_Py_ID(__name__), &name) == 0) {
name = PyObject_Repr(cls);
}
return name;
@@ -2403,7 +2748,7 @@ set_mro_error(PyObject **to_merge, Py_ssize_t to_merge_size, Py_ssize_t *remain)
n = PyDict_GET_SIZE(set);
off = PyOS_snprintf(buf, sizeof(buf), "Cannot create a \
-consistent method resolution\norder (MRO) for bases");
+consistent method resolution order (MRO) for bases");
i = 0;
while (PyDict_Next(set, &i, &k, &v) && (size_t)off < sizeof(buf)) {
PyObject *name = class_name(k);
@@ -2503,8 +2848,10 @@ pmerge(PyObject *acc, PyObject **to_merge, Py_ssize_t to_merge_size)
}
static PyObject *
-mro_implementation(PyTypeObject *type)
+mro_implementation_unlocked(PyTypeObject *type)
{
+ ASSERT_TYPE_LOCK_HELD();
+
if (!_PyType_IsReady(type)) {
if (PyType_Ready(type) < 0)
return NULL;
@@ -2529,6 +2876,7 @@ mro_implementation(PyTypeObject *type)
*/
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, 0));
PyObject *base_mro = lookup_tp_mro(base);
+ assert(base_mro != NULL);
Py_ssize_t k = PyTuple_GET_SIZE(base_mro);
PyObject *result = PyTuple_New(k + 1);
if (result == NULL) {
@@ -2563,9 +2911,12 @@ mro_implementation(PyTypeObject *type)
return NULL;
}
+ PyObject *mro_to_merge;
for (Py_ssize_t i = 0; i < n; i++) {
PyTypeObject *base = _PyType_CAST(PyTuple_GET_ITEM(bases, i));
- to_merge[i] = lookup_tp_mro(base);
+ mro_to_merge = lookup_tp_mro(base);
+ assert(mro_to_merge != NULL);
+ to_merge[i] = mro_to_merge;
}
to_merge[n] = bases;
@@ -2584,6 +2935,16 @@ mro_implementation(PyTypeObject *type)
return result;
}
+static PyObject *
+mro_implementation(PyTypeObject *type)
+{
+ PyObject *mro;
+ BEGIN_TYPE_LOCK();
+ mro = mro_implementation_unlocked(type);
+ END_TYPE_LOCK();
+ return mro;
+}
+
/*[clinic input]
type.mro
@@ -2622,7 +2983,7 @@ mro_check(PyTypeObject *type, PyObject *mro)
}
PyTypeObject *base = (PyTypeObject*)obj;
- if (!PyType_IsSubtype(solid, solid_base(base))) {
+ if (!is_subtype_with_mro(lookup_tp_mro(solid), solid, solid_base(base))) {
PyErr_Format(
PyExc_TypeError,
"mro() returned base with unsuitable layout ('%.500s')",
@@ -2653,6 +3014,9 @@ mro_invoke(PyTypeObject *type)
{
PyObject *mro_result;
PyObject *new_mro;
+
+ ASSERT_TYPE_LOCK_HELD();
+
const int custom = !Py_IS_TYPE(type, &PyType_Type);
if (custom) {
@@ -2665,7 +3029,7 @@ mro_invoke(PyTypeObject *type)
Py_DECREF(mro_meth);
}
else {
- mro_result = mro_implementation(type);
+ mro_result = mro_implementation_unlocked(type);
}
if (mro_result == NULL)
return NULL;
@@ -2712,8 +3076,10 @@ mro_invoke(PyTypeObject *type)
- Returns -1 in case of an error.
*/
static int
-mro_internal(PyTypeObject *type, PyObject **p_old_mro)
+mro_internal_unlocked(PyTypeObject *type, int initial, PyObject **p_old_mro)
{
+ ASSERT_TYPE_LOCK_HELD();
+
PyObject *new_mro, *old_mro;
int reent;
@@ -2733,7 +3099,7 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro)
return 0;
}
- set_tp_mro(type, new_mro);
+ set_tp_mro(type, new_mro, initial);
type_mro_modified(type, new_mro);
/* corner case: the super class might have been hidden
@@ -2747,7 +3113,7 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro)
else {
/* For static builtin types, this is only called during init
before the method cache has been populated. */
- assert(_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG));
+ assert(type->tp_version_tag);
}
if (p_old_mro != NULL)
@@ -2758,6 +3124,16 @@ mro_internal(PyTypeObject *type, PyObject **p_old_mro)
return 1;
}
+static int
+mro_internal(PyTypeObject *type, PyObject **p_old_mro)
+{
+ int res;
+ BEGIN_TYPE_LOCK();
+ res = mro_internal_unlocked(type, 0, p_old_mro);
+ END_TYPE_LOCK();
+ return res;
+}
+
/* Calculate the best base amongst multiple base classes.
This is the first one that's on the path to the "solid base". */
@@ -2934,19 +3310,26 @@ subtype_setdict(PyObject *obj, PyObject *value, void *context)
return func(descr, obj, value);
}
/* Almost like PyObject_GenericSetDict, but allow __dict__ to be deleted. */
- dictptr = _PyObject_GetDictPtr(obj);
- if (dictptr == NULL) {
- PyErr_SetString(PyExc_AttributeError,
- "This object has no __dict__");
- return -1;
- }
if (value != NULL && !PyDict_Check(value)) {
PyErr_Format(PyExc_TypeError,
"__dict__ must be set to a dictionary, "
"not a '%.200s'", Py_TYPE(value)->tp_name);
return -1;
}
- Py_XSETREF(*dictptr, Py_XNewRef(value));
+
+ if (Py_TYPE(obj)->tp_flags & Py_TPFLAGS_MANAGED_DICT) {
+ return _PyObject_SetManagedDict(obj, value);
+ }
+ else {
+ dictptr = _PyObject_ComputedDictPointer(obj);
+ if (dictptr == NULL) {
+ PyErr_SetString(PyExc_AttributeError,
+ "This object has no __dict__");
+ return -1;
+ }
+ Py_CLEAR(*dictptr);
+ *dictptr = Py_XNewRef(value);
+ }
return 0;
}
@@ -3041,7 +3424,7 @@ type_init(PyObject *cls, PyObject *args, PyObject *kwds)
unsigned long
PyType_GetFlags(PyTypeObject *type)
{
- return type->tp_flags;
+ return FT_ATOMIC_LOAD_ULONG_RELAXED(type->tp_flags);
}
@@ -3341,7 +3724,7 @@ type_new_alloc(type_new_ctx *ctx)
type->tp_as_mapping = &et->as_mapping;
type->tp_as_buffer = &et->as_buffer;
- set_tp_bases(type, Py_NewRef(ctx->bases));
+ set_tp_bases(type, Py_NewRef(ctx->bases), 1);
type->tp_base = (PyTypeObject *)Py_NewRef(ctx->base);
type->tp_dealloc = subtype_dealloc;
@@ -3356,6 +3739,8 @@ type_new_alloc(type_new_ctx *ctx)
et->ht_module = NULL;
et->_ht_tpname = NULL;
+ _PyObject_SetDeferredRefcount((PyObject *)et);
+
return type;
}
@@ -3395,18 +3780,13 @@ type_new_set_module(PyTypeObject *type)
return 0;
}
- PyObject *module = PyDict_GetItemWithError(globals, &_Py_ID(__name__));
- if (module == NULL) {
- if (PyErr_Occurred()) {
- return -1;
- }
- return 0;
- }
-
- if (PyDict_SetItem(dict, &_Py_ID(__module__), module) < 0) {
- return -1;
+ PyObject *module;
+ r = PyDict_GetItemRef(globals, &_Py_ID(__name__), &module);
+ if (module) {
+ r = PyDict_SetItem(dict, &_Py_ID(__module__), module);
+ Py_DECREF(module);
}
- return 0;
+ return r;
}
@@ -3417,23 +3797,24 @@ type_new_set_ht_name(PyTypeObject *type)
{
PyHeapTypeObject *et = (PyHeapTypeObject *)type;
PyObject *dict = lookup_tp_dict(type);
- PyObject *qualname = PyDict_GetItemWithError(dict, &_Py_ID(__qualname__));
+ PyObject *qualname;
+ if (PyDict_GetItemRef(dict, &_Py_ID(__qualname__), &qualname) < 0) {
+ return -1;
+ }
if (qualname != NULL) {
if (!PyUnicode_Check(qualname)) {
PyErr_Format(PyExc_TypeError,
"type __qualname__ must be a str, not %s",
Py_TYPE(qualname)->tp_name);
+ Py_DECREF(qualname);
return -1;
}
- et->ht_qualname = Py_NewRef(qualname);
+ et->ht_qualname = qualname;
if (PyDict_DelItem(dict, &_Py_ID(__qualname__)) < 0) {
return -1;
}
}
else {
- if (PyErr_Occurred()) {
- return -1;
- }
et->ht_qualname = Py_NewRef(et->ht_name);
}
return 0;
@@ -3467,7 +3848,7 @@ type_new_set_doc(PyTypeObject *type)
// Silently truncate the docstring if it contains a null byte
Py_ssize_t size = strlen(doc_str) + 1;
- char *tp_doc = (char *)PyObject_Malloc(size);
+ char *tp_doc = (char *)PyMem_Malloc(size);
if (tp_doc == NULL) {
PyErr_NoMemory();
return -1;
@@ -3551,7 +3932,7 @@ type_new_descriptors(const type_new_ctx *ctx, PyTypeObject *type)
if (mp->name == NULL) {
return -1;
}
- mp->type = T_OBJECT_EX;
+ mp->type = Py_T_OBJECT_EX;
mp->offset = slotoffset;
/* __dict__ and __weakref__ are already filtered out */
@@ -3802,6 +4183,17 @@ type_new_impl(type_new_ctx *ctx)
// Put the proper slots in place
fixup_slot_dispatchers(type);
+ if (!_PyDict_HasOnlyStringKeys(type->tp_dict)) {
+ if (PyErr_WarnFormat(
+ PyExc_RuntimeWarning,
+ 1,
+ "non-string key in the __dict__ of class %.200s",
+ type->tp_name) == -1)
+ {
+ goto error;
+ }
+ }
+
if (type_new_set_names(type) < 0) {
goto error;
}
@@ -3840,16 +4232,14 @@ type_new_get_bases(type_new_ctx *ctx, PyObject **type)
if (PyType_Check(base)) {
continue;
}
- PyObject *mro_entries;
- if (_PyObject_LookupAttr(base, &_Py_ID(__mro_entries__),
- &mro_entries) < 0) {
+ int rc = PyObject_HasAttrWithError(base, &_Py_ID(__mro_entries__));
+ if (rc < 0) {
return -1;
}
- if (mro_entries != NULL) {
+ if (rc) {
PyErr_SetString(PyExc_TypeError,
"type() doesn't support MRO entry resolution; "
"use types.new_class()");
- Py_DECREF(mro_entries);
return -1;
}
}
@@ -4024,28 +4414,28 @@ check_basicsize_includes_size_and_offsets(PyTypeObject* type)
if (type->tp_base && type->tp_base->tp_basicsize > type->tp_basicsize) {
PyErr_Format(PyExc_TypeError,
- "tp_basicsize for type '%s' (%d) is too small for base '%s' (%d)",
+ "tp_basicsize for type '%s' (%zd) is too small for base '%s' (%zd)",
type->tp_name, type->tp_basicsize,
type->tp_base->tp_name, type->tp_base->tp_basicsize);
return 0;
}
if (type->tp_weaklistoffset + (Py_ssize_t)sizeof(PyObject*) > max) {
PyErr_Format(PyExc_TypeError,
- "weaklist offset %d is out of bounds for type '%s' (tp_basicsize = %d)",
+ "weaklist offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)",
type->tp_weaklistoffset,
type->tp_name, type->tp_basicsize);
return 0;
}
if (type->tp_dictoffset + (Py_ssize_t)sizeof(PyObject*) > max) {
PyErr_Format(PyExc_TypeError,
- "dict offset %d is out of bounds for type '%s' (tp_basicsize = %d)",
+ "dict offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)",
type->tp_dictoffset,
type->tp_name, type->tp_basicsize);
return 0;
}
if (type->tp_vectorcall_offset + (Py_ssize_t)sizeof(vectorcallfunc*) > max) {
PyErr_Format(PyExc_TypeError,
- "vectorcall offset %d is out of bounds for type '%s' (tp_basicsize = %d)",
+ "vectorcall offset %zd is out of bounds for type '%s' (tp_basicsize = %zd)",
type->tp_vectorcall_offset,
type->tp_name, type->tp_basicsize);
return 0;
@@ -4100,20 +4490,20 @@ _PyType_FromMetaclass_impl(
nmembers++;
if (strcmp(memb->name, "__weaklistoffset__") == 0) {
// The PyMemberDef must be a Py_ssize_t and readonly
- assert(memb->type == T_PYSSIZET);
- assert(memb->flags == READONLY);
+ assert(memb->type == Py_T_PYSSIZET);
+ assert(memb->flags == Py_READONLY);
weaklistoffset = memb->offset;
}
if (strcmp(memb->name, "__dictoffset__") == 0) {
// The PyMemberDef must be a Py_ssize_t and readonly
- assert(memb->type == T_PYSSIZET);
- assert(memb->flags == READONLY);
+ assert(memb->type == Py_T_PYSSIZET);
+ assert(memb->flags == Py_READONLY);
dictoffset = memb->offset;
}
if (strcmp(memb->name, "__vectorcalloffset__") == 0) {
// The PyMemberDef must be a Py_ssize_t and readonly
- assert(memb->type == T_PYSSIZET);
- assert(memb->flags == READONLY);
+ assert(memb->type == Py_T_PYSSIZET);
+ assert(memb->flags == Py_READONLY);
vectorcalloffset = memb->offset;
}
if (memb->flags & Py_RELATIVE_OFFSET) {
@@ -4142,7 +4532,7 @@ _PyType_FromMetaclass_impl(
goto finally;
}
if (slot->pfunc == NULL) {
- PyObject_Free(tp_doc);
+ PyMem_Free(tp_doc);
tp_doc = NULL;
}
else {
@@ -4152,7 +4542,7 @@ _PyType_FromMetaclass_impl(
# endif
#endif
size_t len = strlen(slot->pfunc)+1;
- tp_doc = PyObject_Malloc(len);
+ tp_doc = PyMem_Malloc(len);
if (tp_doc == NULL) {
PyErr_NoMemory();
goto finally;
@@ -4339,7 +4729,7 @@ _PyType_FromMetaclass_impl(
/* Set slots we have prepared */
type->tp_base = (PyTypeObject *)Py_NewRef(base);
- set_tp_bases(type, bases);
+ set_tp_bases(type, bases, 1);
bases = NULL; // We give our reference to bases to the type
type->tp_doc = tp_doc;
@@ -4488,7 +4878,7 @@ _PyType_FromMetaclass_impl(
Py_CLEAR(res);
}
Py_XDECREF(bases);
- PyObject_Free(tp_doc);
+ PyMem_Free(tp_doc);
Py_XDECREF(ht_name);
PyMem_Free(_ht_tpname);
return (PyObject*)res;
@@ -4531,6 +4921,12 @@ PyType_GetQualName(PyTypeObject *type)
return type_qualname(type, NULL);
}
+PyObject *
+PyType_GetModuleName(PyTypeObject *type)
+{
+ return type_module(type);
+}
+
void *
PyType_GetSlot(PyTypeObject *type, int slot)
{
@@ -4591,21 +4987,39 @@ PyType_GetModuleState(PyTypeObject *type)
/* Get the module of the first superclass where the module has the
* given PyModuleDef.
*/
-PyObject *
-PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)
+static inline PyObject *
+get_module_by_def(PyTypeObject *type, PyModuleDef *def)
{
assert(PyType_Check(type));
+ if (!_PyType_HasFeature(type, Py_TPFLAGS_HEAPTYPE)) {
+ // type_ready_mro() ensures that no heap type is
+ // contained in a static type MRO.
+ return NULL;
+ }
+ else {
+ PyHeapTypeObject *ht = (PyHeapTypeObject*)type;
+ PyObject *module = ht->ht_module;
+ if (module && _PyModule_GetDef(module) == def) {
+ return module;
+ }
+ }
+
+ PyObject *res = NULL;
+ BEGIN_TYPE_LOCK();
+
PyObject *mro = lookup_tp_mro(type);
// The type must be ready
assert(mro != NULL);
assert(PyTuple_Check(mro));
- // mro_invoke() ensures that the type MRO cannot be empty, so we don't have
- // to check i < PyTuple_GET_SIZE(mro) at the first loop iteration.
+ // mro_invoke() ensures that the type MRO cannot be empty.
assert(PyTuple_GET_SIZE(mro) >= 1);
+ // Also, the first item in the MRO is the type itself, which
+ // we already checked above. We skip it in the loop.
+ assert(PyTuple_GET_ITEM(mro, 0) == (PyObject *)type);
Py_ssize_t n = PyTuple_GET_SIZE(mro);
- for (Py_ssize_t i = 0; i < n; i++) {
+ for (Py_ssize_t i = 1; i < n; i++) {
PyObject *super = PyTuple_GET_ITEM(mro, i);
if(!_PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) {
// Static types in the MRO need to be skipped
@@ -4615,15 +5029,62 @@ PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)
PyHeapTypeObject *ht = (PyHeapTypeObject*)super;
PyObject *module = ht->ht_module;
if (module && _PyModule_GetDef(module) == def) {
- return module;
+ res = module;
+ break;
}
}
+ END_TYPE_LOCK();
+ return res;
+}
- PyErr_Format(
- PyExc_TypeError,
- "PyType_GetModuleByDef: No superclass of '%s' has the given module",
- type->tp_name);
- return NULL;
+PyObject *
+PyType_GetModuleByDef(PyTypeObject *type, PyModuleDef *def)
+{
+ PyObject *module = get_module_by_def(type, def);
+ if (module == NULL) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "PyType_GetModuleByDef: No superclass of '%s' has the given module",
+ type->tp_name);
+ }
+ return module;
+}
+
+PyObject *
+_PyType_GetModuleByDef2(PyTypeObject *left, PyTypeObject *right,
+ PyModuleDef *def)
+{
+ PyObject *module = get_module_by_def(left, def);
+ if (module == NULL) {
+ module = get_module_by_def(right, def);
+ if (module == NULL) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "PyType_GetModuleByDef: No superclass of '%s' nor '%s' has "
+ "the given module", left->tp_name, right->tp_name);
+ }
+ }
+ return module;
+}
+
+PyObject *
+_PyType_GetModuleByDef3(PyTypeObject *left, PyTypeObject *right, PyTypeObject *third,
+ PyModuleDef *def)
+{
+ PyObject *module = get_module_by_def(left, def);
+ if (module == NULL) {
+ module = get_module_by_def(right, def);
+ if (module == NULL) {
+ module = get_module_by_def(third, def);
+ if (module == NULL) {
+ PyErr_Format(
+ PyExc_TypeError,
+ "PyType_GetModuleByDef: No superclass of '%s', '%s' nor '%s' has "
+ "the given module", left->tp_name, right->tp_name, third->tp_name);
+ }
+ }
+ }
+ return module;
}
void *
@@ -4661,15 +5122,12 @@ PyObject_GetItemData(PyObject *obj)
static PyObject *
find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
{
- Py_hash_t hash;
- if (!PyUnicode_CheckExact(name) ||
- (hash = _PyASCIIObject_CAST(name)->hash) == -1)
- {
- hash = PyObject_Hash(name);
- if (hash == -1) {
- *error = -1;
- return NULL;
- }
+ ASSERT_TYPE_LOCK_HELD();
+
+ Py_hash_t hash = _PyObject_HashFast(name);
+ if (hash == -1) {
+ *error = -1;
+ return NULL;
}
/* Look in tp_dict of types in MRO */
@@ -4697,14 +5155,13 @@ find_name_in_mro(PyTypeObject *type, PyObject *name, int *error)
PyObject *base = PyTuple_GET_ITEM(mro, i);
PyObject *dict = lookup_tp_dict(_PyType_CAST(base));
assert(dict && PyDict_Check(dict));
- res = _PyDict_GetItem_KnownHash(dict, name, hash);
- if (res != NULL) {
- break;
- }
- if (PyErr_Occurred()) {
+ if (_PyDict_GetItemRef_KnownHash((PyDictObject *)dict, name, hash, &res) < 0) {
*error = -1;
goto done;
}
+ if (res != NULL) {
+ break;
+ }
}
*error = 0;
done:
@@ -4730,10 +5187,73 @@ is_dunder_name(PyObject *name)
return 0;
}
+static PyObject *
+update_cache(struct type_cache_entry *entry, PyObject *name, unsigned int version_tag, PyObject *value)
+{
+ _Py_atomic_store_ptr_relaxed(&entry->value, value); /* borrowed */
+ assert(_PyASCIIObject_CAST(name)->hash != -1);
+ OBJECT_STAT_INC_COND(type_cache_collisions, entry->name != Py_None && entry->name != name);
+ // We're releasing this under the lock for simplicity sake because it's always a
+ // exact unicode object or Py_None so it's safe to do so.
+ PyObject *old_name = entry->name;
+ _Py_atomic_store_ptr_relaxed(&entry->name, Py_NewRef(name));
+ // We must write the version last to avoid _Py_TryXGetStackRef()
+ // operating on an invalid (already deallocated) value inside
+ // _PyType_LookupRefAndVersion(). If we write the version first then a
+ // reader could pass the "entry_version == type_version" check but could
+ // be using the old entry value.
+ _Py_atomic_store_uint32_release(&entry->version, version_tag);
+ return old_name;
+}
+
+#if Py_GIL_DISABLED
+
+static void
+update_cache_gil_disabled(struct type_cache_entry *entry, PyObject *name,
+ unsigned int version_tag, PyObject *value)
+{
+ _PySeqLock_LockWrite(&entry->sequence);
+
+ // update the entry
+ if (entry->name == name &&
+ entry->value == value &&
+ entry->version == version_tag) {
+ // We raced with another update, bail and restore previous sequence.
+ _PySeqLock_AbandonWrite(&entry->sequence);
+ return;
+ }
+
+ PyObject *old_value = update_cache(entry, name, version_tag, value);
+
+ // Then update sequence to the next valid value
+ _PySeqLock_UnlockWrite(&entry->sequence);
+
+ Py_DECREF(old_value);
+}
+
+#endif
+
+void
+_PyTypes_AfterFork(void)
+{
+#ifdef Py_GIL_DISABLED
+ struct type_cache *cache = get_type_cache();
+ for (Py_ssize_t i = 0; i < (1 << MCACHE_SIZE_EXP); i++) {
+ struct type_cache_entry *entry = &cache->hashtable[i];
+ if (_PySeqLock_AfterFork(&entry->sequence)) {
+ // Entry was in the process of updating while forking, clear it...
+ entry->value = NULL;
+ Py_SETREF(entry->name, Py_None);
+ entry->version = 0;
+ }
+ }
+#endif
+}
+
/* Internal API to look for a name through the MRO.
This returns a borrowed reference, and doesn't set an exception! */
PyObject *
-_PyType_Lookup(PyTypeObject *type, PyObject *name)
+_PyType_LookupRef(PyTypeObject *type, PyObject *name)
{
PyObject *res;
int error;
@@ -4742,20 +5262,66 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
unsigned int h = MCACHE_HASH_METHOD(type, name);
struct type_cache *cache = get_type_cache();
struct type_cache_entry *entry = &cache->hashtable[h];
+#ifdef Py_GIL_DISABLED
+ // synchronize-with other writing threads by doing an acquire load on the sequence
+ while (1) {
+ uint32_t sequence = _PySeqLock_BeginRead(&entry->sequence);
+ uint32_t entry_version = _Py_atomic_load_uint32_acquire(&entry->version);
+ uint32_t type_version = _Py_atomic_load_uint32_acquire(&type->tp_version_tag);
+ if (entry_version == type_version &&
+ _Py_atomic_load_ptr_relaxed(&entry->name) == name) {
+ OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_name(name));
+ OBJECT_STAT_INC_COND(type_cache_dunder_hits, is_dunder_name(name));
+ PyObject *value = _Py_atomic_load_ptr_relaxed(&entry->value);
+ // If the sequence is still valid then we're done
+ if (value == NULL || _Py_TryIncref(value)) {
+ if (_PySeqLock_EndRead(&entry->sequence, sequence)) {
+ return value;
+ }
+ Py_XDECREF(value);
+ }
+ else {
+ // If we can't incref the object we need to fallback to locking
+ break;
+ }
+ }
+ else {
+ // cache miss
+ break;
+ }
+ }
+#else
if (entry->version == type->tp_version_tag &&
entry->name == name) {
- assert(_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG));
+ assert(type->tp_version_tag);
OBJECT_STAT_INC_COND(type_cache_hits, !is_dunder_name(name));
OBJECT_STAT_INC_COND(type_cache_dunder_hits, is_dunder_name(name));
+ Py_XINCREF(entry->value);
return entry->value;
}
+#endif
OBJECT_STAT_INC_COND(type_cache_misses, !is_dunder_name(name));
OBJECT_STAT_INC_COND(type_cache_dunder_misses, is_dunder_name(name));
/* We may end up clearing live exceptions below, so make sure it's ours. */
assert(!PyErr_Occurred());
+ // We need to atomically do the lookup and capture the version before
+ // anyone else can modify our mro or mutate the type.
+
+ int has_version = 0;
+ int version = 0;
+ BEGIN_TYPE_LOCK();
+ // We must assign the version before doing the lookup. If
+ // find_name_in_mro() blocks and releases the critical section
+ // then the type version can change.
+ if (MCACHE_CACHEABLE_NAME(name)) {
+ has_version = assign_version_tag(interp, type);
+ version = type->tp_version_tag;
+ }
res = find_name_in_mro(type, name, &error);
+ END_TYPE_LOCK();
+
/* Only put NULL results into cache if there was no error. */
if (error) {
/* It's not ideal to clear the error condition,
@@ -4772,20 +5338,26 @@ _PyType_Lookup(PyTypeObject *type, PyObject *name)
return NULL;
}
- if (MCACHE_CACHEABLE_NAME(name) && assign_version_tag(interp, type)) {
- h = MCACHE_HASH_METHOD(type, name);
- struct type_cache_entry *entry = &cache->hashtable[h];
- entry->version = type->tp_version_tag;
- entry->value = res; /* borrowed */
- assert(_PyASCIIObject_CAST(name)->hash != -1);
- OBJECT_STAT_INC_COND(type_cache_collisions, entry->name != Py_None && entry->name != name);
- assert(_PyType_HasFeature(type, Py_TPFLAGS_VALID_VERSION_TAG));
- Py_SETREF(entry->name, Py_NewRef(name));
+ if (has_version) {
+#if Py_GIL_DISABLED
+ update_cache_gil_disabled(entry, name, version, res);
+#else
+ PyObject *old_value = update_cache(entry, name, version, res);
+ Py_DECREF(old_value);
+#endif
}
return res;
}
PyObject *
+_PyType_Lookup(PyTypeObject *type, PyObject *name)
+{
+ PyObject *res = _PyType_LookupRef(type, name);
+ Py_XDECREF(res);
+ return res;
+}
+
+PyObject *
_PyType_LookupId(PyTypeObject *type, _Py_Identifier *name)
{
PyObject *oname;
@@ -4795,8 +5367,54 @@ _PyType_LookupId(PyTypeObject *type, _Py_Identifier *name)
return _PyType_Lookup(type, oname);
}
+static void
+set_flags(PyTypeObject *self, unsigned long mask, unsigned long flags)
+{
+ ASSERT_TYPE_LOCK_HELD();
+ self->tp_flags = (self->tp_flags & ~mask) | flags;
+}
+
+void
+_PyType_SetFlags(PyTypeObject *self, unsigned long mask, unsigned long flags)
+{
+ BEGIN_TYPE_LOCK();
+ set_flags(self, mask, flags);
+ END_TYPE_LOCK();
+}
+
+static void
+set_flags_recursive(PyTypeObject *self, unsigned long mask, unsigned long flags)
+{
+ if (PyType_HasFeature(self, Py_TPFLAGS_IMMUTABLETYPE) ||
+ (self->tp_flags & mask) == flags)
+ {
+ return;
+ }
+
+ set_flags(self, mask, flags);
+
+ PyObject *children = _PyType_GetSubclasses(self);
+ if (children == NULL) {
+ return;
+ }
+
+ for (Py_ssize_t i = 0; i < PyList_GET_SIZE(children); i++) {
+ PyObject *child = PyList_GET_ITEM(children, i);
+ set_flags_recursive((PyTypeObject *)child, mask, flags);
+ }
+ Py_DECREF(children);
+}
+
+void
+_PyType_SetFlagsRecursive(PyTypeObject *self, unsigned long mask, unsigned long flags)
+{
+ BEGIN_TYPE_LOCK();
+ set_flags_recursive(self, mask, flags);
+ END_TYPE_LOCK();
+}
+
/* This is similar to PyObject_GenericGetAttr(),
- but uses _PyType_Lookup() instead of just looking in type->tp_dict.
+ but uses _PyType_LookupRef() instead of just looking in type->tp_dict.
The argument suppress_missing_attribute is used to provide a
fast path for hasattr. The possible values are:
@@ -4832,10 +5450,9 @@ _Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int * suppress_missin
meta_get = NULL;
/* Look for the attribute in the metatype */
- meta_attribute = _PyType_Lookup(metatype, name);
+ meta_attribute = _PyType_LookupRef(metatype, name);
if (meta_attribute != NULL) {
- Py_INCREF(meta_attribute);
meta_get = Py_TYPE(meta_attribute)->tp_descr_get;
if (meta_get != NULL && PyDescr_IsData(meta_attribute)) {
@@ -4852,10 +5469,9 @@ _Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int * suppress_missin
/* No data descriptor found on metatype. Look in tp_dict of this
* type and its bases */
- attribute = _PyType_Lookup(type, name);
+ attribute = _PyType_LookupRef(type, name);
if (attribute != NULL) {
/* Implement descriptor functionality, if any */
- Py_INCREF(attribute);
descrgetfunc local_get = Py_TYPE(attribute)->tp_descr_get;
Py_XDECREF(meta_attribute);
@@ -4900,16 +5516,53 @@ _Py_type_getattro_impl(PyTypeObject *type, PyObject *name, int * suppress_missin
}
/* This is similar to PyObject_GenericGetAttr(),
- but uses _PyType_Lookup() instead of just looking in type->tp_dict. */
+ but uses _PyType_LookupRef() instead of just looking in type->tp_dict. */
PyObject *
-_Py_type_getattro(PyTypeObject *type, PyObject *name)
+_Py_type_getattro(PyObject *type, PyObject *name)
{
- return _Py_type_getattro_impl(type, name, NULL);
+ return _Py_type_getattro_impl((PyTypeObject *)type, name, NULL);
}
static int
-type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
+type_update_dict(PyTypeObject *type, PyDictObject *dict, PyObject *name,
+ PyObject *value, PyObject **old_value)
{
+ // We don't want any re-entrancy between when we update the dict
+ // and call type_modified_unlocked, including running the destructor
+ // of the current value as it can observe the cache in an inconsistent
+ // state. Because we have an exact unicode and our dict has exact
+ // unicodes we know that this will all complete without releasing
+ // the locks.
+ if (_PyDict_GetItemRef_Unicode_LockHeld(dict, name, old_value) < 0) {
+ return -1;
+ }
+
+ /* Clear the VALID_VERSION flag of 'type' and all its
+ subclasses. This could possibly be unified with the
+ update_subclasses() recursion in update_slot(), but carefully:
+ they each have their own conditions on which to stop
+ recursing into subclasses. */
+ type_modified_unlocked(type);
+
+ if (_PyDict_SetItem_LockHeld(dict, name, value) < 0) {
+ PyErr_Format(PyExc_AttributeError,
+ "type object '%.50s' has no attribute '%U'",
+ ((PyTypeObject*)type)->tp_name, name);
+ _PyObject_SetAttributeErrorContext((PyObject *)type, name);
+ return -1;
+ }
+
+ if (is_dunder_name(name)) {
+ return update_slot(type, name);
+ }
+
+ return 0;
+}
+
+static int
+type_setattro(PyObject *self, PyObject *name, PyObject *value)
+{
+ PyTypeObject *type = (PyTypeObject *)self;
int res;
if (type->tp_flags & Py_TPFLAGS_IMMUTABLETYPE) {
PyErr_Format(
@@ -4918,54 +5571,73 @@ type_setattro(PyTypeObject *type, PyObject *name, PyObject *value)
name, type->tp_name);
return -1;
}
- if (PyUnicode_Check(name)) {
- if (PyUnicode_CheckExact(name)) {
- if (PyUnicode_READY(name) == -1)
- return -1;
- Py_INCREF(name);
- }
- else {
- name = _PyUnicode_Copy(name);
- if (name == NULL)
- return -1;
- }
- /* bpo-40521: Interned strings are shared by all subinterpreters */
+ if (!PyUnicode_Check(name)) {
+ PyErr_Format(PyExc_TypeError,
+ "attribute name must be string, not '%.200s'",
+ Py_TYPE(name)->tp_name);
+ return -1;
+ }
+
+ if (PyUnicode_CheckExact(name)) {
+ Py_INCREF(name);
+ }
+ else {
+ name = _PyUnicode_Copy(name);
+ if (name == NULL)
+ return -1;
+ }
+ if (!PyUnicode_CHECK_INTERNED(name)) {
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ _PyUnicode_InternMortal(interp, &name);
if (!PyUnicode_CHECK_INTERNED(name)) {
- PyInterpreterState *interp = _PyInterpreterState_GET();
- _PyUnicode_InternMortal(interp, &name);
- if (!PyUnicode_CHECK_INTERNED(name)) {
- PyErr_SetString(PyExc_MemoryError,
- "Out of memory interning an attribute name");
- Py_DECREF(name);
- return -1;
- }
+ PyErr_SetString(PyExc_MemoryError,
+ "Out of memory interning an attribute name");
+ Py_DECREF(name);
+ return -1;
}
}
- else {
- /* Will fail in _PyObject_GenericSetAttrWithDict. */
- Py_INCREF(name);
+
+ PyTypeObject *metatype = Py_TYPE(type);
+ assert(!_PyType_HasFeature(metatype, Py_TPFLAGS_INLINE_VALUES));
+ assert(!_PyType_HasFeature(metatype, Py_TPFLAGS_MANAGED_DICT));
+
+ PyObject *old_value = NULL;
+ PyObject *descr = _PyType_LookupRef(metatype, name);
+ if (descr != NULL) {
+ descrsetfunc f = Py_TYPE(descr)->tp_descr_set;
+ if (f != NULL) {
+ res = f(descr, (PyObject *)type, value);
+ goto done;
+ }
}
- res = _PyObject_GenericSetAttrWithDict((PyObject *)type, name, value, NULL);
- if (res == 0) {
- /* Clear the VALID_VERSION flag of 'type' and all its
- subclasses. This could possibly be unified with the
- update_subclasses() recursion in update_slot(), but carefully:
- they each have their own conditions on which to stop
- recursing into subclasses. */
- PyType_Modified(type);
- if (is_dunder_name(name)) {
- res = update_slot(type, name);
+ PyObject *dict = type->tp_dict;
+ if (dict == NULL) {
+ // We don't just do PyType_Ready because we could already be readying
+ BEGIN_TYPE_LOCK();
+ dict = type->tp_dict;
+ if (dict == NULL) {
+ dict = type->tp_dict = PyDict_New();
+ }
+ END_TYPE_LOCK();
+ if (dict == NULL) {
+ res = -1;
+ goto done;
}
- assert(_PyType_CheckConsistency(type));
}
+
+ BEGIN_TYPE_DICT_LOCK(dict);
+ res = type_update_dict(type, (PyDictObject *)dict, name, value, &old_value);
+ assert(_PyType_CheckConsistency(type));
+ END_TYPE_DICT_LOCK();
+
+done:
Py_DECREF(name);
+ Py_XDECREF(descr);
+ Py_XDECREF(old_value);
return res;
}
-extern void
-_PyDictKeys_DecRef(PyDictKeysObject *keys);
-
static void
type_dealloc_common(PyTypeObject *type)
@@ -4980,7 +5652,7 @@ type_dealloc_common(PyTypeObject *type)
static void
-clear_static_tp_subclasses(PyTypeObject *type)
+clear_static_tp_subclasses(PyTypeObject *type, int isbuiltin)
{
PyObject *subclasses = lookup_tp_subclasses(type);
if (subclasses == NULL) {
@@ -5007,58 +5679,90 @@ clear_static_tp_subclasses(PyTypeObject *type)
going to leak. This mostly only affects embedding scenarios.
*/
+#ifndef NDEBUG
// For now we just do a sanity check and then clear tp_subclasses.
Py_ssize_t i = 0;
PyObject *key, *ref; // borrowed ref
while (PyDict_Next(subclasses, &i, &key, &ref)) {
- PyTypeObject *subclass = type_from_ref(ref); // borrowed
+ PyTypeObject *subclass = type_from_ref(ref);
if (subclass == NULL) {
continue;
}
// All static builtin subtypes should have been finalized already.
- assert(!(subclass->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
+ assert(!isbuiltin || !(subclass->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN));
+ Py_DECREF(subclass);
}
+#else
+ (void)isbuiltin;
+#endif
clear_tp_subclasses(type);
}
static void
-clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type)
+clear_static_type_objects(PyInterpreterState *interp, PyTypeObject *type,
+ int isbuiltin, int final)
{
- if (_Py_IsMainInterpreter(interp)) {
+ if (final) {
Py_CLEAR(type->tp_cache);
}
clear_tp_dict(type);
- clear_tp_bases(type);
- clear_tp_mro(type);
- clear_static_tp_subclasses(type);
+ clear_tp_bases(type, final);
+ clear_tp_mro(type, final);
+ clear_static_tp_subclasses(type, isbuiltin);
}
-void
-_PyStaticType_Dealloc(PyInterpreterState *interp, PyTypeObject *type)
+
+static void
+fini_static_type(PyInterpreterState *interp, PyTypeObject *type,
+ int isbuiltin, int final)
{
assert(type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
- assert(_Py_IsImmortal((PyObject *)type));
+ assert(_Py_IsImmortalLoose((PyObject *)type));
type_dealloc_common(type);
- clear_static_type_objects(interp, type);
+ clear_static_type_objects(interp, type, isbuiltin, final);
- if (_Py_IsMainInterpreter(interp)) {
+ if (final) {
type->tp_flags &= ~Py_TPFLAGS_READY;
- type->tp_flags &= ~Py_TPFLAGS_VALID_VERSION_TAG;
type->tp_version_tag = 0;
}
_PyStaticType_ClearWeakRefs(interp, type);
- static_builtin_state_clear(interp, type);
+ managed_static_type_state_clear(interp, type, isbuiltin, final);
/* We leave _Py_TPFLAGS_STATIC_BUILTIN set on tp_flags. */
}
+void
+_PyTypes_FiniExtTypes(PyInterpreterState *interp)
+{
+ for (size_t i = _Py_MAX_MANAGED_STATIC_EXT_TYPES; i > 0; i--) {
+ if (interp->types.for_extensions.num_initialized == 0) {
+ break;
+ }
+ int64_t count = 0;
+ PyTypeObject *type = static_ext_type_lookup(interp, i-1, &count);
+ if (type == NULL) {
+ continue;
+ }
+ int final = (count == 1);
+ fini_static_type(interp, type, 0, final);
+ }
+}
+
+void
+_PyStaticType_FiniBuiltin(PyInterpreterState *interp, PyTypeObject *type)
+{
+ fini_static_type(interp, type, 1, _Py_IsMainInterpreter(interp));
+}
+
static void
-type_dealloc(PyTypeObject *type)
+type_dealloc(PyObject *self)
{
+ PyTypeObject *type = (PyTypeObject *)self;
+
// Assert this is a heap-allocated type object
_PyObject_ASSERT((PyObject *)type, type->tp_flags & Py_TPFLAGS_HEAPTYPE);
@@ -5080,7 +5784,7 @@ type_dealloc(PyTypeObject *type)
/* A type's tp_doc is heap allocated, unlike the tp_doc slots
* of most other objects. It's okay to cast it to char *.
*/
- PyObject_Free((char *)type->tp_doc);
+ PyMem_Free((char *)type->tp_doc);
PyHeapTypeObject *et = (PyHeapTypeObject *)type;
Py_XDECREF(et->ht_name);
@@ -5134,7 +5838,7 @@ merge_class_dict(PyObject *dict, PyObject *aclass)
assert(aclass);
/* Merge in the type's dict (if any). */
- if (_PyObject_LookupAttr(aclass, &_Py_ID(__dict__), &classdict) < 0) {
+ if (PyObject_GetOptionalAttr(aclass, &_Py_ID(__dict__), &classdict) < 0) {
return -1;
}
if (classdict != NULL) {
@@ -5145,7 +5849,7 @@ merge_class_dict(PyObject *dict, PyObject *aclass)
}
/* Recursively merge in the base types' (if any) dicts. */
- if (_PyObject_LookupAttr(aclass, &_Py_ID(__bases__), &bases) < 0) {
+ if (PyObject_GetOptionalAttr(aclass, &_Py_ID(__bases__), &bases) < 0) {
return -1;
}
if (bases != NULL) {
@@ -5229,8 +5933,10 @@ static PyMethodDef type_methods[] = {
TYPE___SUBCLASSES___METHODDEF
{"__prepare__", _PyCFunction_CAST(type_prepare),
METH_FASTCALL | METH_KEYWORDS | METH_CLASS,
- PyDoc_STR("__prepare__() -> dict\n"
- "used to create the namespace for the class statement")},
+ PyDoc_STR("__prepare__($cls, name, bases, /, **kwds)\n"
+ "--\n"
+ "\n"
+ "Create the namespace for the class statement")},
TYPE___INSTANCECHECK___METHODDEF
TYPE___SUBCLASSCHECK___METHODDEF
TYPE___DIR___METHODDEF
@@ -5243,8 +5949,10 @@ PyDoc_STRVAR(type_doc,
"type(name, bases, dict, **kwds) -> a new type");
static int
-type_traverse(PyTypeObject *type, visitproc visit, void *arg)
+type_traverse(PyObject *self, visitproc visit, void *arg)
{
+ PyTypeObject *type = (PyTypeObject *)self;
+
/* Because of type_is_gc(), the collector only calls this
for heaptypes. */
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
@@ -5272,8 +5980,10 @@ type_traverse(PyTypeObject *type, visitproc visit, void *arg)
}
static int
-type_clear(PyTypeObject *type)
+type_clear(PyObject *self)
{
+ PyTypeObject *type = (PyTypeObject *)self;
+
/* Because of type_is_gc(), the collector only calls this
for heaptypes. */
_PyObject_ASSERT((PyObject *)type, type->tp_flags & Py_TPFLAGS_HEAPTYPE);
@@ -5282,7 +5992,7 @@ type_clear(PyTypeObject *type)
the dict, so that other objects caught in a reference cycle
don't start calling destroyed methods.
- Otherwise, the we need to clear tp_mro, which is
+ Otherwise, we need to clear tp_mro, which is
part of a hard cycle (its first element is the class itself) that
won't be broken otherwise (it's a tuple and tuples don't have a
tp_clear handler).
@@ -5320,9 +6030,9 @@ type_clear(PyTypeObject *type)
}
static int
-type_is_gc(PyTypeObject *type)
+type_is_gc(PyObject *type)
{
- return type->tp_flags & Py_TPFLAGS_HEAPTYPE;
+ return ((PyTypeObject *)type)->tp_flags & Py_TPFLAGS_HEAPTYPE;
}
@@ -5335,28 +6045,28 @@ PyTypeObject PyType_Type = {
"type", /* tp_name */
sizeof(PyHeapTypeObject), /* tp_basicsize */
sizeof(PyMemberDef), /* tp_itemsize */
- (destructor)type_dealloc, /* tp_dealloc */
+ type_dealloc, /* tp_dealloc */
offsetof(PyTypeObject, tp_vectorcall), /* tp_vectorcall_offset */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)type_repr, /* tp_repr */
+ type_repr, /* tp_repr */
&type_as_number, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
- (ternaryfunc)type_call, /* tp_call */
+ type_call, /* tp_call */
0, /* tp_str */
- (getattrofunc)_Py_type_getattro, /* tp_getattro */
- (setattrofunc)type_setattro, /* tp_setattro */
+ _Py_type_getattro, /* tp_getattro */
+ type_setattro, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TYPE_SUBCLASS |
Py_TPFLAGS_HAVE_VECTORCALL |
Py_TPFLAGS_ITEMS_AT_END, /* tp_flags */
type_doc, /* tp_doc */
- (traverseproc)type_traverse, /* tp_traverse */
- (inquiry)type_clear, /* tp_clear */
+ type_traverse, /* tp_traverse */
+ type_clear, /* tp_clear */
0, /* tp_richcompare */
offsetof(PyTypeObject, tp_weaklist), /* tp_weaklistoffset */
0, /* tp_iter */
@@ -5373,7 +6083,7 @@ PyTypeObject PyType_Type = {
0, /* tp_alloc */
type_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
- (inquiry)type_is_gc, /* tp_is_gc */
+ type_is_gc, /* tp_is_gc */
.tp_vectorcall = type_vectorcall,
};
@@ -5412,12 +6122,6 @@ PyTypeObject PyType_Type = {
symmetrically, __new__() complains about excess arguments unless
__init__() is overridden and __new__() is not overridden
(IOW, if __new__() is overridden or __init__() is not overridden).
-
- However, for backwards compatibility, this breaks too much code.
- Therefore, in 2.6, we'll *warn* about excess arguments when both
- methods are overridden; for all other cases we'll use the above
- rules.
-
*/
/* Forward */
@@ -5518,10 +6222,6 @@ object_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (obj == NULL) {
return NULL;
}
- if (_PyObject_InitializeDict(obj)) {
- Py_DECREF(obj);
- return NULL;
- }
return obj;
}
@@ -5538,7 +6238,7 @@ object_repr(PyObject *self)
PyObject *mod, *name, *rtn;
type = Py_TYPE(self);
- mod = type_module(type, NULL);
+ mod = type_module(type);
if (mod == NULL)
PyErr_Clear();
else if (!PyUnicode_Check(mod)) {
@@ -5705,6 +6405,11 @@ compatible_for_assignment(PyTypeObject* oldto, PyTypeObject* newto, const char*
!same_slots_added(newbase, oldbase))) {
goto differs;
}
+ if ((oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) !=
+ ((newto->tp_flags & Py_TPFLAGS_INLINE_VALUES)))
+ {
+ goto differs;
+ }
/* The above does not check for the preheader */
if ((oldto->tp_flags & Py_TPFLAGS_PREHEADER) ==
((newto->tp_flags & Py_TPFLAGS_PREHEADER)))
@@ -5721,28 +6426,11 @@ differs:
return 0;
}
-static int
-object_set_class(PyObject *self, PyObject *value, void *closure)
-{
- if (value == NULL) {
- PyErr_SetString(PyExc_TypeError,
- "can't delete __class__ attribute");
- return -1;
- }
- if (!PyType_Check(value)) {
- PyErr_Format(PyExc_TypeError,
- "__class__ must be set to a class, not '%s' object",
- Py_TYPE(value)->tp_name);
- return -1;
- }
- PyTypeObject *newto = (PyTypeObject *)value;
-
- if (PySys_Audit("object.__setattr__", "OsO",
- self, "__class__", value) < 0) {
- return -1;
- }
+static int
+object_set_class_world_stopped(PyObject *self, PyTypeObject *newto)
+{
PyTypeObject *oldto = Py_TYPE(self);
/* In versions of CPython prior to 3.5, the code in
@@ -5807,21 +6495,28 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
if (compatible_for_assignment(oldto, newto, "__class__")) {
/* Changing the class will change the implicit dict keys,
* so we must materialize the dictionary first. */
- assert((oldto->tp_flags & Py_TPFLAGS_PREHEADER) == (newto->tp_flags & Py_TPFLAGS_PREHEADER));
- _PyObject_GetDictPtr(self);
- if (oldto->tp_flags & Py_TPFLAGS_MANAGED_DICT &&
- _PyDictOrValues_IsValues(*_PyObject_DictOrValuesPointer(self)))
- {
- /* Was unable to convert to dict */
- PyErr_NoMemory();
- return -1;
+ if (oldto->tp_flags & Py_TPFLAGS_INLINE_VALUES) {
+ PyDictObject *dict = _PyObject_GetManagedDict(self);
+ if (dict == NULL) {
+ dict = _PyObject_MaterializeManagedDict_LockHeld(self);
+ if (dict == NULL) {
+ return -1;
+ }
+ }
+
+ assert(_PyObject_GetManagedDict(self) == dict);
+
+ if (_PyDict_DetachFromObject(dict, self) < 0) {
+ return -1;
+ }
+
}
if (newto->tp_flags & Py_TPFLAGS_HEAPTYPE) {
Py_INCREF(newto);
}
+
Py_SET_TYPE(self, newto);
- if (oldto->tp_flags & Py_TPFLAGS_HEAPTYPE)
- Py_DECREF(oldto);
+
return 0;
}
else {
@@ -5829,6 +6524,48 @@ object_set_class(PyObject *self, PyObject *value, void *closure)
}
}
+static int
+object_set_class(PyObject *self, PyObject *value, void *closure)
+{
+
+ if (value == NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "can't delete __class__ attribute");
+ return -1;
+ }
+ if (!PyType_Check(value)) {
+ PyErr_Format(PyExc_TypeError,
+ "__class__ must be set to a class, not '%s' object",
+ Py_TYPE(value)->tp_name);
+ return -1;
+ }
+ PyTypeObject *newto = (PyTypeObject *)value;
+
+ if (PySys_Audit("object.__setattr__", "OsO",
+ self, "__class__", value) < 0) {
+ return -1;
+ }
+
+#ifdef Py_GIL_DISABLED
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ _PyEval_StopTheWorld(interp);
+#endif
+ PyTypeObject *oldto = Py_TYPE(self);
+ int res = object_set_class_world_stopped(self, newto);
+#ifdef Py_GIL_DISABLED
+ _PyEval_StartTheWorld(interp);
+#endif
+ if (res == 0) {
+ if (oldto->tp_flags & Py_TPFLAGS_HEAPTYPE) {
+ Py_DECREF(oldto);
+ }
+
+ RARE_EVENT_INC(set_class);
+ return 0;
+ }
+ return res;
+}
+
static PyGetSetDef object_getsets[] = {
{"__class__", object_get_class, object_set_class,
PyDoc_STR("the object's class")},
@@ -5871,24 +6608,22 @@ _PyType_GetSlotNames(PyTypeObject *cls)
/* Get the slot names from the cache in the class if possible. */
PyObject *dict = lookup_tp_dict(cls);
- slotnames = PyDict_GetItemWithError(dict, &_Py_ID(__slotnames__));
+ if (PyDict_GetItemRef(dict, &_Py_ID(__slotnames__), &slotnames) < 0) {
+ return NULL;
+ }
if (slotnames != NULL) {
if (slotnames != Py_None && !PyList_Check(slotnames)) {
PyErr_Format(PyExc_TypeError,
"%.200s.__slotnames__ should be a list or None, "
"not %.200s",
cls->tp_name, Py_TYPE(slotnames)->tp_name);
+ Py_DECREF(slotnames);
return NULL;
}
- return Py_NewRef(slotnames);
- }
- else {
- if (PyErr_Occurred()) {
- return NULL;
- }
- /* The class does not have the slot names cached yet. */
+ return slotnames;
}
+ /* The class does not have the slot names cached yet. */
copyreg = import_copyreg();
if (copyreg == NULL)
return NULL;
@@ -5981,7 +6716,7 @@ object_getstate_default(PyObject *obj, int required)
PyObject *name, *value;
name = Py_NewRef(PyList_GET_ITEM(slotnames, i));
- if (_PyObject_LookupAttr(obj, name, &value) < 0) {
+ if (PyObject_GetOptionalAttr(obj, name, &value) < 0) {
Py_DECREF(name);
goto error;
}
@@ -6367,19 +7102,8 @@ static PyObject *
object___reduce_ex___impl(PyObject *self, int protocol)
/*[clinic end generated code: output=2e157766f6b50094 input=f326b43fb8a4c5ff]*/
{
-#define objreduce \
- (_Py_INTERP_CACHED_OBJECT(_PyInterpreterState_Get(), objreduce))
- PyObject *reduce, *res;
-
- if (objreduce == NULL) {
- PyObject *dict = lookup_tp_dict(&PyBaseObject_Type);
- objreduce = PyDict_GetItemWithError(dict, &_Py_ID(__reduce__));
- if (objreduce == NULL && PyErr_Occurred()) {
- return NULL;
- }
- }
-
- if (_PyObject_LookupAttr(self, &_Py_ID(__reduce__), &reduce) < 0) {
+ PyObject *reduce;
+ if (PyObject_GetOptionalAttr(self, &_Py_ID(__reduce__), &reduce) < 0) {
return NULL;
}
if (reduce != NULL) {
@@ -6392,10 +7116,12 @@ object___reduce_ex___impl(PyObject *self, int protocol)
Py_DECREF(reduce);
return NULL;
}
- override = (clsreduce != objreduce);
+
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ override = (clsreduce != _Py_INTERP_CACHED_OBJECT(interp, objreduce));
Py_DECREF(clsreduce);
if (override) {
- res = _PyObject_CallNoArgs(reduce);
+ PyObject *res = _PyObject_CallNoArgs(reduce);
Py_DECREF(reduce);
return res;
}
@@ -6404,7 +7130,6 @@ object___reduce_ex___impl(PyObject *self, int protocol)
}
return _common_reduce(self, protocol);
-#undef objreduce
}
static PyObject *
@@ -6501,7 +7226,7 @@ object___dir___impl(PyObject *self)
PyObject *itsclass = NULL;
/* Get __dict__ (which may or may not be a real dict...) */
- if (_PyObject_LookupAttr(self, &_Py_ID(__dict__), &dict) < 0) {
+ if (PyObject_GetOptionalAttr(self, &_Py_ID(__dict__), &dict) < 0) {
return NULL;
}
if (dict == NULL) {
@@ -6521,7 +7246,7 @@ object___dir___impl(PyObject *self)
goto error;
/* Merge in attrs reachable from its class. */
- if (_PyObject_LookupAttr(self, &_Py_ID(__class__), &itsclass) < 0) {
+ if (PyObject_GetOptionalAttr(self, &_Py_ID(__class__), &itsclass) < 0) {
goto error;
}
/* XXX(tomer): Perhaps fall back to Py_TYPE(obj) if no
@@ -6541,7 +7266,7 @@ static PyMethodDef object_methods[] = {
OBJECT___REDUCE_EX___METHODDEF
OBJECT___REDUCE___METHODDEF
OBJECT___GETSTATE___METHODDEF
- {"__subclasshook__", object_subclasshook, METH_CLASS | METH_VARARGS,
+ {"__subclasshook__", object_subclasshook, METH_CLASS | METH_O,
object_subclasshook_doc},
{"__init_subclass__", object_init_subclass, METH_CLASS | METH_NOARGS,
object_init_subclass_doc},
@@ -6571,7 +7296,7 @@ PyTypeObject PyBaseObject_Type = {
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
- (hashfunc)_Py_HashPointer, /* tp_hash */
+ PyObject_GenericHash, /* tp_hash */
0, /* tp_call */
object_str, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
@@ -6644,7 +7369,7 @@ type_add_method(PyTypeObject *type, PyMethodDef *meth)
int err;
PyObject *dict = lookup_tp_dict(type);
if (!(meth->ml_flags & METH_COEXIST)) {
- err = PyDict_SetDefault(dict, name, descr) == NULL;
+ err = PyDict_SetDefaultRef(dict, name, descr, NULL) < 0;
}
else {
err = PyDict_SetItem(dict, name, descr) < 0;
@@ -6698,7 +7423,7 @@ type_add_members(PyTypeObject *type)
if (descr == NULL)
return -1;
- if (PyDict_SetDefault(dict, PyDescr_NAME(descr), descr) == NULL) {
+ if (PyDict_SetDefaultRef(dict, PyDescr_NAME(descr), descr, NULL) < 0) {
Py_DECREF(descr);
return -1;
}
@@ -6723,7 +7448,7 @@ type_add_getset(PyTypeObject *type)
return -1;
}
- if (PyDict_SetDefault(dict, PyDescr_NAME(descr), descr) == NULL) {
+ if (PyDict_SetDefaultRef(dict, PyDescr_NAME(descr), descr, NULL) < 0) {
Py_DECREF(descr);
return -1;
}
@@ -6763,28 +7488,29 @@ inherit_special(PyTypeObject *type, PyTypeObject *base)
#undef COPYVAL
/* Setup fast subclass flags */
- if (PyType_IsSubtype(base, (PyTypeObject*)PyExc_BaseException)) {
+ PyObject *mro = lookup_tp_mro(base);
+ if (is_subtype_with_mro(mro, base, (PyTypeObject*)PyExc_BaseException)) {
type->tp_flags |= Py_TPFLAGS_BASE_EXC_SUBCLASS;
}
- else if (PyType_IsSubtype(base, &PyType_Type)) {
+ else if (is_subtype_with_mro(mro, base, &PyType_Type)) {
type->tp_flags |= Py_TPFLAGS_TYPE_SUBCLASS;
}
- else if (PyType_IsSubtype(base, &PyLong_Type)) {
+ else if (is_subtype_with_mro(mro, base, &PyLong_Type)) {
type->tp_flags |= Py_TPFLAGS_LONG_SUBCLASS;
}
- else if (PyType_IsSubtype(base, &PyBytes_Type)) {
+ else if (is_subtype_with_mro(mro, base, &PyBytes_Type)) {
type->tp_flags |= Py_TPFLAGS_BYTES_SUBCLASS;
}
- else if (PyType_IsSubtype(base, &PyUnicode_Type)) {
+ else if (is_subtype_with_mro(mro, base, &PyUnicode_Type)) {
type->tp_flags |= Py_TPFLAGS_UNICODE_SUBCLASS;
}
- else if (PyType_IsSubtype(base, &PyTuple_Type)) {
+ else if (is_subtype_with_mro(mro, base, &PyTuple_Type)) {
type->tp_flags |= Py_TPFLAGS_TUPLE_SUBCLASS;
}
- else if (PyType_IsSubtype(base, &PyList_Type)) {
+ else if (is_subtype_with_mro(mro, base, &PyList_Type)) {
type->tp_flags |= Py_TPFLAGS_LIST_SUBCLASS;
}
- else if (PyType_IsSubtype(base, &PyDict_Type)) {
+ else if (is_subtype_with_mro(mro, base, &PyDict_Type)) {
type->tp_flags |= Py_TPFLAGS_DICT_SUBCLASS;
}
@@ -7089,10 +7815,10 @@ type_ready_set_type(PyTypeObject *type)
}
static int
-type_ready_set_bases(PyTypeObject *type)
+type_ready_set_bases(PyTypeObject *type, int initial)
{
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- if (!_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
+ if (!initial) {
assert(lookup_tp_bases(type) != NULL);
return 0;
}
@@ -7111,7 +7837,7 @@ type_ready_set_bases(PyTypeObject *type)
if (bases == NULL) {
return -1;
}
- set_tp_bases(type, bases);
+ set_tp_bases(type, bases, 1);
}
return 0;
}
@@ -7221,10 +7947,12 @@ type_ready_preheader(PyTypeObject *type)
}
static int
-type_ready_mro(PyTypeObject *type)
+type_ready_mro(PyTypeObject *type, int initial)
{
+ ASSERT_TYPE_LOCK_HELD();
+
if (type->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN) {
- if (!_Py_IsMainInterpreter(_PyInterpreterState_GET())) {
+ if (!initial) {
assert(lookup_tp_mro(type) != NULL);
return 0;
}
@@ -7232,7 +7960,7 @@ type_ready_mro(PyTypeObject *type)
}
/* Calculate method resolution order */
- if (mro_internal(type, NULL) < 0) {
+ if (mro_internal_unlocked(type, initial, NULL) < 0) {
return -1;
}
PyObject *mro = lookup_tp_mro(type);
@@ -7296,6 +8024,8 @@ inherit_patma_flags(PyTypeObject *type, PyTypeObject *base) {
static int
type_ready_inherit(PyTypeObject *type)
{
+ ASSERT_TYPE_LOCK_HELD();
+
/* Inherit special flags from dominant base */
PyTypeObject *base = type->tp_base;
if (base != NULL) {
@@ -7304,6 +8034,7 @@ type_ready_inherit(PyTypeObject *type)
// Inherit slots
PyObject *mro = lookup_tp_mro(type);
+ assert(mro != NULL);
Py_ssize_t n = PyTuple_GET_SIZE(mro);
for (Py_ssize_t i = 1; i < n; i++) {
PyObject *b = PyTuple_GET_ITEM(mro, i);
@@ -7385,7 +8116,7 @@ type_ready_add_subclasses(PyTypeObject *type)
// Set tp_new and the "__new__" key in the type dictionary.
// Use the Py_TPFLAGS_DISALLOW_INSTANTIATION flag.
static int
-type_ready_set_new(PyTypeObject *type, int rerunbuiltin)
+type_ready_set_new(PyTypeObject *type, int initial)
{
PyTypeObject *base = type->tp_base;
/* The condition below could use some explanation.
@@ -7407,7 +8138,7 @@ type_ready_set_new(PyTypeObject *type, int rerunbuiltin)
if (!(type->tp_flags & Py_TPFLAGS_DISALLOW_INSTANTIATION)) {
if (type->tp_new != NULL) {
- if (!rerunbuiltin || base == NULL || type->tp_new != base->tp_new) {
+ if (initial || base == NULL || type->tp_new != base->tp_new) {
// If "__new__" key does not exists in the type dictionary,
// set it to tp_new_wrapper().
if (add_tp_new_wrapper(type) < 0) {
@@ -7448,6 +8179,9 @@ type_ready_managed_dict(PyTypeObject *type)
return -1;
}
}
+ if (type->tp_itemsize == 0 && type->tp_basicsize == sizeof(PyObject)) {
+ type->tp_flags |= Py_TPFLAGS_INLINE_VALUES;
+ }
return 0;
}
@@ -7486,8 +8220,10 @@ type_ready_post_checks(PyTypeObject *type)
static int
-type_ready(PyTypeObject *type, int rerunbuiltin)
+type_ready(PyTypeObject *type, int initial)
{
+ ASSERT_TYPE_LOCK_HELD();
+
_PyObject_ASSERT((PyObject *)type, !is_readying(type));
start_readying(type);
@@ -7501,7 +8237,7 @@ type_ready(PyTypeObject *type, int rerunbuiltin)
* to get type objects into the doubly-linked list of all objects.
* Still, not all type objects go through PyType_Ready.
*/
- _Py_AddToAllObjects((PyObject *)type, 0);
+ _Py_AddToAllObjects((PyObject *)type);
#endif
/* Initialize tp_dict: _PyType_IsReady() tests if tp_dict != NULL */
@@ -7514,19 +8250,19 @@ type_ready(PyTypeObject *type, int rerunbuiltin)
if (type_ready_set_type(type) < 0) {
goto error;
}
- if (type_ready_set_bases(type) < 0) {
+ if (type_ready_set_bases(type, initial) < 0) {
goto error;
}
- if (type_ready_mro(type) < 0) {
+ if (type_ready_mro(type, initial) < 0) {
goto error;
}
- if (type_ready_set_new(type, rerunbuiltin) < 0) {
+ if (type_ready_set_new(type, initial) < 0) {
goto error;
}
if (type_ready_fill_dict(type) < 0) {
goto error;
}
- if (!rerunbuiltin) {
+ if (initial) {
if (type_ready_inherit(type) < 0) {
goto error;
}
@@ -7540,7 +8276,7 @@ type_ready(PyTypeObject *type, int rerunbuiltin)
if (type_ready_add_subclasses(type) < 0) {
goto error;
}
- if (!rerunbuiltin) {
+ if (initial) {
if (type_ready_managed_dict(type) < 0) {
goto error;
}
@@ -7573,46 +8309,72 @@ PyType_Ready(PyTypeObject *type)
/* Historically, all static types were immutable. See bpo-43908 */
if (!(type->tp_flags & Py_TPFLAGS_HEAPTYPE)) {
type->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
+ /* Static types must be immortal */
+ _Py_SetImmortalUntracked((PyObject *)type);
}
- return type_ready(type, 0);
+ int res;
+ BEGIN_TYPE_LOCK();
+ if (!(type->tp_flags & Py_TPFLAGS_READY)) {
+ res = type_ready(type, 1);
+ } else {
+ res = 0;
+ assert(_PyType_CheckConsistency(type));
+ }
+ END_TYPE_LOCK();
+ return res;
}
-int
-_PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self)
+
+static int
+init_static_type(PyInterpreterState *interp, PyTypeObject *self,
+ int isbuiltin, int initial)
{
assert(_Py_IsImmortal((PyObject *)self));
assert(!(self->tp_flags & Py_TPFLAGS_HEAPTYPE));
assert(!(self->tp_flags & Py_TPFLAGS_MANAGED_DICT));
assert(!(self->tp_flags & Py_TPFLAGS_MANAGED_WEAKREF));
- int ismain = _Py_IsMainInterpreter(interp);
if ((self->tp_flags & Py_TPFLAGS_READY) == 0) {
- assert(ismain);
+ assert(initial);
self->tp_flags |= _Py_TPFLAGS_STATIC_BUILTIN;
self->tp_flags |= Py_TPFLAGS_IMMUTABLETYPE;
assert(NEXT_GLOBAL_VERSION_TAG <= _Py_MAX_GLOBAL_TYPE_VERSION_TAG);
self->tp_version_tag = NEXT_GLOBAL_VERSION_TAG++;
- self->tp_flags |= Py_TPFLAGS_VALID_VERSION_TAG;
}
else {
- assert(!ismain);
+ assert(!initial);
assert(self->tp_flags & _Py_TPFLAGS_STATIC_BUILTIN);
- assert(self->tp_flags & Py_TPFLAGS_VALID_VERSION_TAG);
+ assert(self->tp_version_tag != 0);
}
- static_builtin_state_init(interp, self);
+ managed_static_type_state_init(interp, self, isbuiltin, initial);
- int res = type_ready(self, !ismain);
+ int res;
+ BEGIN_TYPE_LOCK();
+ res = type_ready(self, initial);
+ END_TYPE_LOCK();
if (res < 0) {
- static_builtin_state_clear(interp, self);
+ _PyStaticType_ClearWeakRefs(interp, self);
+ managed_static_type_state_clear(interp, self, isbuiltin, initial);
}
-
return res;
}
+int
+_PyStaticType_InitForExtension(PyInterpreterState *interp, PyTypeObject *self)
+{
+ return init_static_type(interp, self, 0, ((self->tp_flags & Py_TPFLAGS_READY) == 0));
+}
+
+int
+_PyStaticType_InitBuiltin(PyInterpreterState *interp, PyTypeObject *self)
+{
+ return init_static_type(interp, self, 1, _Py_IsMainInterpreter(interp));
+}
+
static int
add_subclass(PyTypeObject *base, PyTypeObject *type)
@@ -7679,10 +8441,15 @@ get_subclasses_key(PyTypeObject *type, PyTypeObject *base)
PyObject *subclasses = lookup_tp_subclasses(base);
if (subclasses != NULL) {
while (PyDict_Next(subclasses, &i, &key, &ref)) {
- PyTypeObject *subclass = type_from_ref(ref); // borrowed
+ PyTypeObject *subclass = type_from_ref(ref);
+ if (subclass == NULL) {
+ continue;
+ }
if (subclass == type) {
+ Py_DECREF(subclass);
return Py_NewRef(key);
}
+ Py_DECREF(subclass);
}
}
/* It wasn't found. */
@@ -8003,9 +8770,12 @@ wrap_delitem(PyObject *self, PyObject *args, void *wrapped)
https://mail.python.org/pipermail/python-dev/2003-April/034535.html
*/
static int
-hackcheck(PyObject *self, setattrofunc func, const char *what)
+hackcheck_unlocked(PyObject *self, setattrofunc func, const char *what)
{
PyTypeObject *type = Py_TYPE(self);
+
+ ASSERT_TYPE_LOCK_HELD();
+
PyObject *mro = lookup_tp_mro(type);
if (!mro) {
/* Probably ok not to check the call in this case. */
@@ -8047,6 +8817,20 @@ hackcheck(PyObject *self, setattrofunc func, const char *what)
return 1;
}
+static int
+hackcheck(PyObject *self, setattrofunc func, const char *what)
+{
+ if (!PyType_Check(self)) {
+ return 1;
+ }
+
+ int res;
+ BEGIN_TYPE_LOCK();
+ res = hackcheck_unlocked(self, func, what);
+ END_TYPE_LOCK();
+ return res;
+}
+
static PyObject *
wrap_setattr(PyObject *self, PyObject *args, void *wrapped)
{
@@ -8324,6 +9108,11 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds)
/* If staticbase is NULL now, it is a really weird type.
In the spirit of backwards compatibility (?), just shut up. */
if (staticbase && staticbase->tp_new != type->tp_new) {
+ if (staticbase->tp_new == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "cannot create '%s' instances", subtype->tp_name);
+ return NULL;
+ }
PyErr_Format(PyExc_TypeError,
"%s.__new__(%s) is not safe, use %s.__new__()",
type->tp_name,
@@ -8396,7 +9185,7 @@ method_is_overloaded(PyObject *left, PyObject *right, PyObject *name)
PyObject *a, *b;
int ok;
- if (_PyObject_LookupAttr((PyObject *)(Py_TYPE(right)), name, &b) < 0) {
+ if (PyObject_GetOptionalAttr((PyObject *)(Py_TYPE(right)), name, &b) < 0) {
return -1;
}
if (b == NULL) {
@@ -8404,7 +9193,7 @@ method_is_overloaded(PyObject *left, PyObject *right, PyObject *name)
return 0;
}
- if (_PyObject_LookupAttr((PyObject *)(Py_TYPE(left)), name, &a) < 0) {
+ if (PyObject_GetOptionalAttr((PyObject *)(Py_TYPE(left)), name, &a) < 0) {
Py_DECREF(b);
return -1;
}
@@ -8762,6 +9551,7 @@ slot_tp_hash(PyObject *self)
return -1;
if (!PyLong_Check(res)) {
+ Py_DECREF(res);
PyErr_SetString(PyExc_TypeError,
"__hash__ method should return an integer");
return -1;
@@ -8861,33 +9651,33 @@ _Py_slot_tp_getattr_hook(PyObject *self, PyObject *name)
/* speed hack: we could use lookup_maybe, but that would resolve the
method fully for each attribute lookup for classes with
__getattr__, even when the attribute is present. So we use
- _PyType_Lookup and create the method only when needed, with
+ _PyType_LookupRef and create the method only when needed, with
call_attribute. */
- getattr = _PyType_Lookup(tp, &_Py_ID(__getattr__));
+ getattr = _PyType_LookupRef(tp, &_Py_ID(__getattr__));
if (getattr == NULL) {
/* No __getattr__ hook: use a simpler dispatcher */
tp->tp_getattro = _Py_slot_tp_getattro;
return _Py_slot_tp_getattro(self, name);
}
- Py_INCREF(getattr);
/* speed hack: we could use lookup_maybe, but that would resolve the
method fully for each attribute lookup for classes with
__getattr__, even when self has the default __getattribute__
- method. So we use _PyType_Lookup and create the method only when
+ method. So we use _PyType_LookupRef and create the method only when
needed, with call_attribute. */
- getattribute = _PyType_Lookup(tp, &_Py_ID(__getattribute__));
+ getattribute = _PyType_LookupRef(tp, &_Py_ID(__getattribute__));
if (getattribute == NULL ||
(Py_IS_TYPE(getattribute, &PyWrapperDescr_Type) &&
((PyWrapperDescrObject *)getattribute)->d_wrapped ==
(void *)PyObject_GenericGetAttr)) {
+ Py_XDECREF(getattribute);
res = _PyObject_GenericGetAttrWithDict(self, name, NULL, 1);
/* if res == NULL with no exception set, then it must be an
AttributeError suppressed by us. */
if (res == NULL && !PyErr_Occurred()) {
res = call_attribute(self, getattr, name);
}
- } else {
- Py_INCREF(getattribute);
+ }
+ else {
res = call_attribute(self, getattribute, name);
Py_DECREF(getattribute);
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
@@ -8994,7 +9784,7 @@ slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
PyTypeObject *tp = Py_TYPE(self);
PyObject *get;
- get = _PyType_Lookup(tp, &_Py_ID(__get__));
+ get = _PyType_LookupRef(tp, &_Py_ID(__get__));
if (get == NULL) {
/* Avoid further slowdowns */
if (tp->tp_descr_get == slot_tp_descr_get)
@@ -9006,7 +9796,9 @@ slot_tp_descr_get(PyObject *self, PyObject *obj, PyObject *type)
if (type == NULL)
type = Py_None;
PyObject *stack[3] = {self, obj, type};
- return PyObject_Vectorcall(get, stack, 3, NULL);
+ PyObject *res = PyObject_Vectorcall(get, stack, 3, NULL);
+ Py_DECREF(get);
+ return res;
}
static int
@@ -9215,13 +10007,13 @@ fail:
return -1;
}
-static int
-releasebuffer_maybe_call_super(PyObject *self, Py_buffer *buffer)
+static releasebufferproc
+releasebuffer_maybe_call_super_unlocked(PyObject *self, Py_buffer *buffer)
{
PyTypeObject *self_type = Py_TYPE(self);
PyObject *mro = lookup_tp_mro(self_type);
if (mro == NULL) {
- return -1;
+ return NULL;
}
assert(PyTuple_Check(mro));
@@ -9235,9 +10027,8 @@ releasebuffer_maybe_call_super(PyObject *self, Py_buffer *buffer)
}
i++; /* skip self_type */
if (i >= n)
- return -1;
+ return NULL;
- releasebufferproc base_releasebuffer = NULL;
for (; i < n; i++) {
PyObject *obj = PyTuple_GET_ITEM(mro, i);
if (!PyType_Check(obj)) {
@@ -9247,15 +10038,25 @@ releasebuffer_maybe_call_super(PyObject *self, Py_buffer *buffer)
if (base_type->tp_as_buffer != NULL
&& base_type->tp_as_buffer->bf_releasebuffer != NULL
&& base_type->tp_as_buffer->bf_releasebuffer != slot_bf_releasebuffer) {
- base_releasebuffer = base_type->tp_as_buffer->bf_releasebuffer;
- break;
+ return base_type->tp_as_buffer->bf_releasebuffer;
}
}
+ return NULL;
+}
+
+static void
+releasebuffer_maybe_call_super(PyObject *self, Py_buffer *buffer)
+{
+ releasebufferproc base_releasebuffer;
+
+ BEGIN_TYPE_LOCK();
+ base_releasebuffer = releasebuffer_maybe_call_super_unlocked(self, buffer);
+ END_TYPE_LOCK();
+
if (base_releasebuffer != NULL) {
base_releasebuffer(self, buffer);
}
- return 0;
}
static void
@@ -9283,7 +10084,7 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
// from a Python __buffer__ function.
mv = PyMemoryView_FromBuffer(buffer);
if (mv == NULL) {
- PyErr_WriteUnraisable(self);
+ PyErr_FormatUnraisable("Exception ignored in bf_releasebuffer of %s", Py_TYPE(self)->tp_name);
goto end;
}
// Set the memoryview to restricted mode, which forbids
@@ -9296,7 +10097,7 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
PyObject *stack[2] = {self, mv};
PyObject *ret = vectorcall_method(&_Py_ID(__release_buffer__), stack, 2);
if (ret == NULL) {
- PyErr_WriteUnraisable(self);
+ PyErr_FormatUnraisable("Exception ignored in __release_buffer__ of %s", Py_TYPE(self)->tp_name);
}
else {
Py_DECREF(ret);
@@ -9304,7 +10105,7 @@ releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
if (!is_buffer_wrapper) {
PyObject *res = PyObject_CallMethodNoArgs(mv, &_Py_ID(release));
if (res == NULL) {
- PyErr_WriteUnraisable(self);
+ PyErr_FormatUnraisable("Exception ignored in bf_releasebuffer of %s", Py_TYPE(self)->tp_name);
}
else {
Py_DECREF(res);
@@ -9332,11 +10133,7 @@ static void
slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer)
{
releasebuffer_call_python(self, buffer);
- if (releasebuffer_maybe_call_super(self, buffer) < 0) {
- if (PyErr_Occurred()) {
- PyErr_WriteUnraisable(self);
- }
- }
+ releasebuffer_maybe_call_super(self, buffer);
}
static PyObject *
@@ -9472,7 +10269,8 @@ static pytype_slotdef slotdefs[] = {
TPSLOT(__getattribute__, tp_getattro, _Py_slot_tp_getattr_hook,
wrap_binaryfunc,
"__getattribute__($self, name, /)\n--\n\nReturn getattr(self, name)."),
- TPSLOT(__getattr__, tp_getattro, _Py_slot_tp_getattr_hook, NULL, ""),
+ TPSLOT(__getattr__, tp_getattro, _Py_slot_tp_getattr_hook, NULL,
+ "__getattr__($self, name, /)\n--\n\nImplement getattr(self, name)."),
TPSLOT(__setattr__, tp_setattro, slot_tp_setattro, wrap_setattr,
"__setattr__($self, name, value, /)\n--\n\nImplement setattr(self, name, value)."),
TPSLOT(__delattr__, tp_setattro, slot_tp_setattro, wrap_delattr,
@@ -9507,7 +10305,9 @@ static pytype_slotdef slotdefs[] = {
TPSLOT(__new__, tp_new, slot_tp_new, NULL,
"__new__(type, /, *args, **kwargs)\n--\n\n"
"Create and return new object. See help(type) for accurate signature."),
- TPSLOT(__del__, tp_finalize, slot_tp_finalize, (wrapperfunc)wrap_del, ""),
+ TPSLOT(__del__, tp_finalize, slot_tp_finalize, (wrapperfunc)wrap_del,
+ "__del__($self, /)\n--\n\n"
+ "Called when the instance is about to be destroyed."),
BUFSLOT(__buffer__, bf_getbuffer, slot_bf_getbuffer, wrap_buffer,
"__buffer__($self, flags, /)\n--\n\n"
@@ -9699,7 +10499,7 @@ resolve_slotdups(PyTypeObject *type, PyObject *name)
/* XXX Maybe this could be optimized more -- but is it worth it? */
/* pname and ptrs act as a little cache */
- PyInterpreterState *interp = _PyInterpreterState_Get();
+ PyInterpreterState *interp = _PyInterpreterState_GET();
#define pname _Py_INTERP_CACHED_OBJECT(interp, type_slots_pname)
#define ptrs _Py_INTERP_CACHED_OBJECT(interp, type_slots_ptrs)
pytype_slotdef *p, **pp;
@@ -9791,6 +10591,8 @@ resolve_slotdups(PyTypeObject *type, PyObject *name)
static pytype_slotdef *
update_one_slot(PyTypeObject *type, pytype_slotdef *p)
{
+ ASSERT_TYPE_LOCK_HELD();
+
PyObject *descr;
PyWrapperDescrObject *d;
@@ -9839,7 +10641,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
d = (PyWrapperDescrObject *)descr;
if ((specific == NULL || specific == d->d_wrapped) &&
d->d_base->wrapper == p->wrapper &&
- PyType_IsSubtype(type, PyDescr_TYPE(d)))
+ is_subtype_with_mro(lookup_tp_mro(type), type, PyDescr_TYPE(d)))
{
specific = d->d_wrapped;
}
@@ -9891,6 +10693,7 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
type->tp_flags &= ~Py_TPFLAGS_HAVE_VECTORCALL;
}
}
+ Py_DECREF(descr);
} while ((++p)->offset == offset);
if (specific && !use_generic)
*ptr = specific;
@@ -9904,6 +10707,8 @@ update_one_slot(PyTypeObject *type, pytype_slotdef *p)
static int
update_slots_callback(PyTypeObject *type, void *data)
{
+ ASSERT_TYPE_LOCK_HELD();
+
pytype_slotdef **pp = (pytype_slotdef **)data;
for (; *pp; pp++) {
update_one_slot(type, *pp);
@@ -9920,6 +10725,7 @@ update_slot(PyTypeObject *type, PyObject *name)
pytype_slotdef **pp;
int offset;
+ ASSERT_TYPE_LOCK_HELD();
assert(PyUnicode_CheckExact(name));
assert(PyUnicode_CHECK_INTERNED(name));
@@ -9953,10 +10759,17 @@ update_slot(PyTypeObject *type, PyObject *name)
static void
fixup_slot_dispatchers(PyTypeObject *type)
{
+ // This lock isn't strictly necessary because the type has not been
+ // exposed to anyone else yet, but update_ont_slot calls find_name_in_mro
+ // where we'd like to assert that the type is locked.
+ BEGIN_TYPE_LOCK();
+
assert(!PyErr_Occurred());
for (pytype_slotdef *p = slotdefs; p->name; ) {
p = update_one_slot(type, p);
}
+
+ END_TYPE_LOCK();
}
static void
@@ -9964,6 +10777,8 @@ update_all_slots(PyTypeObject* type)
{
pytype_slotdef *p;
+ ASSERT_TYPE_LOCK_HELD();
+
/* Clear the VALID_VERSION flag of 'type' and all its subclasses. */
PyType_Modified(type);
@@ -10026,7 +10841,8 @@ static int
type_new_init_subclass(PyTypeObject *type, PyObject *kwds)
{
PyObject *args[2] = {(PyObject *)type, (PyObject *)type};
- PyObject *super = _PyObject_FastCall((PyObject *)&PySuper_Type, args, 2);
+ PyObject *super = PyObject_Vectorcall((PyObject *)&PySuper_Type,
+ args, 2, NULL);
if (super == NULL) {
return -1;
}
@@ -10078,7 +10894,7 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
Py_ssize_t i = 0;
PyObject *ref;
while (PyDict_Next(subclasses, &i, NULL, &ref)) {
- PyTypeObject *subclass = type_from_ref(ref); // borrowed
+ PyTypeObject *subclass = type_from_ref(ref);
if (subclass == NULL) {
continue;
}
@@ -10088,16 +10904,20 @@ recurse_down_subclasses(PyTypeObject *type, PyObject *attr_name,
if (dict != NULL && PyDict_Check(dict)) {
int r = PyDict_Contains(dict, attr_name);
if (r < 0) {
+ Py_DECREF(subclass);
return -1;
}
if (r > 0) {
+ Py_DECREF(subclass);
continue;
}
}
if (update_subclasses(subclass, attr_name, callback, data) < 0) {
+ Py_DECREF(subclass);
return -1;
}
+ Py_DECREF(subclass);
}
return 0;
}
@@ -10282,11 +11102,11 @@ typedef struct {
} superobject;
static PyMemberDef super_members[] = {
- {"__thisclass__", T_OBJECT, offsetof(superobject, type), READONLY,
+ {"__thisclass__", _Py_T_OBJECT, offsetof(superobject, type), Py_READONLY,
"the class invoking super()"},
- {"__self__", T_OBJECT, offsetof(superobject, obj), READONLY,
+ {"__self__", _Py_T_OBJECT, offsetof(superobject, obj), Py_READONLY,
"the instance invoking super(); may be None"},
- {"__self_class__", T_OBJECT, offsetof(superobject, obj_type), READONLY,
+ {"__self_class__", _Py_T_OBJECT, offsetof(superobject, obj_type), Py_READONLY,
"the type of the instance invoking super(); may be None"},
{0}
};
@@ -10329,7 +11149,15 @@ _super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject *
PyObject *mro, *res;
Py_ssize_t i, n;
+ BEGIN_TYPE_LOCK();
mro = lookup_tp_mro(su_obj_type);
+ /* keep a strong reference to mro because su_obj_type->tp_mro can be
+ replaced during PyDict_GetItemRef(dict, name, &res) and because
+ another thread can modify it after we end the critical section
+ below */
+ Py_XINCREF(mro);
+ END_TYPE_LOCK();
+
if (mro == NULL)
return NULL;
@@ -10342,27 +11170,21 @@ _super_lookup_descr(PyTypeObject *su_type, PyTypeObject *su_obj_type, PyObject *
break;
}
i++; /* skip su->type (if any) */
- if (i >= n)
+ if (i >= n) {
+ Py_DECREF(mro);
return NULL;
+ }
- /* keep a strong reference to mro because su_obj_type->tp_mro can be
- replaced during PyDict_GetItemWithError(dict, name) */
- Py_INCREF(mro);
do {
PyObject *obj = PyTuple_GET_ITEM(mro, i);
PyObject *dict = lookup_tp_dict(_PyType_CAST(obj));
assert(dict != NULL && PyDict_Check(dict));
- res = PyDict_GetItemWithError(dict, name);
- if (res != NULL) {
- Py_INCREF(res);
+ if (PyDict_GetItemRef(dict, name, &res) != 0) {
+ // found or error
Py_DECREF(mro);
return res;
}
- else if (PyErr_Occurred()) {
- Py_DECREF(mro);
- return NULL;
- }
i++;
} while (i < n);
@@ -10469,7 +11291,7 @@ supercheck(PyTypeObject *type, PyObject *obj)
/* Try the slow way */
PyObject *class_attr;
- if (_PyObject_LookupAttr(obj, &_Py_ID(__class__), &class_attr) < 0) {
+ if (PyObject_GetOptionalAttr(obj, &_Py_ID(__class__), &class_attr) < 0) {
return NULL;
}
if (class_attr != NULL &&
@@ -10485,9 +11307,22 @@ supercheck(PyTypeObject *type, PyObject *obj)
Py_XDECREF(class_attr);
}
- PyErr_SetString(PyExc_TypeError,
- "super(type, obj): "
- "obj must be an instance or subtype of type");
+ const char *type_or_instance, *obj_str;
+
+ if (PyType_Check(obj)) {
+ type_or_instance = "type";
+ obj_str = ((PyTypeObject*)obj)->tp_name;
+ }
+ else {
+ type_or_instance = "instance of";
+ obj_str = Py_TYPE(obj)->tp_name;
+ }
+
+ PyErr_Format(PyExc_TypeError,
+ "super(type, obj): obj (%s %.200s) is not "
+ "an instance or subtype of type (%.200s).",
+ type_or_instance, obj_str, type->tp_name);
+
return NULL;
}
@@ -10546,7 +11381,7 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co,
return -1;
}
- assert(cframe->f_code->co_nlocalsplus > 0);
+ assert(_PyFrame_GetCode(cframe)->co_nlocalsplus > 0);
PyObject *firstarg = _PyFrame_GetLocalsArray(cframe)[0];
// The first argument might be a cell.
if (firstarg != NULL && (_PyLocals_GetKind(co->co_localspluskinds, 0) & CO_FAST_CELL)) {
@@ -10569,7 +11404,7 @@ super_init_without_args(_PyInterpreterFrame *cframe, PyCodeObject *co,
// Look for __class__ in the free vars.
PyTypeObject *type = NULL;
- int i = PyCode_GetFirstFree(co);
+ int i = PyUnstable_Code_GetFirstFree(co);
for (; i < co->co_nlocalsplus; i++) {
assert((_PyLocals_GetKind(co->co_localspluskinds, i) & CO_FAST_FREE) != 0);
PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i);
@@ -10639,7 +11474,7 @@ super_init_impl(PyObject *self, PyTypeObject *type, PyObject *obj) {
"super(): no current frame");
return -1;
}
- int res = super_init_without_args(frame, frame->f_code, &type, &obj);
+ int res = super_init_without_args(frame, _PyFrame_GetCode(frame), &type, &obj);
if (res < 0) {
return -1;
diff --git a/contrib/tools/python3/Objects/typevarobject.c b/contrib/tools/python3/Objects/typevarobject.c
index 4f0bc09637b..5caec8a73bb 100644
--- a/contrib/tools/python3/Objects/typevarobject.c
+++ b/contrib/tools/python3/Objects/typevarobject.c
@@ -1,9 +1,9 @@
// TypeVar, TypeVarTuple, and ParamSpec
#include "Python.h"
-#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
+#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
#include "pycore_typevarobject.h"
#include "pycore_unionobject.h" // _Py_union_type_or
-#include "structmember.h"
+
/*[clinic input]
class typevar "typevarobject *" "&_PyTypeVar_Type"
@@ -23,6 +23,8 @@ typedef struct {
PyObject *evaluate_bound;
PyObject *constraints;
PyObject *evaluate_constraints;
+ PyObject *default_value;
+ PyObject *evaluate_default;
bool covariant;
bool contravariant;
bool infer_variance;
@@ -31,12 +33,16 @@ typedef struct {
typedef struct {
PyObject_HEAD
PyObject *name;
+ PyObject *default_value;
+ PyObject *evaluate_default;
} typevartupleobject;
typedef struct {
PyObject_HEAD
PyObject *name;
PyObject *bound;
+ PyObject *default_value;
+ PyObject *evaluate_default;
bool covariant;
bool contravariant;
bool infer_variance;
@@ -53,6 +59,64 @@ typedef struct {
#include "clinic/typevarobject.c.h"
+/* NoDefault is a marker object to indicate that a parameter has no default. */
+
+static PyObject *
+NoDefault_repr(PyObject *op)
+{
+ return PyUnicode_FromString("typing.NoDefault");
+}
+
+static PyObject *
+NoDefault_reduce(PyObject *op, PyObject *Py_UNUSED(ignored))
+{
+ return PyUnicode_FromString("NoDefault");
+}
+
+static PyMethodDef nodefault_methods[] = {
+ {"__reduce__", NoDefault_reduce, METH_NOARGS, NULL},
+ {NULL, NULL}
+};
+
+static PyObject *
+nodefault_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ if (PyTuple_GET_SIZE(args) || (kwargs && PyDict_GET_SIZE(kwargs))) {
+ PyErr_SetString(PyExc_TypeError, "NoDefaultType takes no arguments");
+ return NULL;
+ }
+ return &_Py_NoDefaultStruct;
+}
+
+static void
+nodefault_dealloc(PyObject *nodefault)
+{
+ /* This should never get called, but we also don't want to SEGV if
+ * we accidentally decref NoDefault out of existence. Instead,
+ * since NoDefault is an immortal object, re-set the reference count.
+ */
+ _Py_SetImmortal(nodefault);
+}
+
+PyDoc_STRVAR(nodefault_doc,
+"NoDefaultType()\n"
+"--\n\n"
+"The type of the NoDefault singleton.");
+
+PyTypeObject _PyNoDefault_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "NoDefaultType",
+ .tp_dealloc = nodefault_dealloc,
+ .tp_repr = NoDefault_repr,
+ .tp_flags = Py_TPFLAGS_DEFAULT,
+ .tp_doc = nodefault_doc,
+ .tp_methods = nodefault_methods,
+ .tp_new = nodefault_new,
+};
+
+PyObject _Py_NoDefaultStruct = _PyObject_HEAD_INIT(&_PyNoDefault_Type);
+
+
static PyObject *
call_typing_func_object(const char *name, PyObject **args, size_t nargs)
{
@@ -107,7 +171,7 @@ make_union(PyObject *self, PyObject *other)
static PyObject *
caller(void)
{
- _PyInterpreterFrame *f = _PyThreadState_GET()->cframe->current_frame;
+ _PyInterpreterFrame *f = _PyThreadState_GET()->current_frame;
if (f == NULL) {
Py_RETURN_NONE;
}
@@ -144,7 +208,7 @@ static int
contains_typevartuple(PyTupleObject *params)
{
Py_ssize_t n = PyTuple_GET_SIZE(params);
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.typevartuple_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.typevartuple_type;
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *param = PyTuple_GET_ITEM(params, i);
if (Py_IS_TYPE(param, tp)) {
@@ -165,7 +229,7 @@ unpack_typevartuples(PyObject *params)
if (new_params == NULL) {
return NULL;
}
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.typevartuple_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.typevartuple_type;
for (Py_ssize_t i = 0; i < n; i++) {
PyObject *param = PyTuple_GET_ITEM(params, i);
if (Py_IS_TYPE(param, tp)) {
@@ -195,12 +259,14 @@ typevar_dealloc(PyObject *self)
_PyObject_GC_UNTRACK(self);
- Py_DECREF(tv->name);
+ Py_XDECREF(tv->name);
Py_XDECREF(tv->bound);
Py_XDECREF(tv->evaluate_bound);
Py_XDECREF(tv->constraints);
Py_XDECREF(tv->evaluate_constraints);
- _PyObject_ClearManagedDict(self);
+ Py_XDECREF(tv->default_value);
+ Py_XDECREF(tv->evaluate_default);
+ PyObject_ClearManagedDict(self);
PyObject_ClearWeakRefs(self);
Py_TYPE(self)->tp_free(self);
@@ -212,22 +278,28 @@ typevar_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
typevarobject *tv = (typevarobject *)self;
+ Py_VISIT(tv->name);
Py_VISIT(tv->bound);
Py_VISIT(tv->evaluate_bound);
Py_VISIT(tv->constraints);
Py_VISIT(tv->evaluate_constraints);
- _PyObject_VisitManagedDict(self, visit, arg);
+ Py_VISIT(tv->default_value);
+ Py_VISIT(tv->evaluate_default);
+ PyObject_VisitManagedDict(self, visit, arg);
return 0;
}
static int
typevar_clear(typevarobject *self)
{
+ Py_CLEAR(self->name);
Py_CLEAR(self->bound);
Py_CLEAR(self->evaluate_bound);
Py_CLEAR(self->constraints);
Py_CLEAR(self->evaluate_constraints);
- _PyObject_ClearManagedDict((PyObject *)self);
+ Py_CLEAR(self->default_value);
+ Py_CLEAR(self->evaluate_default);
+ PyObject_ClearManagedDict((PyObject *)self);
return 0;
}
@@ -245,10 +317,10 @@ typevar_repr(PyObject *self)
}
static PyMemberDef typevar_members[] = {
- {"__name__", T_OBJECT, offsetof(typevarobject, name), READONLY},
- {"__covariant__", T_BOOL, offsetof(typevarobject, covariant), READONLY},
- {"__contravariant__", T_BOOL, offsetof(typevarobject, contravariant), READONLY},
- {"__infer_variance__", T_BOOL, offsetof(typevarobject, infer_variance), READONLY},
+ {"__name__", _Py_T_OBJECT, offsetof(typevarobject, name), Py_READONLY},
+ {"__covariant__", Py_T_BOOL, offsetof(typevarobject, covariant), Py_READONLY},
+ {"__contravariant__", Py_T_BOOL, offsetof(typevarobject, contravariant), Py_READONLY},
+ {"__infer_variance__", Py_T_BOOL, offsetof(typevarobject, infer_variance), Py_READONLY},
{0}
};
@@ -267,6 +339,20 @@ typevar_bound(typevarobject *self, void *Py_UNUSED(ignored))
}
static PyObject *
+typevar_default(typevarobject *self, void *unused)
+{
+ if (self->default_value != NULL) {
+ return Py_NewRef(self->default_value);
+ }
+ if (self->evaluate_default == NULL) {
+ return &_Py_NoDefaultStruct;
+ }
+ PyObject *default_value = PyObject_CallNoArgs(self->evaluate_default);
+ self->default_value = Py_XNewRef(default_value);
+ return default_value;
+}
+
+static PyObject *
typevar_constraints(typevarobject *self, void *Py_UNUSED(ignored))
{
if (self->constraints != NULL) {
@@ -283,16 +369,18 @@ typevar_constraints(typevarobject *self, void *Py_UNUSED(ignored))
static PyGetSetDef typevar_getset[] = {
{"__bound__", (getter)typevar_bound, NULL, NULL, NULL},
{"__constraints__", (getter)typevar_constraints, NULL, NULL, NULL},
+ {"__default__", (getter)typevar_default, NULL, NULL, NULL},
{0}
};
static typevarobject *
typevar_alloc(PyObject *name, PyObject *bound, PyObject *evaluate_bound,
PyObject *constraints, PyObject *evaluate_constraints,
+ PyObject *default_value,
bool covariant, bool contravariant, bool infer_variance,
PyObject *module)
{
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.typevar_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.typevar_type;
assert(tp != NULL);
typevarobject *tv = PyObject_GC_New(typevarobject, tp);
if (tv == NULL) {
@@ -305,6 +393,8 @@ typevar_alloc(PyObject *name, PyObject *bound, PyObject *evaluate_bound,
tv->evaluate_bound = Py_XNewRef(evaluate_bound);
tv->constraints = Py_XNewRef(constraints);
tv->evaluate_constraints = Py_XNewRef(evaluate_constraints);
+ tv->default_value = Py_XNewRef(default_value);
+ tv->evaluate_default = NULL;
tv->covariant = covariant;
tv->contravariant = contravariant;
@@ -327,8 +417,8 @@ typevar.__new__ as typevar_new
name: object(subclass_of="&PyUnicode_Type")
*constraints: object
- *
bound: object = None
+ default as default_value: object(c_default="&_Py_NoDefaultStruct") = typing.NoDefault
covariant: bool = False
contravariant: bool = False
infer_variance: bool = False
@@ -338,9 +428,9 @@ Create a TypeVar.
static PyObject *
typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints,
- PyObject *bound, int covariant, int contravariant,
- int infer_variance)
-/*[clinic end generated code: output=1d200450ee99226d input=2c07ab87c94f462b]*/
+ PyObject *bound, PyObject *default_value, int covariant,
+ int contravariant, int infer_variance)
+/*[clinic end generated code: output=d2b248ff074eaab6 input=836f97f631d7293a]*/
{
if (covariant && contravariant) {
PyErr_SetString(PyExc_ValueError,
@@ -364,26 +454,20 @@ typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints,
}
}
- if (constraints != NULL) {
- if (!PyTuple_CheckExact(constraints)) {
- PyErr_SetString(PyExc_TypeError,
- "constraints must be a tuple");
- return NULL;
- }
- Py_ssize_t n_constraints = PyTuple_GET_SIZE(constraints);
- if (n_constraints == 1) {
- PyErr_SetString(PyExc_TypeError,
- "A single constraint is not allowed");
- Py_XDECREF(bound);
- return NULL;
- } else if (n_constraints == 0) {
- constraints = NULL;
- } else if (bound != NULL) {
- PyErr_SetString(PyExc_TypeError,
- "Constraints cannot be combined with bound=...");
- Py_XDECREF(bound);
- return NULL;
- }
+ assert(PyTuple_CheckExact(constraints));
+ Py_ssize_t n_constraints = PyTuple_GET_SIZE(constraints);
+ if (n_constraints == 1) {
+ PyErr_SetString(PyExc_TypeError,
+ "A single constraint is not allowed");
+ Py_XDECREF(bound);
+ return NULL;
+ } else if (n_constraints == 0) {
+ constraints = NULL;
+ } else if (bound != NULL) {
+ PyErr_SetString(PyExc_TypeError,
+ "Constraints cannot be combined with bound=...");
+ Py_XDECREF(bound);
+ return NULL;
}
PyObject *module = caller();
if (module == NULL) {
@@ -393,6 +477,7 @@ typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints,
PyObject *tv = (PyObject *)typevar_alloc(name, bound, NULL,
constraints, NULL,
+ default_value,
covariant, contravariant,
infer_variance, module);
Py_XDECREF(bound);
@@ -404,12 +489,13 @@ typevar_new_impl(PyTypeObject *type, PyObject *name, PyObject *constraints,
typevar.__typing_subst__ as typevar_typing_subst
arg: object
+ /
[clinic start generated code]*/
static PyObject *
-typevar_typing_subst_impl(typevarobject *self, PyObject *arg)
-/*[clinic end generated code: output=c76ced134ed8f4e1 input=6b70a4bb2da838de]*/
+typevar_typing_subst(typevarobject *self, PyObject *arg)
+/*[clinic end generated code: output=0773735e8ce18968 input=9e87b57f0fc59b92]*/
{
PyObject *args[2] = {(PyObject *)self, arg};
PyObject *result = call_typing_func_object("_typevar_subst", args, 2);
@@ -417,6 +503,66 @@ typevar_typing_subst_impl(typevarobject *self, PyObject *arg)
}
/*[clinic input]
+typevar.__typing_prepare_subst__ as typevar_typing_prepare_subst
+
+ alias: object
+ args: object
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+typevar_typing_prepare_subst_impl(typevarobject *self, PyObject *alias,
+ PyObject *args)
+/*[clinic end generated code: output=82c3f4691e0ded22 input=201a750415d14ffb]*/
+{
+ PyObject *params = PyObject_GetAttrString(alias, "__parameters__");
+ if (params == NULL) {
+ return NULL;
+ }
+ Py_ssize_t i = PySequence_Index(params, (PyObject *)self);
+ if (i == -1) {
+ Py_DECREF(params);
+ return NULL;
+ }
+ Py_ssize_t args_len = PySequence_Length(args);
+ if (args_len == -1) {
+ Py_DECREF(params);
+ return NULL;
+ }
+ if (i < args_len) {
+ // We already have a value for our TypeVar
+ Py_DECREF(params);
+ return Py_NewRef(args);
+ }
+ else if (i == args_len) {
+ // If the TypeVar has a default, use it.
+ PyObject *dflt = typevar_default(self, NULL);
+ if (dflt == NULL) {
+ Py_DECREF(params);
+ return NULL;
+ }
+ if (dflt != &_Py_NoDefaultStruct) {
+ PyObject *new_args = PyTuple_Pack(1, dflt);
+ Py_DECREF(dflt);
+ if (new_args == NULL) {
+ Py_DECREF(params);
+ return NULL;
+ }
+ PyObject *result = PySequence_Concat(args, new_args);
+ Py_DECREF(params);
+ Py_DECREF(new_args);
+ return result;
+ }
+ }
+ Py_DECREF(params);
+ PyErr_Format(PyExc_TypeError,
+ "Too few arguments for %S; actual %zd, expected at least %zd",
+ alias, args_len, i + 1);
+ return NULL;
+}
+
+/*[clinic input]
typevar.__reduce__ as typevar_reduce
[clinic start generated code]*/
@@ -428,6 +574,23 @@ typevar_reduce_impl(typevarobject *self)
return Py_NewRef(self->name);
}
+
+/*[clinic input]
+typevar.has_default as typevar_has_default
+
+[clinic start generated code]*/
+
+static PyObject *
+typevar_has_default_impl(typevarobject *self)
+/*[clinic end generated code: output=76bf0b8dc98b97dd input=31024aa030761cf6]*/
+{
+ if (self->evaluate_default != NULL ||
+ (self->default_value != &_Py_NoDefaultStruct && self->default_value != NULL)) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
static PyObject *
typevar_mro_entries(PyObject *self, PyObject *args)
{
@@ -438,7 +601,9 @@ typevar_mro_entries(PyObject *self, PyObject *args)
static PyMethodDef typevar_methods[] = {
TYPEVAR_TYPING_SUBST_METHODDEF
+ TYPEVAR_TYPING_PREPARE_SUBST_METHODDEF
TYPEVAR_REDUCE_METHODDEF
+ TYPEVAR_HAS_DEFAULT_METHODDEF
{"__mro_entries__", typevar_mro_entries, METH_O},
{0}
};
@@ -463,12 +628,18 @@ variables::\n\
class StrOrBytesSequence[A: (str, bytes)]:\n\
...\n\
\n\
+Type variables can also have defaults:\n\
+\n\
+ class IntDefault[T = int]:\n\
+ ...\n\
+\n\
However, if desired, reusable type variables can also be constructed\n\
manually, like so::\n\
\n\
T = TypeVar('T') # Can be anything\n\
S = TypeVar('S', bound=str) # Can be any subtype of str\n\
A = TypeVar('A', str, bytes) # Must be exactly str or bytes\n\
+ D = TypeVar('D', default=int) # Defaults to int\n\
\n\
Type variables exist primarily for the benefit of static type\n\
checkers. They serve as the parameters for generic types as well\n\
@@ -558,7 +729,7 @@ paramspecattr_richcompare(PyObject *a, PyObject *b, int op)
}
static PyMemberDef paramspecattr_members[] = {
- {"__origin__", T_OBJECT, offsetof(paramspecattrobject, __origin__), READONLY},
+ {"__origin__", _Py_T_OBJECT, offsetof(paramspecattrobject, __origin__), Py_READONLY},
{0}
};
@@ -579,7 +750,7 @@ paramspecargs_repr(PyObject *self)
{
paramspecattrobject *psa = (paramspecattrobject *)self;
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.paramspec_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.paramspec_type;
if (Py_IS_TYPE(psa->__origin__, tp)) {
return PyUnicode_FromFormat("%U.args",
((paramspecobject *)psa->__origin__)->name);
@@ -660,7 +831,7 @@ paramspeckwargs_repr(PyObject *self)
{
paramspecattrobject *psk = (paramspecattrobject *)self;
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.paramspec_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.paramspec_type;
if (Py_IS_TYPE(psk->__origin__, tp)) {
return PyUnicode_FromFormat("%U.kwargs",
((paramspecobject *)psk->__origin__)->name);
@@ -743,9 +914,11 @@ paramspec_dealloc(PyObject *self)
_PyObject_GC_UNTRACK(self);
- Py_DECREF(ps->name);
+ Py_XDECREF(ps->name);
Py_XDECREF(ps->bound);
- _PyObject_ClearManagedDict(self);
+ Py_XDECREF(ps->default_value);
+ Py_XDECREF(ps->evaluate_default);
+ PyObject_ClearManagedDict(self);
PyObject_ClearWeakRefs(self);
Py_TYPE(self)->tp_free(self);
@@ -757,16 +930,22 @@ paramspec_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
paramspecobject *ps = (paramspecobject *)self;
+ Py_VISIT(ps->name);
Py_VISIT(ps->bound);
- _PyObject_VisitManagedDict(self, visit, arg);
+ Py_VISIT(ps->default_value);
+ Py_VISIT(ps->evaluate_default);
+ PyObject_VisitManagedDict(self, visit, arg);
return 0;
}
static int
paramspec_clear(paramspecobject *self)
{
+ Py_CLEAR(self->name);
Py_CLEAR(self->bound);
- _PyObject_ClearManagedDict((PyObject *)self);
+ Py_CLEAR(self->default_value);
+ Py_CLEAR(self->evaluate_default);
+ PyObject_ClearManagedDict((PyObject *)self);
return 0;
}
@@ -784,39 +963,54 @@ paramspec_repr(PyObject *self)
}
static PyMemberDef paramspec_members[] = {
- {"__name__", T_OBJECT, offsetof(paramspecobject, name), READONLY},
- {"__bound__", T_OBJECT, offsetof(paramspecobject, bound), READONLY},
- {"__covariant__", T_BOOL, offsetof(paramspecobject, covariant), READONLY},
- {"__contravariant__", T_BOOL, offsetof(paramspecobject, contravariant), READONLY},
- {"__infer_variance__", T_BOOL, offsetof(paramspecobject, infer_variance), READONLY},
+ {"__name__", _Py_T_OBJECT, offsetof(paramspecobject, name), Py_READONLY},
+ {"__bound__", _Py_T_OBJECT, offsetof(paramspecobject, bound), Py_READONLY},
+ {"__covariant__", Py_T_BOOL, offsetof(paramspecobject, covariant), Py_READONLY},
+ {"__contravariant__", Py_T_BOOL, offsetof(paramspecobject, contravariant), Py_READONLY},
+ {"__infer_variance__", Py_T_BOOL, offsetof(paramspecobject, infer_variance), Py_READONLY},
{0}
};
static PyObject *
paramspec_args(PyObject *self, void *unused)
{
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.paramspecargs_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.paramspecargs_type;
return (PyObject *)paramspecattr_new(tp, self);
}
static PyObject *
paramspec_kwargs(PyObject *self, void *unused)
{
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.paramspeckwargs_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.paramspeckwargs_type;
return (PyObject *)paramspecattr_new(tp, self);
}
+static PyObject *
+paramspec_default(paramspecobject *self, void *unused)
+{
+ if (self->default_value != NULL) {
+ return Py_NewRef(self->default_value);
+ }
+ if (self->evaluate_default == NULL) {
+ return &_Py_NoDefaultStruct;
+ }
+ PyObject *default_value = PyObject_CallNoArgs(self->evaluate_default);
+ self->default_value = Py_XNewRef(default_value);
+ return default_value;
+}
+
static PyGetSetDef paramspec_getset[] = {
- {"args", (getter)paramspec_args, NULL, "Represents positional arguments.", NULL},
- {"kwargs", (getter)paramspec_kwargs, NULL, "Represents keyword arguments.", NULL},
+ {"args", (getter)paramspec_args, NULL, PyDoc_STR("Represents positional arguments."), NULL},
+ {"kwargs", (getter)paramspec_kwargs, NULL, PyDoc_STR("Represents keyword arguments."), NULL},
+ {"__default__", (getter)paramspec_default, NULL, "The default value for this ParamSpec.", NULL},
{0},
};
static paramspecobject *
-paramspec_alloc(PyObject *name, PyObject *bound, bool covariant,
+paramspec_alloc(PyObject *name, PyObject *bound, PyObject *default_value, bool covariant,
bool contravariant, bool infer_variance, PyObject *module)
{
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.paramspec_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.paramspec_type;
paramspecobject *ps = PyObject_GC_New(paramspecobject, tp);
if (ps == NULL) {
return NULL;
@@ -826,6 +1020,8 @@ paramspec_alloc(PyObject *name, PyObject *bound, bool covariant,
ps->covariant = covariant;
ps->contravariant = contravariant;
ps->infer_variance = infer_variance;
+ ps->default_value = Py_XNewRef(default_value);
+ ps->evaluate_default = NULL;
_PyObject_GC_TRACK(ps);
if (module != NULL) {
if (PyObject_SetAttrString((PyObject *)ps, "__module__", module) < 0) {
@@ -843,6 +1039,7 @@ paramspec.__new__ as paramspec_new
name: object(subclass_of="&PyUnicode_Type")
*
bound: object = None
+ default as default_value: object(c_default="&_Py_NoDefaultStruct") = typing.NoDefault
covariant: bool = False
contravariant: bool = False
infer_variance: bool = False
@@ -852,8 +1049,9 @@ Create a ParamSpec object.
static PyObject *
paramspec_new_impl(PyTypeObject *type, PyObject *name, PyObject *bound,
- int covariant, int contravariant, int infer_variance)
-/*[clinic end generated code: output=fd2daab79cba62da input=57c49c581979b952]*/
+ PyObject *default_value, int covariant, int contravariant,
+ int infer_variance)
+/*[clinic end generated code: output=47ca9d63fa5a094d input=495e1565bc067ab9]*/
{
if (covariant && contravariant) {
PyErr_SetString(PyExc_ValueError, "Bivariant types are not supported.");
@@ -875,7 +1073,7 @@ paramspec_new_impl(PyTypeObject *type, PyObject *name, PyObject *bound,
return NULL;
}
PyObject *ps = (PyObject *)paramspec_alloc(
- name, bound, covariant, contravariant, infer_variance, module);
+ name, bound, default_value, covariant, contravariant, infer_variance, module);
Py_XDECREF(bound);
Py_DECREF(module);
return ps;
@@ -886,12 +1084,13 @@ paramspec_new_impl(PyTypeObject *type, PyObject *name, PyObject *bound,
paramspec.__typing_subst__ as paramspec_typing_subst
arg: object
+ /
[clinic start generated code]*/
static PyObject *
-paramspec_typing_subst_impl(paramspecobject *self, PyObject *arg)
-/*[clinic end generated code: output=803e1ade3f13b57d input=4e0005d24023e896]*/
+paramspec_typing_subst(paramspecobject *self, PyObject *arg)
+/*[clinic end generated code: output=4c5b4aaada1c5814 input=2d5b5e3d4a717189]*/
{
PyObject *args[2] = {(PyObject *)self, arg};
PyObject *result = call_typing_func_object("_paramspec_subst", args, 2);
@@ -903,13 +1102,14 @@ paramspec.__typing_prepare_subst__ as paramspec_typing_prepare_subst
alias: object
args: object
+ /
[clinic start generated code]*/
static PyObject *
paramspec_typing_prepare_subst_impl(paramspecobject *self, PyObject *alias,
PyObject *args)
-/*[clinic end generated code: output=95449d630a2adb9a input=4375e2ffcb2ad635]*/
+/*[clinic end generated code: output=95449d630a2adb9a input=6df6f9fef3e150da]*/
{
PyObject *args_array[3] = {(PyObject *)self, alias, args};
PyObject *result = call_typing_func_object(
@@ -929,6 +1129,22 @@ paramspec_reduce_impl(paramspecobject *self)
return Py_NewRef(self->name);
}
+/*[clinic input]
+paramspec.has_default as paramspec_has_default
+
+[clinic start generated code]*/
+
+static PyObject *
+paramspec_has_default_impl(paramspecobject *self)
+/*[clinic end generated code: output=daaae7467a6a4368 input=2112e97eeb76cd59]*/
+{
+ if (self->evaluate_default != NULL ||
+ (self->default_value != &_Py_NoDefaultStruct && self->default_value != NULL)) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
static PyObject *
paramspec_mro_entries(PyObject *self, PyObject *args)
{
@@ -940,6 +1156,7 @@ paramspec_mro_entries(PyObject *self, PyObject *args)
static PyMethodDef paramspec_methods[] = {
PARAMSPEC_TYPING_SUBST_METHODDEF
PARAMSPEC_TYPING_PREPARE_SUBST_METHODDEF
+ PARAMSPEC_HAS_DEFAULT_METHODDEF
PARAMSPEC_REDUCE_METHODDEF
{"__mro_entries__", paramspec_mro_entries, METH_O},
{0}
@@ -954,10 +1171,17 @@ where the use of '**' creates a parameter specification::\n\
\n\
type IntFunc[**P] = Callable[P, int]\n\
\n\
+The following syntax creates a parameter specification that defaults\n\
+to a callable accepting two positional-only arguments of types int\n\
+and str:\n\
+\n\
+ type IntFuncDefault[**P = [int, str]] = Callable[P, int]\n\
+\n\
For compatibility with Python 3.11 and earlier, ParamSpec objects\n\
can also be created as follows::\n\
\n\
P = ParamSpec('P')\n\
+ DefaultP = ParamSpec('DefaultP', default=[int, str])\n\
\n\
Parameter specification variables exist primarily for the benefit of\n\
static type checkers. They are used to forward the parameter types of\n\
@@ -1024,8 +1248,10 @@ typevartuple_dealloc(PyObject *self)
_PyObject_GC_UNTRACK(self);
typevartupleobject *tvt = (typevartupleobject *)self;
- Py_DECREF(tvt->name);
- _PyObject_ClearManagedDict(self);
+ Py_XDECREF(tvt->name);
+ Py_XDECREF(tvt->default_value);
+ Py_XDECREF(tvt->evaluate_default);
+ PyObject_ClearManagedDict(self);
PyObject_ClearWeakRefs(self);
Py_TYPE(self)->tp_free(self);
@@ -1059,19 +1285,21 @@ typevartuple_repr(PyObject *self)
}
static PyMemberDef typevartuple_members[] = {
- {"__name__", T_OBJECT, offsetof(typevartupleobject, name), READONLY},
+ {"__name__", _Py_T_OBJECT, offsetof(typevartupleobject, name), Py_READONLY},
{0}
};
static typevartupleobject *
-typevartuple_alloc(PyObject *name, PyObject *module)
+typevartuple_alloc(PyObject *name, PyObject *module, PyObject *default_value)
{
- PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.typevartuple_type;
+ PyTypeObject *tp = _PyInterpreterState_GET()->cached_objects.typevartuple_type;
typevartupleobject *tvt = PyObject_GC_New(typevartupleobject, tp);
if (tvt == NULL) {
return NULL;
}
tvt->name = Py_NewRef(name);
+ tvt->default_value = Py_XNewRef(default_value);
+ tvt->evaluate_default = NULL;
_PyObject_GC_TRACK(tvt);
if (module != NULL) {
if (PyObject_SetAttrString((PyObject *)tvt, "__module__", module) < 0) {
@@ -1087,19 +1315,22 @@ typevartuple_alloc(PyObject *name, PyObject *module)
typevartuple.__new__
name: object(subclass_of="&PyUnicode_Type")
+ *
+ default as default_value: object(c_default="&_Py_NoDefaultStruct") = typing.NoDefault
Create a new TypeVarTuple with the given name.
[clinic start generated code]*/
static PyObject *
-typevartuple_impl(PyTypeObject *type, PyObject *name)
-/*[clinic end generated code: output=09d417a28f976202 input=00d28abcf1fc96bb]*/
+typevartuple_impl(PyTypeObject *type, PyObject *name,
+ PyObject *default_value)
+/*[clinic end generated code: output=9d6b76dfe95aae51 input=e149739929a866d0]*/
{
PyObject *module = caller();
if (module == NULL) {
return NULL;
}
- PyObject *result = (PyObject *)typevartuple_alloc(name, module);
+ PyObject *result = (PyObject *)typevartuple_alloc(name, module, default_value);
Py_DECREF(module);
return result;
}
@@ -1108,12 +1339,13 @@ typevartuple_impl(PyTypeObject *type, PyObject *name)
typevartuple.__typing_subst__ as typevartuple_typing_subst
arg: object
+ /
[clinic start generated code]*/
static PyObject *
-typevartuple_typing_subst_impl(typevartupleobject *self, PyObject *arg)
-/*[clinic end generated code: output=814316519441cd76 input=670c4e0a36e5d8c0]*/
+typevartuple_typing_subst(typevartupleobject *self, PyObject *arg)
+/*[clinic end generated code: output=237054c6d7484eea input=3fcf2dfd9eee7945]*/
{
PyErr_SetString(PyExc_TypeError, "Substitution of bare TypeVarTuple is not supported");
return NULL;
@@ -1124,13 +1356,14 @@ typevartuple.__typing_prepare_subst__ as typevartuple_typing_prepare_subst
alias: object
args: object
+ /
[clinic start generated code]*/
static PyObject *
typevartuple_typing_prepare_subst_impl(typevartupleobject *self,
PyObject *alias, PyObject *args)
-/*[clinic end generated code: output=ff999bc5b02036c1 input=a211b05f2eeb4306]*/
+/*[clinic end generated code: output=ff999bc5b02036c1 input=685b149b0fc47556]*/
{
PyObject *args_array[3] = {(PyObject *)self, alias, args};
PyObject *result = call_typing_func_object(
@@ -1150,6 +1383,23 @@ typevartuple_reduce_impl(typevartupleobject *self)
return Py_NewRef(self->name);
}
+
+/*[clinic input]
+typevartuple.has_default as typevartuple_has_default
+
+[clinic start generated code]*/
+
+static PyObject *
+typevartuple_has_default_impl(typevartupleobject *self)
+/*[clinic end generated code: output=4895f602f56a5e29 input=9ef3250ddb2c1851]*/
+{
+ if (self->evaluate_default != NULL ||
+ (self->default_value != &_Py_NoDefaultStruct && self->default_value != NULL)) {
+ Py_RETURN_TRUE;
+ }
+ Py_RETURN_FALSE;
+}
+
static PyObject *
typevartuple_mro_entries(PyObject *self, PyObject *args)
{
@@ -1162,21 +1412,49 @@ static int
typevartuple_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
- _PyObject_VisitManagedDict(self, visit, arg);
+ typevartupleobject *tvt = (typevartupleobject *)self;
+ Py_VISIT(tvt->name);
+ Py_VISIT(tvt->default_value);
+ Py_VISIT(tvt->evaluate_default);
+ PyObject_VisitManagedDict(self, visit, arg);
return 0;
}
static int
typevartuple_clear(PyObject *self)
{
- _PyObject_ClearManagedDict(self);
+ typevartupleobject *tvt = (typevartupleobject *)self;
+ Py_CLEAR(tvt->name);
+ Py_CLEAR(tvt->default_value);
+ Py_CLEAR(tvt->evaluate_default);
+ PyObject_ClearManagedDict(self);
return 0;
}
+static PyObject *
+typevartuple_default(typevartupleobject *self, void *unused)
+{
+ if (self->default_value != NULL) {
+ return Py_NewRef(self->default_value);
+ }
+ if (self->evaluate_default == NULL) {
+ return &_Py_NoDefaultStruct;
+ }
+ PyObject *default_value = PyObject_CallNoArgs(self->evaluate_default);
+ self->default_value = Py_XNewRef(default_value);
+ return default_value;
+}
+
+static PyGetSetDef typevartuple_getset[] = {
+ {"__default__", (getter)typevartuple_default, NULL, "The default value for this TypeVarTuple.", NULL},
+ {0},
+};
+
static PyMethodDef typevartuple_methods[] = {
TYPEVARTUPLE_TYPING_SUBST_METHODDEF
TYPEVARTUPLE_TYPING_PREPARE_SUBST_METHODDEF
TYPEVARTUPLE_REDUCE_METHODDEF
+ TYPEVARTUPLE_HAS_DEFAULT_METHODDEF
{"__mro_entries__", typevartuple_mro_entries, METH_O},
{0}
};
@@ -1192,10 +1470,15 @@ where a single '*' indicates a type variable tuple::\n\
def move_first_element_to_last[T, *Ts](tup: tuple[T, *Ts]) -> tuple[*Ts, T]:\n\
return (*tup[1:], tup[0])\n\
\n\
+Type variables tuples can have default values:\n\
+\n\
+ type AliasWithDefault[*Ts = (str, int)] = tuple[*Ts]\n\
+\n\
For compatibility with Python 3.11 and earlier, TypeVarTuple objects\n\
can also be created as follows::\n\
\n\
Ts = TypeVarTuple('Ts') # Can be given any name\n\
+ DefaultTs = TypeVarTuple('Ts', default=(str, int))\n\
\n\
Just as a TypeVar (type variable) is a placeholder for a single type,\n\
a TypeVarTuple is a placeholder for an *arbitrary* number of types. For\n\
@@ -1220,6 +1503,7 @@ PyType_Slot typevartuple_slots[] = {
{Py_tp_doc, (void *)typevartuple_doc},
{Py_tp_members, typevartuple_members},
{Py_tp_methods, typevartuple_methods},
+ {Py_tp_getset, typevartuple_getset},
{Py_tp_new, typevartuple},
{Py_tp_iter, typevartuple_iter},
{Py_tp_repr, typevartuple_repr},
@@ -1243,21 +1527,21 @@ PyObject *
_Py_make_typevar(PyObject *name, PyObject *evaluate_bound, PyObject *evaluate_constraints)
{
return (PyObject *)typevar_alloc(name, NULL, evaluate_bound, NULL, evaluate_constraints,
- false, false, true, NULL);
+ NULL, false, false, true, NULL);
}
PyObject *
_Py_make_paramspec(PyThreadState *Py_UNUSED(ignored), PyObject *v)
{
assert(PyUnicode_Check(v));
- return (PyObject *)paramspec_alloc(v, NULL, false, false, true, NULL);
+ return (PyObject *)paramspec_alloc(v, NULL, NULL, false, false, true, NULL);
}
PyObject *
_Py_make_typevartuple(PyThreadState *Py_UNUSED(ignored), PyObject *v)
{
assert(PyUnicode_Check(v));
- return (PyObject *)typevartuple_alloc(v, NULL);
+ return (PyObject *)typevartuple_alloc(v, NULL, NULL);
}
static void
@@ -1266,7 +1550,7 @@ typealias_dealloc(PyObject *self)
PyTypeObject *tp = Py_TYPE(self);
_PyObject_GC_UNTRACK(self);
typealiasobject *ta = (typealiasobject *)self;
- Py_DECREF(ta->name);
+ Py_XDECREF(ta->name);
Py_XDECREF(ta->type_params);
Py_XDECREF(ta->compute_value);
Py_XDECREF(ta->value);
@@ -1297,7 +1581,7 @@ typealias_repr(PyObject *self)
}
static PyMemberDef typealias_members[] = {
- {"__name__", T_OBJECT, offsetof(typealiasobject, name), READONLY},
+ {"__name__", _Py_T_OBJECT, offsetof(typealiasobject, name), Py_READONLY},
{0}
};
@@ -1384,6 +1668,7 @@ typealias_alloc(PyObject *name, PyObject *type_params, PyObject *compute_value,
static int
typealias_traverse(typealiasobject *self, visitproc visit, void *arg)
{
+ Py_VISIT(self->name);
Py_VISIT(self->type_params);
Py_VISIT(self->compute_value);
Py_VISIT(self->value);
@@ -1394,6 +1679,7 @@ typealias_traverse(typealiasobject *self, visitproc visit, void *arg)
static int
typealias_clear(typealiasobject *self)
{
+ Py_CLEAR(self->name);
Py_CLEAR(self->type_params);
Py_CLEAR(self->compute_value);
Py_CLEAR(self->value);
@@ -1614,7 +1900,7 @@ _Py_subscript_generic(PyThreadState* unused, PyObject *params)
{
params = unpack_typevartuples(params);
- PyInterpreterState *interp = PyInterpreterState_Get();
+ PyInterpreterState *interp = _PyInterpreterState_GET();
if (interp->cached_objects.generic_type == NULL) {
PyErr_SetString(PyExc_SystemError, "Cannot find Generic type");
return NULL;
@@ -1698,3 +1984,24 @@ void _Py_clear_generic_types(PyInterpreterState *interp)
Py_CLEAR(interp->cached_objects.paramspecargs_type);
Py_CLEAR(interp->cached_objects.paramspeckwargs_type);
}
+
+PyObject *
+_Py_set_typeparam_default(PyThreadState *ts, PyObject *typeparam, PyObject *evaluate_default)
+{
+ if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.typevar_type)) {
+ Py_XSETREF(((typevarobject *)typeparam)->evaluate_default, Py_NewRef(evaluate_default));
+ return Py_NewRef(typeparam);
+ }
+ else if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.paramspec_type)) {
+ Py_XSETREF(((paramspecobject *)typeparam)->evaluate_default, Py_NewRef(evaluate_default));
+ return Py_NewRef(typeparam);
+ }
+ else if (Py_IS_TYPE(typeparam, ts->interp->cached_objects.typevartuple_type)) {
+ Py_XSETREF(((typevartupleobject *)typeparam)->evaluate_default, Py_NewRef(evaluate_default));
+ return Py_NewRef(typeparam);
+ }
+ else {
+ PyErr_Format(PyExc_TypeError, "Expected a type param, got %R", typeparam);
+ return NULL;
+ }
+}
diff --git a/contrib/tools/python3/Objects/unicodectype.c b/contrib/tools/python3/Objects/unicodectype.c
index aa5c5b2a4ad..7cd0dca3d13 100644
--- a/contrib/tools/python3/Objects/unicodectype.c
+++ b/contrib/tools/python3/Objects/unicodectype.c
@@ -142,18 +142,10 @@ int _PyUnicode_IsNumeric(Py_UCS4 ch)
return (ctype->flags & NUMERIC_MASK) != 0;
}
-/* Returns 1 for Unicode characters to be hex-escaped when repr()ed,
- 0 otherwise.
- All characters except those characters defined in the Unicode character
- database as following categories are considered printable.
- * Cc (Other, Control)
- * Cf (Other, Format)
- * Cs (Other, Surrogate)
- * Co (Other, Private Use)
- * Cn (Other, Not Assigned)
- * Zl Separator, Line ('\u2028', LINE SEPARATOR)
- * Zp Separator, Paragraph ('\u2029', PARAGRAPH SEPARATOR)
- * Zs (Separator, Space) other than ASCII space('\x20').
+/* Returns 1 for Unicode characters that repr() may use in its output,
+ and 0 for characters to be hex-escaped.
+
+ See documentation of `str.isprintable` for details.
*/
int _PyUnicode_IsPrintable(Py_UCS4 ch)
{
diff --git a/contrib/tools/python3/Objects/unicodeobject.c b/contrib/tools/python3/Objects/unicodeobject.c
index 8c258666403..d6320dc192f 100644
--- a/contrib/tools/python3/Objects/unicodeobject.c
+++ b/contrib/tools/python3/Objects/unicodeobject.c
@@ -38,23 +38,26 @@ OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define PY_SSIZE_T_CLEAN
#include "Python.h"
#include "pycore_abstract.h" // _PyIndex_Check()
-#include "pycore_atomic_funcs.h" // _Py_atomic_size_get()
-#include "pycore_bytesobject.h" // _PyBytes_Repeat()
#include "pycore_bytes_methods.h" // _Py_bytes_lower()
+#include "pycore_bytesobject.h" // _PyBytes_Repeat()
+#include "pycore_ceval.h" // _PyEval_GetBuiltin()
+#include "pycore_codecs.h" // _PyCodec_Lookup()
+#include "pycore_critical_section.h" // Py_*_CRITICAL_SECTION_SEQUENCE_FAST
#include "pycore_format.h" // F_LJUST
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_interp.h" // PyInterpreterState.fs_codec
#include "pycore_long.h" // _PyLong_FormatWriter()
#include "pycore_object.h" // _PyObject_GC_TRACK(), _Py_FatalRefcountError()
#include "pycore_pathconfig.h" // _Py_DumpPathConfig()
+#include "pycore_pyerrors.h" // _PyUnicodeTranslateError_Create()
#include "pycore_pylifecycle.h" // _Py_SetFileSystemEncoding()
#include "pycore_pystate.h" // _PyInterpreterState_GET()
#include "pycore_ucnhash.h" // _PyUnicode_Name_CAPI
#include "pycore_unicodeobject.h" // struct _Py_unicode_state
#include "pycore_unicodeobject_generated.h" // _PyUnicode_InitStaticStrings()
+
#include "stringlib/eq.h" // unicode_eq()
#include <stddef.h> // ptrdiff_t
@@ -98,11 +101,6 @@ NOTE: In the interpreter's initialization phase, some globals are currently
*/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
// Maximum code point of Unicode 6.0: 0x10ffff (1,114,111).
// The value must be the same in fileutils.c.
#define MAX_UNICODE 0x10ffff
@@ -113,20 +111,42 @@ extern "C" {
# define _PyUnicode_CHECK(op) PyUnicode_Check(op)
#endif
-#define _PyUnicode_UTF8(op) \
- (_PyCompactUnicodeObject_CAST(op)->utf8)
-#define PyUnicode_UTF8(op) \
- (assert(_PyUnicode_CHECK(op)), \
- PyUnicode_IS_COMPACT_ASCII(op) ? \
- ((char*)(_PyASCIIObject_CAST(op) + 1)) : \
- _PyUnicode_UTF8(op))
-#define _PyUnicode_UTF8_LENGTH(op) \
- (_PyCompactUnicodeObject_CAST(op)->utf8_length)
-#define PyUnicode_UTF8_LENGTH(op) \
- (assert(_PyUnicode_CHECK(op)), \
- PyUnicode_IS_COMPACT_ASCII(op) ? \
- _PyASCIIObject_CAST(op)->length : \
- _PyUnicode_UTF8_LENGTH(op))
+static inline char* _PyUnicode_UTF8(PyObject *op)
+{
+ return FT_ATOMIC_LOAD_PTR_ACQUIRE(_PyCompactUnicodeObject_CAST(op)->utf8);
+}
+
+static inline char* PyUnicode_UTF8(PyObject *op)
+{
+ assert(_PyUnicode_CHECK(op));
+ if (PyUnicode_IS_COMPACT_ASCII(op)) {
+ return ((char*)(_PyASCIIObject_CAST(op) + 1));
+ }
+ else {
+ return _PyUnicode_UTF8(op);
+ }
+}
+
+static inline void PyUnicode_SET_UTF8(PyObject *op, char *utf8)
+{
+ FT_ATOMIC_STORE_PTR_RELEASE(_PyCompactUnicodeObject_CAST(op)->utf8, utf8);
+}
+
+static inline Py_ssize_t PyUnicode_UTF8_LENGTH(PyObject *op)
+{
+ assert(_PyUnicode_CHECK(op));
+ if (PyUnicode_IS_COMPACT_ASCII(op)) {
+ return _PyASCIIObject_CAST(op)->length;
+ }
+ else {
+ return _PyCompactUnicodeObject_CAST(op)->utf8_length;
+ }
+}
+
+static inline void PyUnicode_SET_UTF8_LENGTH(PyObject *op, Py_ssize_t length)
+{
+ _PyCompactUnicodeObject_CAST(op)->utf8_length = length;
+}
#define _PyUnicode_LENGTH(op) \
(_PyASCIIObject_CAST(op)->length)
@@ -134,26 +154,37 @@ extern "C" {
(_PyASCIIObject_CAST(op)->state)
#define _PyUnicode_HASH(op) \
(_PyASCIIObject_CAST(op)->hash)
-#define _PyUnicode_KIND(op) \
- (assert(_PyUnicode_CHECK(op)), \
- _PyASCIIObject_CAST(op)->state.kind)
-#define _PyUnicode_GET_LENGTH(op) \
- (assert(_PyUnicode_CHECK(op)), \
- _PyASCIIObject_CAST(op)->length)
+
+static inline Py_hash_t PyUnicode_HASH(PyObject *op)
+{
+ assert(_PyUnicode_CHECK(op));
+ return FT_ATOMIC_LOAD_SSIZE_RELAXED(_PyASCIIObject_CAST(op)->hash);
+}
+
+static inline void PyUnicode_SET_HASH(PyObject *op, Py_hash_t hash)
+{
+ FT_ATOMIC_STORE_SSIZE_RELAXED(_PyASCIIObject_CAST(op)->hash, hash);
+}
+
#define _PyUnicode_DATA_ANY(op) \
(_PyUnicodeObject_CAST(op)->data.any)
-#define _PyUnicode_SHARE_UTF8(op) \
- (assert(_PyUnicode_CHECK(op)), \
- assert(!PyUnicode_IS_COMPACT_ASCII(op)), \
- (_PyUnicode_UTF8(op) == PyUnicode_DATA(op)))
+static inline int _PyUnicode_SHARE_UTF8(PyObject *op)
+{
+ assert(_PyUnicode_CHECK(op));
+ assert(!PyUnicode_IS_COMPACT_ASCII(op));
+ return (_PyUnicode_UTF8(op) == PyUnicode_DATA(op));
+}
/* true if the Unicode object has an allocated UTF-8 memory block
(not shared with other data) */
-#define _PyUnicode_HAS_UTF8_MEMORY(op) \
- ((!PyUnicode_IS_COMPACT_ASCII(op) \
- && _PyUnicode_UTF8(op) \
- && _PyUnicode_UTF8(op) != PyUnicode_DATA(op)))
+static inline int _PyUnicode_HAS_UTF8_MEMORY(PyObject *op)
+{
+ return (!PyUnicode_IS_COMPACT_ASCII(op)
+ && _PyUnicode_UTF8(op) != NULL
+ && _PyUnicode_UTF8(op) != PyUnicode_DATA(op));
+}
+
/* Generic helper macro to convert characters of different types.
from_type and to_type have to be valid type names, begin and end
@@ -207,21 +238,13 @@ static int unicode_is_singleton(PyObject *unicode);
#endif
-// Return a borrowed reference to the empty string singleton.
+// Return a reference to the immortal empty string singleton.
static inline PyObject* unicode_get_empty(void)
{
_Py_DECLARE_STR(empty, "");
return &_Py_STR(empty);
}
-
-// Return a strong reference to the empty string singleton.
-static inline PyObject* unicode_new_empty(void)
-{
- PyObject *empty = unicode_get_empty();
- return Py_NewRef(empty);
-}
-
/* This dictionary holds per-interpreter interned strings.
* See InternalDocs/string_interning.md for details.
*/
@@ -391,7 +414,7 @@ static void clear_global_interned_strings(void)
#define _Py_RETURN_UNICODE_EMPTY() \
do { \
- return unicode_new_empty(); \
+ return unicode_get_empty(); \
} while (0)
static inline void
@@ -660,7 +683,7 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content)
|| kind == PyUnicode_2BYTE_KIND
|| kind == PyUnicode_4BYTE_KIND);
CHECK(ascii->state.ascii == 0);
- CHECK(compact->utf8 != data);
+ CHECK(_PyUnicode_UTF8(op) != data);
}
else {
PyUnicodeObject *unicode = _PyUnicodeObject_CAST(op);
@@ -672,16 +695,17 @@ _PyUnicode_CheckConsistency(PyObject *op, int check_content)
CHECK(ascii->state.compact == 0);
CHECK(data != NULL);
if (ascii->state.ascii) {
- CHECK(compact->utf8 == data);
+ CHECK(_PyUnicode_UTF8(op) == data);
CHECK(compact->utf8_length == ascii->length);
}
else {
- CHECK(compact->utf8 != data);
+ CHECK(_PyUnicode_UTF8(op) != data);
}
}
-
- if (compact->utf8 == NULL)
+#ifndef Py_GIL_DISABLED
+ if (_PyUnicode_UTF8(op) == NULL)
CHECK(compact->utf8_length == 0);
+#endif
}
/* check that the best kind is used: O(n) operation */
@@ -766,7 +790,6 @@ unicode_result(PyObject *unicode)
PyObject *empty = unicode_get_empty();
if (unicode != empty) {
Py_DECREF(unicode);
- Py_INCREF(empty);
}
return empty;
}
@@ -778,7 +801,6 @@ unicode_result(PyObject *unicode)
Py_UCS1 ch = data[0];
PyObject *latin1_char = LATIN1(ch);
if (unicode != latin1_char) {
- Py_INCREF(latin1_char);
Py_DECREF(unicode);
}
return latin1_char;
@@ -1100,6 +1122,21 @@ unicode_fill_invalid(PyObject *unicode, Py_ssize_t old_length)
#endif
static PyObject*
+resize_copy(PyObject *unicode, Py_ssize_t length)
+{
+ Py_ssize_t copy_length;
+ PyObject *copy;
+
+ copy = PyUnicode_New(length, PyUnicode_MAX_CHAR_VALUE(unicode));
+ if (copy == NULL)
+ return NULL;
+
+ copy_length = Py_MIN(length, PyUnicode_GET_LENGTH(unicode));
+ _PyUnicode_FastCopyCharacters(copy, 0, unicode, 0, copy_length);
+ return copy;
+}
+
+static PyObject*
resize_compact(PyObject *unicode, Py_ssize_t length)
{
Py_ssize_t char_size;
@@ -1110,7 +1147,14 @@ resize_compact(PyObject *unicode, Py_ssize_t length)
Py_ssize_t old_length = _PyUnicode_LENGTH(unicode);
#endif
- assert(unicode_modifiable(unicode));
+ if (!unicode_modifiable(unicode)) {
+ PyObject *copy = resize_copy(unicode, length);
+ if (copy == NULL) {
+ return NULL;
+ }
+ Py_DECREF(unicode);
+ return copy;
+ }
assert(PyUnicode_IS_COMPACT(unicode));
char_size = PyUnicode_KIND(unicode);
@@ -1126,13 +1170,14 @@ resize_compact(PyObject *unicode, Py_ssize_t length)
new_size = (struct_size + (length + 1) * char_size);
if (_PyUnicode_HAS_UTF8_MEMORY(unicode)) {
- PyObject_Free(_PyUnicode_UTF8(unicode));
- _PyUnicode_UTF8(unicode) = NULL;
- _PyUnicode_UTF8_LENGTH(unicode) = 0;
+ PyMem_Free(_PyUnicode_UTF8(unicode));
+ PyUnicode_SET_UTF8_LENGTH(unicode, 0);
+ PyUnicode_SET_UTF8(unicode, NULL);
}
#ifdef Py_TRACE_REFS
_Py_ForgetReference(unicode);
#endif
+ _PyReftracerTrack(unicode, PyRefTracer_DESTROY);
new_unicode = (PyObject *)PyObject_Realloc(unicode, new_size);
if (new_unicode == NULL) {
@@ -1179,9 +1224,9 @@ resize_inplace(PyObject *unicode, Py_ssize_t length)
if (!share_utf8 && _PyUnicode_HAS_UTF8_MEMORY(unicode))
{
- PyObject_Free(_PyUnicode_UTF8(unicode));
- _PyUnicode_UTF8(unicode) = NULL;
- _PyUnicode_UTF8_LENGTH(unicode) = 0;
+ PyMem_Free(_PyUnicode_UTF8(unicode));
+ PyUnicode_SET_UTF8_LENGTH(unicode, 0);
+ PyUnicode_SET_UTF8(unicode, NULL);
}
data = (PyObject *)PyObject_Realloc(data, new_size);
@@ -1191,8 +1236,8 @@ resize_inplace(PyObject *unicode, Py_ssize_t length)
}
_PyUnicode_DATA_ANY(unicode) = data;
if (share_utf8) {
- _PyUnicode_UTF8(unicode) = data;
- _PyUnicode_UTF8_LENGTH(unicode) = length;
+ PyUnicode_SET_UTF8_LENGTH(unicode, length);
+ PyUnicode_SET_UTF8(unicode, data);
}
_PyUnicode_LENGTH(unicode) = length;
PyUnicode_WRITE(PyUnicode_KIND(unicode), data, length, 0);
@@ -1209,21 +1254,6 @@ resize_inplace(PyObject *unicode, Py_ssize_t length)
return 0;
}
-static PyObject*
-resize_copy(PyObject *unicode, Py_ssize_t length)
-{
- Py_ssize_t copy_length;
- PyObject *copy;
-
- copy = PyUnicode_New(length, PyUnicode_MAX_CHAR_VALUE(unicode));
- if (copy == NULL)
- return NULL;
-
- copy_length = Py_MIN(length, PyUnicode_GET_LENGTH(unicode));
- _PyUnicode_FastCopyCharacters(copy, 0, unicode, 0, copy_length);
- return copy;
-}
-
static const char*
unicode_kind_name(PyObject *unicode)
{
@@ -1315,7 +1345,7 @@ PyUnicode_New(Py_ssize_t size, Py_UCS4 maxchar)
{
/* Optimization for empty strings */
if (size == 0) {
- return unicode_new_empty();
+ return unicode_get_empty();
}
PyObject *obj;
@@ -1422,12 +1452,12 @@ unicode_convert_wchar_to_ucs4(const wchar_t *begin, const wchar_t *end,
assert(unicode != NULL);
assert(_PyUnicode_CHECK(unicode));
- assert(_PyUnicode_KIND(unicode) == PyUnicode_4BYTE_KIND);
+ assert(PyUnicode_KIND(unicode) == PyUnicode_4BYTE_KIND);
ucs4_out = PyUnicode_4BYTE_DATA(unicode);
for (iter = begin; iter < end; ) {
assert(ucs4_out < (PyUnicode_4BYTE_DATA(unicode) +
- _PyUnicode_GET_LENGTH(unicode)));
+ PyUnicode_GET_LENGTH(unicode)));
if (Py_UNICODE_IS_HIGH_SURROGATE(iter[0])
&& (iter+1) < end
&& Py_UNICODE_IS_LOW_SURROGATE(iter[1]))
@@ -1441,7 +1471,7 @@ unicode_convert_wchar_to_ucs4(const wchar_t *begin, const wchar_t *end,
}
}
assert(ucs4_out == (PyUnicode_4BYTE_DATA(unicode) +
- _PyUnicode_GET_LENGTH(unicode)));
+ PyUnicode_GET_LENGTH(unicode)));
}
#endif
@@ -1728,39 +1758,48 @@ unicode_dealloc(PyObject *unicode)
break;
case SSTATE_INTERNED_MORTAL:
/* Remove the object from the intern dict.
- * Before doing so, we set the refcount to 3: the key and value
- * in the interned_dict, plus one to work with.
+ * Before doing so, we set the refcount to 2: the key and value
+ * in the interned_dict.
*/
assert(Py_REFCNT(unicode) == 0);
- Py_SET_REFCNT(unicode, 3);
+ Py_SET_REFCNT(unicode, 2);
#ifdef Py_REF_DEBUG
/* let's be pedantic with the ref total */
- _Py_IncRefTotal(_PyInterpreterState_GET());
- _Py_IncRefTotal(_PyInterpreterState_GET());
- _Py_IncRefTotal(_PyInterpreterState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
#endif
PyInterpreterState *interp = _PyInterpreterState_GET();
PyObject *interned = get_interned_dict(interp);
assert(interned != NULL);
- int r = PyDict_DelItem(interned, unicode);
+ PyObject *popped;
+ int r = PyDict_Pop(interned, unicode, &popped);
if (r == -1) {
PyErr_WriteUnraisable(unicode);
// We don't know what happened to the string. It's probably
// best to leak it:
- // - if it was not found, something is very wrong
- // - if it was deleted, there are no more references to it
+ // - if it was popped, there are no more references to it
// so it can't cause trouble (except wasted memory)
- // - if it wasn't deleted, it'll remain interned
+ // - if it wasn't popped, it'll remain interned
_Py_SetImmortal(unicode);
_PyUnicode_STATE(unicode).interned = SSTATE_INTERNED_IMMORTAL;
return;
}
- // Only our work reference should be left; remove it too.
+ if (r == 0) {
+ // The interned string was not found in the interned_dict.
+#ifdef Py_DEBUG
+ Py_UNREACHABLE();
+#endif
+ _Py_SetImmortal(unicode);
+ return;
+ }
+ // Successfully popped.
+ assert(popped == unicode);
+ // Only our `popped` reference should be left; remove it too.
assert(Py_REFCNT(unicode) == 1);
Py_SET_REFCNT(unicode, 0);
#ifdef Py_REF_DEBUG
/* let's be pedantic with the ref total */
- _Py_DecRefTotal(_PyInterpreterState_GET());
+ _Py_DecRefTotal(_PyThreadState_GET());
#endif
break;
default:
@@ -1772,10 +1811,10 @@ unicode_dealloc(PyObject *unicode)
return;
}
if (_PyUnicode_HAS_UTF8_MEMORY(unicode)) {
- PyObject_Free(_PyUnicode_UTF8(unicode));
+ PyMem_Free(_PyUnicode_UTF8(unicode));
}
if (!PyUnicode_IS_COMPACT(unicode) && _PyUnicode_DATA_ANY(unicode)) {
- PyObject_Free(_PyUnicode_DATA_ANY(unicode));
+ PyMem_Free(_PyUnicode_DATA_ANY(unicode));
}
Py_TYPE(unicode)->tp_free(unicode);
@@ -1804,9 +1843,9 @@ static int
unicode_modifiable(PyObject *unicode)
{
assert(_PyUnicode_CHECK(unicode));
- if (Py_REFCNT(unicode) != 1)
+ if (!_PyObject_IsUniquelyReferenced(unicode))
return 0;
- if (_PyUnicode_HASH(unicode) != -1)
+ if (PyUnicode_HASH(unicode) != -1)
return 0;
if (PyUnicode_CHECK_INTERNED(unicode))
return 0;
@@ -1837,7 +1876,7 @@ unicode_resize(PyObject **p_unicode, Py_ssize_t length)
return 0;
if (length == 0) {
- PyObject *empty = unicode_new_empty();
+ PyObject *empty = unicode_get_empty();
Py_SETREF(*p_unicode, empty);
return 0;
}
@@ -2016,14 +2055,14 @@ PyUnicode_FromWideChar(const wchar_t *u, Py_ssize_t size)
switch (PyUnicode_KIND(unicode)) {
case PyUnicode_1BYTE_KIND:
- _PyUnicode_CONVERT_BYTES(Py_UNICODE, unsigned char,
+ _PyUnicode_CONVERT_BYTES(wchar_t, unsigned char,
u, u + size, PyUnicode_1BYTE_DATA(unicode));
break;
case PyUnicode_2BYTE_KIND:
#if Py_UNICODE_SIZE == 2
memcpy(PyUnicode_2BYTE_DATA(unicode), u, size * 2);
#else
- _PyUnicode_CONVERT_BYTES(Py_UNICODE, Py_UCS2,
+ _PyUnicode_CONVERT_BYTES(wchar_t, Py_UCS2,
u, u + size, PyUnicode_2BYTE_DATA(unicode));
#endif
break;
@@ -2060,7 +2099,7 @@ PyUnicode_FromStringAndSize(const char *u, Py_ssize_t size)
"NULL string with positive size with NULL passed to PyUnicode_FromStringAndSize");
return NULL;
}
- return unicode_new_empty();
+ return unicode_get_empty();
}
PyObject *
@@ -2083,24 +2122,25 @@ PyUnicode_FromString(const char *u)
PyObject *
_PyUnicode_FromId(_Py_Identifier *id)
{
+ PyMutex_Lock((PyMutex *)&id->mutex);
PyInterpreterState *interp = _PyInterpreterState_GET();
struct _Py_unicode_ids *ids = &interp->unicode.ids;
- Py_ssize_t index = _Py_atomic_size_get(&id->index);
+ Py_ssize_t index = _Py_atomic_load_ssize(&id->index);
if (index < 0) {
struct _Py_unicode_runtime_ids *rt_ids = &interp->runtime->unicode_state.ids;
- PyThread_acquire_lock(rt_ids->lock, WAIT_LOCK);
+ PyMutex_Lock(&rt_ids->mutex);
// Check again to detect concurrent access. Another thread can have
// initialized the index while this thread waited for the lock.
- index = _Py_atomic_size_get(&id->index);
+ index = _Py_atomic_load_ssize(&id->index);
if (index < 0) {
assert(rt_ids->next_index < PY_SSIZE_T_MAX);
index = rt_ids->next_index;
rt_ids->next_index++;
- _Py_atomic_size_set(&id->index, index);
+ _Py_atomic_store_ssize(&id->index, index);
}
- PyThread_release_lock(rt_ids->lock);
+ PyMutex_Unlock(&rt_ids->mutex);
}
assert(index >= 0);
@@ -2109,14 +2149,14 @@ _PyUnicode_FromId(_Py_Identifier *id)
obj = ids->array[index];
if (obj) {
// Return a borrowed reference
- return obj;
+ goto end;
}
}
obj = PyUnicode_DecodeUTF8Stateful(id->string, strlen(id->string),
NULL, NULL);
if (!obj) {
- return NULL;
+ goto end;
}
_PyUnicode_InternImmortal(interp, &obj);
@@ -2127,7 +2167,8 @@ _PyUnicode_FromId(_Py_Identifier *id)
PyObject **new_array = PyMem_Realloc(ids->array, new_size * item_size);
if (new_array == NULL) {
PyErr_NoMemory();
- return NULL;
+ obj = NULL;
+ goto end;
}
memset(&new_array[ids->size], 0, (new_size - ids->size) * item_size);
ids->array = new_array;
@@ -2137,6 +2178,8 @@ _PyUnicode_FromId(_Py_Identifier *id)
// The array stores a strong reference
ids->array[index] = obj;
+end:
+ PyMutex_Unlock((PyMutex *)&id->mutex);
// Return a borrowed reference
return obj;
}
@@ -2650,6 +2693,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
switch (*f++) {
case '-': flags |= F_LJUST; continue;
case '0': flags |= F_ZERO; continue;
+ case '#': flags |= F_ALT; continue;
}
f--;
break;
@@ -2973,6 +3017,62 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
break;
}
+ case 'T':
+ {
+ PyObject *obj = va_arg(*vargs, PyObject *);
+ PyTypeObject *type = (PyTypeObject *)Py_NewRef(Py_TYPE(obj));
+
+ PyObject *type_name;
+ if (flags & F_ALT) {
+ type_name = _PyType_GetFullyQualifiedName(type, ':');
+ }
+ else {
+ type_name = PyType_GetFullyQualifiedName(type);
+ }
+ Py_DECREF(type);
+ if (!type_name) {
+ return NULL;
+ }
+
+ if (unicode_fromformat_write_str(writer, type_name,
+ width, precision, flags) == -1) {
+ Py_DECREF(type_name);
+ return NULL;
+ }
+ Py_DECREF(type_name);
+ break;
+ }
+
+ case 'N':
+ {
+ PyObject *type_raw = va_arg(*vargs, PyObject *);
+ assert(type_raw != NULL);
+
+ if (!PyType_Check(type_raw)) {
+ PyErr_SetString(PyExc_TypeError, "%N argument must be a type");
+ return NULL;
+ }
+ PyTypeObject *type = (PyTypeObject*)type_raw;
+
+ PyObject *type_name;
+ if (flags & F_ALT) {
+ type_name = _PyType_GetFullyQualifiedName(type, ':');
+ }
+ else {
+ type_name = PyType_GetFullyQualifiedName(type);
+ }
+ if (!type_name) {
+ return NULL;
+ }
+ if (unicode_fromformat_write_str(writer, type_name,
+ width, precision, flags) == -1) {
+ Py_DECREF(type_name);
+ return NULL;
+ }
+ Py_DECREF(type_name);
+ break;
+ }
+
default:
invalid_format:
PyErr_Format(PyExc_SystemError, "invalid format string: %s", p);
@@ -4001,22 +4101,42 @@ PyUnicode_FSDecoder(PyObject* arg, void* addr)
static int unicode_fill_utf8(PyObject *unicode);
+
+static int
+unicode_ensure_utf8(PyObject *unicode)
+{
+ int err = 0;
+ if (PyUnicode_UTF8(unicode) == NULL) {
+ Py_BEGIN_CRITICAL_SECTION(unicode);
+ if (PyUnicode_UTF8(unicode) == NULL) {
+ err = unicode_fill_utf8(unicode);
+ }
+ Py_END_CRITICAL_SECTION();
+ }
+ return err;
+}
+
const char *
PyUnicode_AsUTF8AndSize(PyObject *unicode, Py_ssize_t *psize)
{
if (!PyUnicode_Check(unicode)) {
PyErr_BadArgument();
+ if (psize) {
+ *psize = -1;
+ }
return NULL;
}
- if (PyUnicode_UTF8(unicode) == NULL) {
- if (unicode_fill_utf8(unicode) == -1) {
- return NULL;
+ if (unicode_ensure_utf8(unicode) == -1) {
+ if (psize) {
+ *psize = -1;
}
+ return NULL;
}
- if (psize)
+ if (psize) {
*psize = PyUnicode_UTF8_LENGTH(unicode);
+ }
return PyUnicode_UTF8(unicode);
}
@@ -4042,9 +4162,9 @@ _PyUnicode_AsUTF8NoNUL(PyObject *unicode)
PyUnicode_GetSize() has been deprecated since Python 3.3
because it returned length of Py_UNICODE.
-But this function is part of stable abi, because it don't
+But this function is part of stable abi, because it doesn't
include Py_UNICODE in signature and it was not excluded from
-stable abi in PEP 384.
+stable ABI in PEP 384.
*/
PyAPI_FUNC(Py_ssize_t)
PyUnicode_GetSize(PyObject *unicode)
@@ -5343,6 +5463,7 @@ unicode_encode_utf8(PyObject *unicode, _Py_error_handler error_handler,
static int
unicode_fill_utf8(PyObject *unicode)
{
+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(unicode);
/* the string cannot be ASCII, or PyUnicode_UTF8() would be set */
assert(!PyUnicode_IS_ASCII(unicode));
@@ -5378,16 +5499,16 @@ unicode_fill_utf8(PyObject *unicode)
PyBytes_AS_STRING(writer.buffer);
Py_ssize_t len = end - start;
- char *cache = PyObject_Malloc(len + 1);
+ char *cache = PyMem_Malloc(len + 1);
if (cache == NULL) {
_PyBytesWriter_Dealloc(&writer);
PyErr_NoMemory();
return -1;
}
- _PyUnicode_UTF8(unicode) = cache;
- _PyUnicode_UTF8_LENGTH(unicode) = len;
memcpy(cache, start, len);
cache[len] = '\0';
+ PyUnicode_SET_UTF8_LENGTH(unicode, len);
+ PyUnicode_SET_UTF8(unicode, cache);
_PyBytesWriter_Dealloc(&writer);
return 0;
}
@@ -6048,25 +6169,44 @@ PyUnicode_AsUTF16String(PyObject *unicode)
return _PyUnicode_EncodeUTF16(unicode, NULL, 0);
}
+_PyUnicode_Name_CAPI *
+_PyUnicode_GetNameCAPI(void)
+{
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ _PyUnicode_Name_CAPI *ucnhash_capi;
+
+ ucnhash_capi = _Py_atomic_load_ptr(&interp->unicode.ucnhash_capi);
+ if (ucnhash_capi == NULL) {
+ ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import(
+ PyUnicodeData_CAPSULE_NAME, 1);
+
+ // It's fine if we overwite the value here. It's always the same value.
+ _Py_atomic_store_ptr(&interp->unicode.ucnhash_capi, ucnhash_capi);
+ }
+ return ucnhash_capi;
+}
+
/* --- Unicode Escape Codec ----------------------------------------------- */
PyObject *
-_PyUnicode_DecodeUnicodeEscapeInternal(const char *s,
+_PyUnicode_DecodeUnicodeEscapeInternal2(const char *s,
Py_ssize_t size,
const char *errors,
Py_ssize_t *consumed,
- const char **first_invalid_escape)
+ int *first_invalid_escape_char,
+ const char **first_invalid_escape_ptr)
{
const char *starts = s;
+ const char *initial_starts = starts;
_PyUnicodeWriter writer;
const char *end;
PyObject *errorHandler = NULL;
PyObject *exc = NULL;
_PyUnicode_Name_CAPI *ucnhash_capi;
- PyInterpreterState *interp = _PyInterpreterState_Get();
// so we can remember if we've seen an invalid escape char or not
- *first_invalid_escape = NULL;
+ *first_invalid_escape_char = -1;
+ *first_invalid_escape_ptr = NULL;
if (size == 0) {
if (consumed) {
@@ -6154,9 +6294,12 @@ _PyUnicode_DecodeUnicodeEscapeInternal(const char *s,
}
}
if (ch > 0377) {
- if (*first_invalid_escape == NULL) {
- *first_invalid_escape = s-3; /* Back up 3 chars, since we've
- already incremented s. */
+ if (*first_invalid_escape_char == -1) {
+ *first_invalid_escape_char = ch;
+ if (starts == initial_starts) {
+ /* Back up 3 chars, since we've already incremented s. */
+ *first_invalid_escape_ptr = s - 3;
+ }
}
}
WRITE_CHAR(ch);
@@ -6211,19 +6354,13 @@ _PyUnicode_DecodeUnicodeEscapeInternal(const char *s,
/* \N{name} */
case 'N':
- ucnhash_capi = interp->unicode.ucnhash_capi;
+ ucnhash_capi = _PyUnicode_GetNameCAPI();
if (ucnhash_capi == NULL) {
- /* load the unicode data module */
- ucnhash_capi = (_PyUnicode_Name_CAPI *)PyCapsule_Import(
- PyUnicodeData_CAPSULE_NAME, 1);
- if (ucnhash_capi == NULL) {
- PyErr_SetString(
+ PyErr_SetString(
PyExc_UnicodeError,
"\\N escapes not supported (can't load unicodedata module)"
- );
- goto onError;
- }
- interp->unicode.ucnhash_capi = ucnhash_capi;
+ );
+ goto onError;
}
message = "malformed \\N character escape";
@@ -6257,9 +6394,12 @@ _PyUnicode_DecodeUnicodeEscapeInternal(const char *s,
goto error;
default:
- if (*first_invalid_escape == NULL) {
- *first_invalid_escape = s-1; /* Back up one char, since we've
- already incremented s. */
+ if (*first_invalid_escape_char == -1) {
+ *first_invalid_escape_char = c;
+ if (starts == initial_starts) {
+ /* Back up one char, since we've already incremented s. */
+ *first_invalid_escape_ptr = s - 1;
+ }
}
WRITE_ASCII_CHAR('\\');
WRITE_CHAR(c);
@@ -6298,24 +6438,40 @@ _PyUnicode_DecodeUnicodeEscapeInternal(const char *s,
return NULL;
}
+// Export for binary compatibility.
+PyObject *
+_PyUnicode_DecodeUnicodeEscapeInternal(const char *s,
+ Py_ssize_t size,
+ const char *errors,
+ Py_ssize_t *consumed,
+ const char **first_invalid_escape)
+{
+ int first_invalid_escape_char;
+ return _PyUnicode_DecodeUnicodeEscapeInternal2(
+ s, size, errors, consumed,
+ &first_invalid_escape_char,
+ first_invalid_escape);
+}
+
PyObject *
_PyUnicode_DecodeUnicodeEscapeStateful(const char *s,
Py_ssize_t size,
const char *errors,
Py_ssize_t *consumed)
{
- const char *first_invalid_escape;
- PyObject *result = _PyUnicode_DecodeUnicodeEscapeInternal(s, size, errors,
+ int first_invalid_escape_char;
+ const char *first_invalid_escape_ptr;
+ PyObject *result = _PyUnicode_DecodeUnicodeEscapeInternal2(s, size, errors,
consumed,
- &first_invalid_escape);
+ &first_invalid_escape_char,
+ &first_invalid_escape_ptr);
if (result == NULL)
return NULL;
- if (first_invalid_escape != NULL) {
- unsigned char c = *first_invalid_escape;
- if ('4' <= c && c <= '7') {
+ if (first_invalid_escape_char != -1) {
+ if (first_invalid_escape_char > 0xff) {
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
- "invalid octal escape sequence '\\%.3s'",
- first_invalid_escape) < 0)
+ "invalid octal escape sequence '\\%o'",
+ first_invalid_escape_char) < 0)
{
Py_DECREF(result);
return NULL;
@@ -6324,7 +6480,7 @@ _PyUnicode_DecodeUnicodeEscapeStateful(const char *s,
else {
if (PyErr_WarnFormat(PyExc_DeprecationWarning, 1,
"invalid escape sequence '\\%c'",
- c) < 0)
+ first_invalid_escape_char) < 0)
{
Py_DECREF(result);
return NULL;
@@ -7139,10 +7295,6 @@ code_page_name(UINT code_page, PyObject **obj)
*obj = NULL;
if (code_page == CP_ACP)
return "mbcs";
- if (code_page == CP_UTF7)
- return "CP_UTF7";
- if (code_page == CP_UTF8)
- return "CP_UTF8";
*obj = PyBytes_FromFormat("cp%u", code_page);
if (*obj == NULL)
@@ -7980,7 +8132,7 @@ charmap_decode_mapping(const char *s,
goto Undefined;
if (value < 0 || value > MAX_UNICODE) {
PyErr_Format(PyExc_TypeError,
- "character mapping must be in range(0x%x)",
+ "character mapping must be in range(0x%lx)",
(unsigned long)MAX_UNICODE + 1);
goto onError;
}
@@ -8714,8 +8866,8 @@ charmaptranslate_lookup(Py_UCS4 c, PyObject *mapping, PyObject **result)
long value = PyLong_AS_LONG(x);
if (value < 0 || value > MAX_UNICODE) {
PyErr_Format(PyExc_ValueError,
- "character mapping must be in range(0x%x)",
- MAX_UNICODE+1);
+ "character mapping must be in range(0x%lx)",
+ (unsigned long)MAX_UNICODE + 1);
Py_DECREF(x);
return -1;
}
@@ -9301,75 +9453,6 @@ _PyUnicode_InsertThousandsGrouping(
return count;
}
-static Py_ssize_t
-unicode_count_impl(PyObject *str,
- PyObject *substr,
- Py_ssize_t start,
- Py_ssize_t end)
-{
- assert(PyUnicode_Check(str));
- assert(PyUnicode_Check(substr));
-
- Py_ssize_t result;
- int kind1, kind2;
- const void *buf1 = NULL, *buf2 = NULL;
- Py_ssize_t len1, len2;
-
- kind1 = PyUnicode_KIND(str);
- kind2 = PyUnicode_KIND(substr);
- if (kind1 < kind2)
- return 0;
-
- len1 = PyUnicode_GET_LENGTH(str);
- len2 = PyUnicode_GET_LENGTH(substr);
- ADJUST_INDICES(start, end, len1);
- if (end - start < len2)
- return 0;
-
- buf1 = PyUnicode_DATA(str);
- buf2 = PyUnicode_DATA(substr);
- if (kind2 != kind1) {
- buf2 = unicode_askind(kind2, buf2, len2, kind1);
- if (!buf2)
- goto onError;
- }
-
- // We don't reuse `anylib_count` here because of the explicit casts.
- switch (kind1) {
- case PyUnicode_1BYTE_KIND:
- result = ucs1lib_count(
- ((const Py_UCS1*)buf1) + start, end - start,
- buf2, len2, PY_SSIZE_T_MAX
- );
- break;
- case PyUnicode_2BYTE_KIND:
- result = ucs2lib_count(
- ((const Py_UCS2*)buf1) + start, end - start,
- buf2, len2, PY_SSIZE_T_MAX
- );
- break;
- case PyUnicode_4BYTE_KIND:
- result = ucs4lib_count(
- ((const Py_UCS4*)buf1) + start, end - start,
- buf2, len2, PY_SSIZE_T_MAX
- );
- break;
- default:
- Py_UNREACHABLE();
- }
-
- assert((kind2 != kind1) == (buf2 != PyUnicode_DATA(substr)));
- if (kind2 != kind1)
- PyMem_Free((void *)buf2);
-
- return result;
- onError:
- assert((kind2 != kind1) == (buf2 != PyUnicode_DATA(substr)));
- if (kind2 != kind1)
- PyMem_Free((void *)buf2);
- return -1;
-}
-
Py_ssize_t
PyUnicode_Count(PyObject *str,
PyObject *substr,
@@ -9736,13 +9819,14 @@ PyUnicode_Join(PyObject *separator, PyObject *seq)
return NULL;
}
- /* NOTE: the following code can't call back into Python code,
- * so we are sure that fseq won't be mutated.
- */
+ Py_BEGIN_CRITICAL_SECTION_SEQUENCE_FAST(seq);
items = PySequence_Fast_ITEMS(fseq);
seqlen = PySequence_Fast_GET_SIZE(fseq);
res = _PyUnicode_JoinArray(separator, items, seqlen);
+
+ Py_END_CRITICAL_SECTION_SEQUENCE_FAST();
+
Py_DECREF(fseq);
return res;
}
@@ -10447,7 +10531,7 @@ replace(PyObject *self, PyObject *str1,
}
new_size = slen + n * (len2 - len1);
if (new_size == 0) {
- u = unicode_new_empty();
+ u = unicode_get_empty();
goto done;
}
if (new_size > (PY_SSIZE_T_MAX / rkind)) {
@@ -10872,6 +10956,82 @@ PyUnicode_CompareWithASCIIString(PyObject* uni, const char* str)
}
int
+PyUnicode_EqualToUTF8(PyObject *unicode, const char *str)
+{
+ return PyUnicode_EqualToUTF8AndSize(unicode, str, strlen(str));
+}
+
+int
+PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t size)
+{
+ assert(_PyUnicode_CHECK(unicode));
+ assert(str);
+
+ if (PyUnicode_IS_ASCII(unicode)) {
+ Py_ssize_t len = PyUnicode_GET_LENGTH(unicode);
+ return size == len &&
+ memcmp(PyUnicode_1BYTE_DATA(unicode), str, len) == 0;
+ }
+ if (PyUnicode_UTF8(unicode) != NULL) {
+ Py_ssize_t len = PyUnicode_UTF8_LENGTH(unicode);
+ return size == len &&
+ memcmp(PyUnicode_UTF8(unicode), str, len) == 0;
+ }
+
+ Py_ssize_t len = PyUnicode_GET_LENGTH(unicode);
+ if ((size_t)len >= (size_t)size || (size_t)len < (size_t)size / 4) {
+ return 0;
+ }
+ const unsigned char *s = (const unsigned char *)str;
+ const unsigned char *ends = s + (size_t)size;
+ int kind = PyUnicode_KIND(unicode);
+ const void *data = PyUnicode_DATA(unicode);
+ /* Compare Unicode string and UTF-8 string */
+ for (Py_ssize_t i = 0; i < len; i++) {
+ Py_UCS4 ch = PyUnicode_READ(kind, data, i);
+ if (ch < 0x80) {
+ if (ends == s || s[0] != ch) {
+ return 0;
+ }
+ s += 1;
+ }
+ else if (ch < 0x800) {
+ if ((ends - s) < 2 ||
+ s[0] != (0xc0 | (ch >> 6)) ||
+ s[1] != (0x80 | (ch & 0x3f)))
+ {
+ return 0;
+ }
+ s += 2;
+ }
+ else if (ch < 0x10000) {
+ if (Py_UNICODE_IS_SURROGATE(ch) ||
+ (ends - s) < 3 ||
+ s[0] != (0xe0 | (ch >> 12)) ||
+ s[1] != (0x80 | ((ch >> 6) & 0x3f)) ||
+ s[2] != (0x80 | (ch & 0x3f)))
+ {
+ return 0;
+ }
+ s += 3;
+ }
+ else {
+ assert(ch <= MAX_UNICODE);
+ if ((ends - s) < 4 ||
+ s[0] != (0xf0 | (ch >> 18)) ||
+ s[1] != (0x80 | ((ch >> 12) & 0x3f)) ||
+ s[2] != (0x80 | ((ch >> 6) & 0x3f)) ||
+ s[3] != (0x80 | (ch & 0x3f)))
+ {
+ return 0;
+ }
+ s += 4;
+ }
+ }
+ return s == ends;
+}
+
+int
_PyUnicode_EqualToASCIIString(PyObject *unicode, const char *str)
{
size_t len;
@@ -10920,9 +11080,10 @@ _PyUnicode_EqualToASCIIId(PyObject *left, _Py_Identifier *right)
return 0;
}
- assert(_PyUnicode_HASH(right_uni) != -1);
- Py_hash_t hash = _PyUnicode_HASH(left);
- if (hash != -1 && hash != _PyUnicode_HASH(right_uni)) {
+ Py_hash_t right_hash = PyUnicode_HASH(right_uni);
+ assert(right_hash != -1);
+ Py_hash_t hash = PyUnicode_HASH(left);
+ if (hash != -1 && hash != right_hash) {
return 0;
}
@@ -11164,47 +11325,87 @@ PyUnicode_AppendAndDel(PyObject **pleft, PyObject *right)
Py_XDECREF(right);
}
-/*
-Wraps asciilib_parse_args_finds() and additionally ensures that the
-first argument is a unicode object.
-*/
+/*[clinic input]
+@text_signature "($self, sub[, start[, end]], /)"
+str.count as unicode_count -> Py_ssize_t
-static inline int
-parse_args_finds_unicode(const char * function_name, PyObject *args,
- PyObject **substring,
- Py_ssize_t *start, Py_ssize_t *end)
-{
- if (asciilib_parse_args_finds(function_name, args, substring, start, end)) {
- if (ensure_unicode(*substring) < 0)
- return 0;
- return 1;
- }
- return 0;
-}
+ self as str: self
+ sub as substr: unicode
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ /
-PyDoc_STRVAR(count__doc__,
- "S.count(sub[, start[, end]]) -> int\n\
-\n\
-Return the number of non-overlapping occurrences of substring sub in\n\
-string S[start:end]. Optional arguments start and end are\n\
-interpreted as in slice notation.");
+Return the number of non-overlapping occurrences of substring sub in string S[start:end].
-static PyObject *
-unicode_count(PyObject *self, PyObject *args)
+Optional arguments start and end are interpreted as in slice notation.
+[clinic start generated code]*/
+
+static Py_ssize_t
+unicode_count_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=8fcc3aef0b18edbf input=6f168ffd94be8785]*/
{
- PyObject *substring = NULL; /* initialize to fix a compiler warning */
- Py_ssize_t start = 0;
- Py_ssize_t end = PY_SSIZE_T_MAX;
+ assert(PyUnicode_Check(str));
+ assert(PyUnicode_Check(substr));
+
Py_ssize_t result;
+ int kind1, kind2;
+ const void *buf1 = NULL, *buf2 = NULL;
+ Py_ssize_t len1, len2;
- if (!parse_args_finds_unicode("count", args, &substring, &start, &end))
- return NULL;
+ kind1 = PyUnicode_KIND(str);
+ kind2 = PyUnicode_KIND(substr);
+ if (kind1 < kind2)
+ return 0;
- result = unicode_count_impl(self, substring, start, end);
- if (result == -1)
- return NULL;
+ len1 = PyUnicode_GET_LENGTH(str);
+ len2 = PyUnicode_GET_LENGTH(substr);
+ ADJUST_INDICES(start, end, len1);
+ if (end - start < len2)
+ return 0;
+
+ buf1 = PyUnicode_DATA(str);
+ buf2 = PyUnicode_DATA(substr);
+ if (kind2 != kind1) {
+ buf2 = unicode_askind(kind2, buf2, len2, kind1);
+ if (!buf2)
+ goto onError;
+ }
+
+ // We don't reuse `anylib_count` here because of the explicit casts.
+ switch (kind1) {
+ case PyUnicode_1BYTE_KIND:
+ result = ucs1lib_count(
+ ((const Py_UCS1*)buf1) + start, end - start,
+ buf2, len2, PY_SSIZE_T_MAX
+ );
+ break;
+ case PyUnicode_2BYTE_KIND:
+ result = ucs2lib_count(
+ ((const Py_UCS2*)buf1) + start, end - start,
+ buf2, len2, PY_SSIZE_T_MAX
+ );
+ break;
+ case PyUnicode_4BYTE_KIND:
+ result = ucs4lib_count(
+ ((const Py_UCS4*)buf1) + start, end - start,
+ buf2, len2, PY_SSIZE_T_MAX
+ );
+ break;
+ default:
+ Py_UNREACHABLE();
+ }
+
+ assert((kind2 != kind1) == (buf2 != PyUnicode_DATA(substr)));
+ if (kind2 != kind1)
+ PyMem_Free((void *)buf2);
- return PyLong_FromSsize_t(result);
+ return result;
+ onError:
+ assert((kind2 != kind1) == (buf2 != PyUnicode_DATA(substr)));
+ if (kind2 != kind1)
+ PyMem_Free((void *)buf2);
+ return -1;
}
/*[clinic input]
@@ -11315,33 +11516,25 @@ unicode_expandtabs_impl(PyObject *self, int tabsize)
return NULL;
}
-PyDoc_STRVAR(find__doc__,
- "S.find(sub[, start[, end]]) -> int\n\
-\n\
-Return the lowest index in S where substring sub is found,\n\
-such that sub is contained within S[start:end]. Optional\n\
-arguments start and end are interpreted as in slice notation.\n\
-\n\
-Return -1 on failure.");
-
-static PyObject *
-unicode_find(PyObject *self, PyObject *args)
-{
- /* initialize variables to prevent gcc warning */
- PyObject *substring = NULL;
- Py_ssize_t start = 0;
- Py_ssize_t end = 0;
- Py_ssize_t result;
-
- if (!parse_args_finds_unicode("find", args, &substring, &start, &end))
- return NULL;
+/*[clinic input]
+str.find as unicode_find = str.count
- result = any_find_slice(self, substring, start, end, 1);
+Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].
- if (result == -2)
- return NULL;
+Optional arguments start and end are interpreted as in slice notation.
+Return -1 on failure.
+[clinic start generated code]*/
- return PyLong_FromSsize_t(result);
+static Py_ssize_t
+unicode_find_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=51dbe6255712e278 input=4a89d2d68ef57256]*/
+{
+ Py_ssize_t result = any_find_slice(str, substr, start, end, 1);
+ if (result < 0) {
+ return -1;
+ }
+ return result;
}
static PyObject *
@@ -11375,47 +11568,39 @@ unicode_hash(PyObject *self)
#ifdef Py_DEBUG
assert(_Py_HashSecret_Initialized);
#endif
- if (_PyUnicode_HASH(self) != -1)
- return _PyUnicode_HASH(self);
-
+ Py_hash_t hash = PyUnicode_HASH(self);
+ if (hash != -1) {
+ return hash;
+ }
x = _Py_HashBytes(PyUnicode_DATA(self),
PyUnicode_GET_LENGTH(self) * PyUnicode_KIND(self));
- _PyUnicode_HASH(self) = x;
+
+ PyUnicode_SET_HASH(self, x);
return x;
}
-PyDoc_STRVAR(index__doc__,
- "S.index(sub[, start[, end]]) -> int\n\
-\n\
-Return the lowest index in S where substring sub is found,\n\
-such that sub is contained within S[start:end]. Optional\n\
-arguments start and end are interpreted as in slice notation.\n\
-\n\
-Raises ValueError when the substring is not found.");
-
-static PyObject *
-unicode_index(PyObject *self, PyObject *args)
-{
- /* initialize variables to prevent gcc warning */
- Py_ssize_t result;
- PyObject *substring = NULL;
- Py_ssize_t start = 0;
- Py_ssize_t end = 0;
-
- if (!parse_args_finds_unicode("index", args, &substring, &start, &end))
- return NULL;
+/*[clinic input]
+str.index as unicode_index = str.count
- result = any_find_slice(self, substring, start, end, 1);
+Return the lowest index in S where substring sub is found, such that sub is contained within S[start:end].
- if (result == -2)
- return NULL;
+Optional arguments start and end are interpreted as in slice notation.
+Raises ValueError when the substring is not found.
+[clinic start generated code]*/
- if (result < 0) {
+static Py_ssize_t
+unicode_index_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=77558288837cdf40 input=d986aeac0be14a1c]*/
+{
+ Py_ssize_t result = any_find_slice(str, substr, start, end, 1);
+ if (result == -1) {
PyErr_SetString(PyExc_ValueError, "substring not found");
- return NULL;
}
-
- return PyLong_FromSsize_t(result);
+ else if (result < 0) {
+ return -1;
+ }
+ return result;
}
/*[clinic input]
@@ -11864,15 +12049,14 @@ unicode_isidentifier_impl(PyObject *self)
/*[clinic input]
str.isprintable as unicode_isprintable
-Return True if the string is printable, False otherwise.
+Return True if all characters in the string are printable, False otherwise.
-A string is printable if all of its characters are considered printable in
-repr() or if it is empty.
+A character is printable if repr() may use it in its output.
[clinic start generated code]*/
static PyObject *
unicode_isprintable_impl(PyObject *self)
-/*[clinic end generated code: output=3ab9626cd32dd1a0 input=98a0e1c2c1813209]*/
+/*[clinic end generated code: output=3ab9626cd32dd1a0 input=4e56bcc6b06ca18c]*/
{
Py_ssize_t i, length;
int kind;
@@ -12259,10 +12443,10 @@ str.replace as unicode_replace
old: unicode
new: unicode
+ /
count: Py_ssize_t = -1
Maximum number of occurrences to replace.
-1 (the default value) means replace all occurrences.
- /
Return a copy with all occurrences of substring old replaced by new.
@@ -12273,7 +12457,7 @@ replaced.
static PyObject *
unicode_replace_impl(PyObject *self, PyObject *old, PyObject *new,
Py_ssize_t count)
-/*[clinic end generated code: output=b63f1a8b5eebf448 input=147d12206276ebeb]*/
+/*[clinic end generated code: output=b63f1a8b5eebf448 input=3345c455d60a5499]*/
{
return replace(self, old, new, count);
}
@@ -12495,67 +12679,49 @@ unicode_repr(PyObject *unicode)
return repr;
}
-PyDoc_STRVAR(rfind__doc__,
- "S.rfind(sub[, start[, end]]) -> int\n\
-\n\
-Return the highest index in S where substring sub is found,\n\
-such that sub is contained within S[start:end]. Optional\n\
-arguments start and end are interpreted as in slice notation.\n\
-\n\
-Return -1 on failure.");
-
-static PyObject *
-unicode_rfind(PyObject *self, PyObject *args)
-{
- /* initialize variables to prevent gcc warning */
- PyObject *substring = NULL;
- Py_ssize_t start = 0;
- Py_ssize_t end = 0;
- Py_ssize_t result;
-
- if (!parse_args_finds_unicode("rfind", args, &substring, &start, &end))
- return NULL;
-
- result = any_find_slice(self, substring, start, end, -1);
-
- if (result == -2)
- return NULL;
+/*[clinic input]
+str.rfind as unicode_rfind = str.count
- return PyLong_FromSsize_t(result);
-}
+Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].
-PyDoc_STRVAR(rindex__doc__,
- "S.rindex(sub[, start[, end]]) -> int\n\
-\n\
-Return the highest index in S where substring sub is found,\n\
-such that sub is contained within S[start:end]. Optional\n\
-arguments start and end are interpreted as in slice notation.\n\
-\n\
-Raises ValueError when the substring is not found.");
+Optional arguments start and end are interpreted as in slice notation.
+Return -1 on failure.
+[clinic start generated code]*/
-static PyObject *
-unicode_rindex(PyObject *self, PyObject *args)
+static Py_ssize_t
+unicode_rfind_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=880b29f01dd014c8 input=898361fb71f59294]*/
{
- /* initialize variables to prevent gcc warning */
- PyObject *substring = NULL;
- Py_ssize_t start = 0;
- Py_ssize_t end = 0;
- Py_ssize_t result;
+ Py_ssize_t result = any_find_slice(str, substr, start, end, -1);
+ if (result < 0) {
+ return -1;
+ }
+ return result;
+}
- if (!parse_args_finds_unicode("rindex", args, &substring, &start, &end))
- return NULL;
+/*[clinic input]
+str.rindex as unicode_rindex = str.count
- result = any_find_slice(self, substring, start, end, -1);
+Return the highest index in S where substring sub is found, such that sub is contained within S[start:end].
- if (result == -2)
- return NULL;
+Optional arguments start and end are interpreted as in slice notation.
+Raises ValueError when the substring is not found.
+[clinic start generated code]*/
- if (result < 0) {
+static Py_ssize_t
+unicode_rindex_impl(PyObject *str, PyObject *substr, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=5f3aef124c867fe1 input=35943dead6c1ea9d]*/
+{
+ Py_ssize_t result = any_find_slice(str, substr, start, end, -1);
+ if (result == -1) {
PyErr_SetString(PyExc_ValueError, "substring not found");
- return NULL;
}
-
- return PyLong_FromSsize_t(result);
+ else if (result < 0) {
+ return -1;
+ }
+ return result;
}
/*[clinic input]
@@ -13054,30 +13220,30 @@ unicode_zfill_impl(PyObject *self, Py_ssize_t width)
return u;
}
-PyDoc_STRVAR(startswith__doc__,
- "S.startswith(prefix[, start[, end]]) -> bool\n\
-\n\
-Return True if S starts with the specified prefix, False otherwise.\n\
-With optional start, test S beginning at that position.\n\
-With optional end, stop comparing S at that position.\n\
-prefix can also be a tuple of strings to try.");
+/*[clinic input]
+@text_signature "($self, prefix[, start[, end]], /)"
+str.startswith as unicode_startswith
+
+ prefix as subobj: object
+ A string or a tuple of strings to try.
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ Optional start position. Default: start of the string.
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ Optional stop position. Default: end of the string.
+ /
+
+Return True if the string starts with the specified prefix, False otherwise.
+[clinic start generated code]*/
static PyObject *
-unicode_startswith(PyObject *self,
- PyObject *args)
+unicode_startswith_impl(PyObject *self, PyObject *subobj, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=4bd7cfd0803051d4 input=5f918b5f5f89d856]*/
{
- PyObject *subobj;
- PyObject *substring;
- Py_ssize_t start = 0;
- Py_ssize_t end = PY_SSIZE_T_MAX;
- int result;
-
- if (!asciilib_parse_args_finds("startswith", args, &subobj, &start, &end))
- return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
- substring = PyTuple_GET_ITEM(subobj, i);
+ PyObject *substring = PyTuple_GET_ITEM(subobj, i);
if (!PyUnicode_Check(substring)) {
PyErr_Format(PyExc_TypeError,
"tuple for startswith must only contain str, "
@@ -13085,9 +13251,10 @@ unicode_startswith(PyObject *self,
Py_TYPE(substring)->tp_name);
return NULL;
}
- result = tailmatch(self, substring, start, end, -1);
- if (result == -1)
+ int result = tailmatch(self, substring, start, end, -1);
+ if (result < 0) {
return NULL;
+ }
if (result) {
Py_RETURN_TRUE;
}
@@ -13101,37 +13268,38 @@ unicode_startswith(PyObject *self,
"a tuple of str, not %.100s", Py_TYPE(subobj)->tp_name);
return NULL;
}
- result = tailmatch(self, subobj, start, end, -1);
- if (result == -1)
+ int result = tailmatch(self, subobj, start, end, -1);
+ if (result < 0) {
return NULL;
+ }
return PyBool_FromLong(result);
}
-PyDoc_STRVAR(endswith__doc__,
- "S.endswith(suffix[, start[, end]]) -> bool\n\
-\n\
-Return True if S ends with the specified suffix, False otherwise.\n\
-With optional start, test S beginning at that position.\n\
-With optional end, stop comparing S at that position.\n\
-suffix can also be a tuple of strings to try.");
+/*[clinic input]
+@text_signature "($self, suffix[, start[, end]], /)"
+str.endswith as unicode_endswith
+
+ suffix as subobj: object
+ A string or a tuple of strings to try.
+ start: slice_index(accept={int, NoneType}, c_default='0') = None
+ Optional start position. Default: start of the string.
+ end: slice_index(accept={int, NoneType}, c_default='PY_SSIZE_T_MAX') = None
+ Optional stop position. Default: end of the string.
+ /
+
+Return True if the string ends with the specified suffix, False otherwise.
+[clinic start generated code]*/
static PyObject *
-unicode_endswith(PyObject *self,
- PyObject *args)
+unicode_endswith_impl(PyObject *self, PyObject *subobj, Py_ssize_t start,
+ Py_ssize_t end)
+/*[clinic end generated code: output=cce6f8ceb0102ca9 input=00fbdc774a7d4d71]*/
{
- PyObject *subobj;
- PyObject *substring;
- Py_ssize_t start = 0;
- Py_ssize_t end = PY_SSIZE_T_MAX;
- int result;
-
- if (!asciilib_parse_args_finds("endswith", args, &subobj, &start, &end))
- return NULL;
if (PyTuple_Check(subobj)) {
Py_ssize_t i;
for (i = 0; i < PyTuple_GET_SIZE(subobj); i++) {
- substring = PyTuple_GET_ITEM(subobj, i);
+ PyObject *substring = PyTuple_GET_ITEM(subobj, i);
if (!PyUnicode_Check(substring)) {
PyErr_Format(PyExc_TypeError,
"tuple for endswith must only contain str, "
@@ -13139,9 +13307,10 @@ unicode_endswith(PyObject *self,
Py_TYPE(substring)->tp_name);
return NULL;
}
- result = tailmatch(self, substring, start, end, +1);
- if (result == -1)
+ int result = tailmatch(self, substring, start, end, +1);
+ if (result < 0) {
return NULL;
+ }
if (result) {
Py_RETURN_TRUE;
}
@@ -13154,9 +13323,10 @@ unicode_endswith(PyObject *self,
"a tuple of str, not %.100s", Py_TYPE(subobj)->tp_name);
return NULL;
}
- result = tailmatch(self, subobj, start, end, +1);
- if (result == -1)
+ int result = tailmatch(self, subobj, start, end, +1);
+ if (result < 0) {
return NULL;
+ }
return PyBool_FromLong(result);
}
@@ -13497,15 +13667,17 @@ _PyUnicodeWriter_Dealloc(_PyUnicodeWriter *writer)
#include "stringlib/unicode_format.h"
PyDoc_STRVAR(format__doc__,
- "S.format(*args, **kwargs) -> str\n\
+ "format($self, /, *args, **kwargs)\n\
+--\n\
\n\
-Return a formatted version of S, using substitutions from args and kwargs.\n\
+Return a formatted version of the string, using substitutions from args and kwargs.\n\
The substitutions are identified by braces ('{' and '}').");
PyDoc_STRVAR(format_map__doc__,
- "S.format_map(mapping) -> str\n\
+ "format_map($self, mapping, /)\n\
+--\n\
\n\
-Return a formatted version of S, using substitutions from mapping.\n\
+Return a formatted version of the string, using substitutions from mapping.\n\
The substitutions are identified by braces ('{' and '}').");
/*[clinic input]
@@ -13589,16 +13761,16 @@ static PyMethodDef unicode_methods[] = {
UNICODE_CASEFOLD_METHODDEF
UNICODE_TITLE_METHODDEF
UNICODE_CENTER_METHODDEF
- {"count", (PyCFunction) unicode_count, METH_VARARGS, count__doc__},
+ UNICODE_COUNT_METHODDEF
UNICODE_EXPANDTABS_METHODDEF
- {"find", (PyCFunction) unicode_find, METH_VARARGS, find__doc__},
+ UNICODE_FIND_METHODDEF
UNICODE_PARTITION_METHODDEF
- {"index", (PyCFunction) unicode_index, METH_VARARGS, index__doc__},
+ UNICODE_INDEX_METHODDEF
UNICODE_LJUST_METHODDEF
UNICODE_LOWER_METHODDEF
UNICODE_LSTRIP_METHODDEF
- {"rfind", (PyCFunction) unicode_rfind, METH_VARARGS, rfind__doc__},
- {"rindex", (PyCFunction) unicode_rindex, METH_VARARGS, rindex__doc__},
+ UNICODE_RFIND_METHODDEF
+ UNICODE_RINDEX_METHODDEF
UNICODE_RJUST_METHODDEF
UNICODE_RSTRIP_METHODDEF
UNICODE_RPARTITION_METHODDEF
@@ -13607,8 +13779,8 @@ static PyMethodDef unicode_methods[] = {
UNICODE_SWAPCASE_METHODDEF
UNICODE_TRANSLATE_METHODDEF
UNICODE_UPPER_METHODDEF
- {"startswith", (PyCFunction) unicode_startswith, METH_VARARGS, startswith__doc__},
- {"endswith", (PyCFunction) unicode_endswith, METH_VARARGS, endswith__doc__},
+ UNICODE_STARTSWITH_METHODDEF
+ UNICODE_ENDSWITH_METHODDEF
UNICODE_REMOVEPREFIX_METHODDEF
UNICODE_REMOVESUFFIX_METHODDEF
UNICODE_ISASCII_METHODDEF
@@ -13886,7 +14058,7 @@ _PyUnicode_FormatLong(PyObject *val, int alt, int prec, int type)
assert(PyUnicode_IS_ASCII(result));
/* To modify the string in-place, there can only be one reference. */
- if (Py_REFCNT(result) != 1) {
+ if (!_PyObject_IsUniquelyReferenced(result)) {
Py_DECREF(result);
PyErr_BadInternalCall();
return NULL;
@@ -14230,7 +14402,7 @@ unicode_format_arg_parse(struct unicode_formatter_t *ctx,
"* wants int");
return -1;
}
- arg->prec = _PyLong_AsInt(v);
+ arg->prec = PyLong_AsInt(v);
if (arg->prec == -1 && PyErr_Occurred())
return -1;
if (arg->prec < 0)
@@ -14695,7 +14867,7 @@ unicode_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
{
PyObject *unicode;
if (x == NULL) {
- unicode = unicode_new_empty();
+ unicode = unicode_get_empty();
}
else if (encoding == NULL && errors == NULL) {
unicode = PyObject_Str(x);
@@ -14710,6 +14882,65 @@ unicode_new_impl(PyTypeObject *type, PyObject *x, const char *encoding,
return unicode;
}
+static const char *
+arg_as_utf8(PyObject *obj, const char *name)
+{
+ if (!PyUnicode_Check(obj)) {
+ PyErr_Format(PyExc_TypeError,
+ "str() argument '%s' must be str, not %T",
+ name, obj);
+ return NULL;
+ }
+ return _PyUnicode_AsUTF8NoNUL(obj);
+}
+
+static PyObject *
+unicode_vectorcall(PyObject *type, PyObject *const *args,
+ size_t nargsf, PyObject *kwnames)
+{
+ assert(Py_Is(_PyType_CAST(type), &PyUnicode_Type));
+
+ Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
+ if (kwnames != NULL && PyTuple_GET_SIZE(kwnames) != 0) {
+ // Fallback to unicode_new()
+ PyObject *tuple = _PyTuple_FromArray(args, nargs);
+ if (tuple == NULL) {
+ return NULL;
+ }
+ PyObject *dict = _PyStack_AsDict(args + nargs, kwnames);
+ if (dict == NULL) {
+ Py_DECREF(tuple);
+ return NULL;
+ }
+ PyObject *ret = unicode_new(_PyType_CAST(type), tuple, dict);
+ Py_DECREF(tuple);
+ Py_DECREF(dict);
+ return ret;
+ }
+ if (!_PyArg_CheckPositional("str", nargs, 0, 3)) {
+ return NULL;
+ }
+ if (nargs == 0) {
+ return unicode_get_empty();
+ }
+ PyObject *object = args[0];
+ if (nargs == 1) {
+ return PyObject_Str(object);
+ }
+ const char *encoding = arg_as_utf8(args[1], "encoding");
+ if (encoding == NULL) {
+ return NULL;
+ }
+ const char *errors = NULL;
+ if (nargs == 3) {
+ errors = arg_as_utf8(args[2], "errors");
+ if (errors == NULL) {
+ return NULL;
+ }
+ }
+ return PyUnicode_FromEncodedObject(object, encoding, errors);
+}
+
static PyObject *
unicode_subtype_new(PyTypeObject *type, PyObject *unicode)
{
@@ -14740,8 +14971,8 @@ unicode_subtype_new(PyTypeObject *type, PyObject *unicode)
_PyUnicode_STATE(self).compact = 0;
_PyUnicode_STATE(self).ascii = _PyUnicode_STATE(unicode).ascii;
_PyUnicode_STATE(self).statically_allocated = 0;
- _PyUnicode_UTF8_LENGTH(self) = 0;
- _PyUnicode_UTF8(self) = NULL;
+ PyUnicode_SET_UTF8_LENGTH(self, 0);
+ PyUnicode_SET_UTF8(self, NULL);
_PyUnicode_DATA_ANY(self) = NULL;
share_utf8 = 0;
@@ -14763,7 +14994,7 @@ unicode_subtype_new(PyTypeObject *type, PyObject *unicode)
PyErr_NoMemory();
goto onError;
}
- data = PyObject_Malloc((length + 1) * char_size);
+ data = PyMem_Malloc((length + 1) * char_size);
if (data == NULL) {
PyErr_NoMemory();
goto onError;
@@ -14771,8 +15002,8 @@ unicode_subtype_new(PyTypeObject *type, PyObject *unicode)
_PyUnicode_DATA_ANY(self) = data;
if (share_utf8) {
- _PyUnicode_UTF8_LENGTH(self) = length;
- _PyUnicode_UTF8(self) = data;
+ PyUnicode_SET_UTF8_LENGTH(self, length);
+ PyUnicode_SET_UTF8(self, data);
}
memcpy(data, PyUnicode_DATA(unicode), kind * (length + 1));
@@ -14803,7 +15034,7 @@ errors is specified, then the object must expose a data buffer\n\
that will be decoded using the given encoding and error handler.\n\
Otherwise, returns the result of object.__str__() (if defined)\n\
or repr(object).\n\
-encoding defaults to sys.getdefaultencoding().\n\
+encoding defaults to 'utf-8'.\n\
errors defaults to 'strict'.");
static PyObject *unicode_iter(PyObject *seq);
@@ -14851,6 +15082,7 @@ PyTypeObject PyUnicode_Type = {
0, /* tp_alloc */
unicode_new, /* tp_new */
PyObject_Del, /* tp_free */
+ .tp_vectorcall = unicode_vectorcall,
};
/* Initialize the Unicode implementation */
@@ -14901,15 +15133,6 @@ _PyUnicode_InitGlobalObjects(PyInterpreterState *interp)
}
assert(INTERNED_STRINGS);
- return _PyStatus_OK();
-}
-
-
-PyStatus
-_PyUnicode_InitInternDict(PyInterpreterState *interp)
-{
- assert(INTERNED_STRINGS);
-
if (init_interned_dict(interp)) {
PyErr_Clear();
return _PyStatus_ERR("failed to create interned dict");
@@ -14953,7 +15176,7 @@ intern_static(PyInterpreterState *interp, PyObject *s /* stolen */)
* per-interpreter interned_dict, which might contain duplicates.
*/
PyObject *interned = get_interned_dict(interp);
- // assert(interned == NULL);
+ //assert(interned == NULL);
#endif
/* Look in the global cache first. */
@@ -14996,7 +15219,7 @@ immortalize_interned(PyObject *s)
The decrements to these objects will not be registered so they
need to be accounted for in here. */
for (Py_ssize_t i = 0; i < Py_REFCNT(s); i++) {
- _Py_DecRefTotal(_PyInterpreterState_GET());
+ _Py_DecRefTotal(_PyThreadState_GET());
}
#endif
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_IMMORTAL;
@@ -15041,9 +15264,13 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
return s;
}
- if (_PyUnicode_STATE(s).statically_allocated) {
- return intern_static(interp, s);
- }
+ /* Statically allocated strings must be already interned. */
+ assert(!_PyUnicode_STATE(s).statically_allocated);
+
+#if Py_GIL_DISABLED
+ /* In the free-threaded build, all interned strings are immortal */
+ immortalize = 1;
+#endif
/* If it's already immortal, intern it as such */
if (_Py_IsImmortal(s)) {
@@ -15077,24 +15304,27 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
PyObject *interned = get_interned_dict(interp);
assert(interned != NULL);
- PyObject *t = PyDict_SetDefault(interned, s, s); // t is borrowed
- if (t == NULL) {
- PyErr_Clear();
- return s;
- }
-
- if (t != s) {
- // value was already present (not inserted)
- Py_INCREF(t);
- Py_DECREF(s);
- if (immortalize &&
- PyUnicode_CHECK_INTERNED(t) == SSTATE_INTERNED_MORTAL) {
- immortalize_interned(t);
+ PyObject *t;
+ {
+ int res = PyDict_SetDefaultRef(interned, s, s, &t);
+ if (res < 0) {
+ PyErr_Clear();
+ return s;
+ }
+ else if (res == 1) {
+ // value was already present (not inserted)
+ Py_DECREF(s);
+ if (immortalize &&
+ PyUnicode_CHECK_INTERNED(t) == SSTATE_INTERNED_MORTAL) {
+ immortalize_interned(t);
+ }
+ return t;
+ }
+ else {
+ // value was newly inserted
+ assert (s == t);
+ Py_DECREF(t);
}
- return t;
- }
- else {
- // value was newly inserted
}
/* NOT_INTERNED -> INTERNED_MORTAL */
@@ -15107,8 +15337,8 @@ intern_common(PyInterpreterState *interp, PyObject *s /* stolen */,
Py_SET_REFCNT(s, Py_REFCNT(s) - 2);
#ifdef Py_REF_DEBUG
/* let's be pedantic with the ref total */
- _Py_DecRefTotal(_PyInterpreterState_GET());
- _Py_DecRefTotal(_PyInterpreterState_GET());
+ _Py_DecRefTotal(_PyThreadState_GET());
+ _Py_DecRefTotal(_PyThreadState_GET());
#endif
}
_PyUnicode_STATE(s).interned = SSTATE_INTERNED_MORTAL;
@@ -15223,11 +15453,11 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp)
// Skip the Immortal Instance check and restore
// the two references (key and value) ignored
// by PyUnicode_InternInPlace().
- s->ob_refcnt = 2;
+ _Py_SetMortal(s, 2);
#ifdef Py_REF_DEBUG
/* let's be pedantic with the ref total */
- _Py_IncRefTotal(_PyInterpreterState_GET());
- _Py_IncRefTotal(_PyInterpreterState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
#endif
#ifdef INTERNED_STATS
total_length += PyUnicode_GET_LENGTH(s);
@@ -15250,8 +15480,8 @@ _PyUnicode_ClearInterned(PyInterpreterState *interp)
Py_SET_REFCNT(s, Py_REFCNT(s) + 2);
#ifdef Py_REF_DEBUG
/* let's be pedantic with the ref total */
- _Py_IncRefTotal(_PyInterpreterState_GET());
- _Py_IncRefTotal(_PyInterpreterState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
+ _Py_IncRefTotal(_PyThreadState_GET());
#endif
break;
case SSTATE_NOT_INTERNED:
@@ -15343,8 +15573,7 @@ unicode_ascii_iter_next(unicodeiterobject *it)
Py_UCS1 chr = (Py_UCS1)PyUnicode_READ(PyUnicode_1BYTE_KIND,
data, it->it_index);
it->it_index++;
- PyObject *item = (PyObject*)&_Py_SINGLETON(strings).ascii[chr];
- return Py_NewRef(item);
+ return (PyObject*)&_Py_SINGLETON(strings).ascii[chr];
}
it->it_seq = NULL;
Py_DECREF(seq);
@@ -15374,7 +15603,7 @@ unicodeiter_reduce(unicodeiterobject *it, PyObject *Py_UNUSED(ignored))
if (it->it_seq != NULL) {
return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index);
} else {
- PyObject *u = unicode_new_empty();
+ PyObject *u = unicode_get_empty();
if (u == NULL) {
Py_XDECREF(iter);
return NULL;
@@ -15641,7 +15870,11 @@ init_fs_encoding(PyThreadState *tstate)
PyStatus
_PyUnicode_InitEncodings(PyThreadState *tstate)
{
- PyStatus status = init_fs_encoding(tstate);
+ PyStatus status = _PyCodec_InitRegistry(tstate->interp);
+ if (_PyStatus_EXCEPTION(status)) {
+ return status;
+ }
+ status = init_fs_encoding(tstate);
if (_PyStatus_EXCEPTION(status)) {
return status;
}
@@ -15701,9 +15934,9 @@ unicode_is_finalizing(void)
void
_PyUnicode_FiniTypes(PyInterpreterState *interp)
{
- _PyStaticType_Dealloc(interp, &EncodingMapType);
- _PyStaticType_Dealloc(interp, &PyFieldNameIter_Type);
- _PyStaticType_Dealloc(interp, &PyFormatterIter_Type);
+ _PyStaticType_FiniBuiltin(interp, &EncodingMapType);
+ _PyStaticType_FiniBuiltin(interp, &PyFieldNameIter_Type);
+ _PyStaticType_FiniBuiltin(interp, &PyFormatterIter_Type);
}
@@ -15739,6 +15972,7 @@ static PyMethodDef _string_methods[] = {
static PyModuleDef_Slot module_slots[] = {
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
{0, NULL}
};
@@ -15756,8 +15990,3 @@ PyInit__string(void)
{
return PyModuleDef_Init(&_string_module);
}
-
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/contrib/tools/python3/Objects/unicodetype_db.h b/contrib/tools/python3/Objects/unicodetype_db.h
index 22f8243eaec..e6dbeffbe2a 100644
--- a/contrib/tools/python3/Objects/unicodetype_db.h
+++ b/contrib/tools/python3/Objects/unicodetype_db.h
@@ -1,4 +1,4 @@
-/* this file was generated by Tools/unicode/makeunicodedata.py 3.3 */
+/* this file was generated by ./Tools/unicode/makeunicodedata.py 3.3 */
/* a list of unique character type descriptors */
const _PyUnicode_TypeRecord _PyUnicode_TypeRecords[] = {
@@ -360,6 +360,7 @@ const _PyUnicode_TypeRecord _PyUnicode_TypeRecords[] = {
{0, -128, 0, 0, 0, 10113},
{0, -126, 0, 0, 0, 10113},
{33555335, 18875268, 16778121, 0, 0, 26433},
+ {0, 0, 0, 0, 0, 4608},
{0, 0, 0, 0, 0, 3076},
{0, 0, 0, 0, 4, 3076},
{0, 0, 0, 0, 5, 3076},
@@ -1760,603 +1761,603 @@ static const unsigned short index1[] = {
76, 64, 77, 78, 79, 80, 81, 82, 83, 64, 64, 84, 85, 34, 34, 34, 34, 34,
34, 86, 34, 34, 34, 34, 34, 87, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 88, 89, 90, 91, 34, 34, 34, 92, 34, 34,
- 34, 93, 94, 34, 34, 34, 34, 34, 95, 34, 34, 34, 96, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 97, 98, 99, 34, 34, 34, 34, 34, 34, 100, 101, 34, 34,
- 34, 34, 34, 34, 34, 34, 102, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 103, 34, 34, 34, 34, 34, 34, 34, 34, 104, 34, 34, 34, 34,
- 100, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 103, 34, 34, 34, 34, 34, 34, 105, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 106, 107, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 108, 109, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 110, 34, 34, 34, 34, 34,
- 34, 34, 34, 111, 34, 34, 112, 113, 114, 115, 116, 117, 118, 119, 120,
- 121, 122, 123, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 88, 89, 90, 91, 92, 93, 34, 94, 34, 34,
+ 34, 95, 96, 34, 34, 34, 34, 34, 97, 34, 34, 34, 98, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 99, 100, 101, 34, 34, 34, 34, 34, 34, 102, 103, 34,
+ 34, 34, 34, 34, 34, 34, 34, 104, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 105, 34, 34, 34, 93, 34, 34, 34, 34, 34, 34, 34, 34, 106, 34, 34, 34, 34,
+ 107, 108, 34, 34, 34, 34, 34, 109, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 93, 34, 34, 34, 34, 34, 34, 110, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 111, 112, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 113, 34, 34, 34, 34, 114, 34, 34, 115, 116, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 117, 34, 34, 34,
+ 34, 34, 34, 34, 34, 118, 34, 34, 119, 120, 121, 122, 123, 124, 125, 126,
+ 127, 128, 129, 130, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 124, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 126, 127, 128,
- 129, 130, 131, 132, 34, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142,
- 125, 143, 144, 145, 146, 147, 148, 149, 34, 34, 150, 151, 152, 153, 154,
- 155, 156, 157, 158, 159, 160, 161, 162, 125, 163, 164, 165, 166, 167,
- 168, 169, 170, 171, 172, 173, 125, 174, 175, 125, 176, 177, 178, 179,
- 125, 180, 181, 182, 183, 184, 185, 186, 125, 187, 188, 189, 190, 125,
- 191, 192, 193, 34, 34, 34, 34, 34, 34, 34, 194, 195, 34, 196, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 197, 34, 34, 34, 34, 34, 34, 34, 34, 198, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 34, 34, 34, 34, 199, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 34, 34, 34, 34, 200, 201, 202, 203, 125, 125, 125, 125, 204,
- 205, 206, 207, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 131, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 133, 134,
+ 135, 136, 137, 138, 139, 34, 140, 141, 142, 143, 144, 145, 146, 147, 148,
+ 149, 132, 150, 151, 152, 153, 154, 155, 156, 34, 34, 157, 158, 159, 160,
+ 161, 162, 163, 164, 165, 166, 167, 168, 169, 132, 170, 171, 172, 173,
+ 174, 175, 176, 177, 178, 179, 180, 132, 181, 182, 132, 183, 184, 185,
+ 186, 132, 187, 188, 189, 190, 191, 192, 193, 132, 194, 195, 196, 197,
+ 132, 198, 199, 200, 34, 34, 34, 34, 34, 34, 34, 201, 202, 34, 203, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 204, 34, 34, 34, 34, 34, 34, 34, 34, 205, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 34, 34, 34, 34, 206, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 34, 34, 34, 34, 207, 208, 209, 210, 132, 132, 132, 132,
+ 211, 212, 213, 214, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 208, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 209, 210, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 211, 34, 34, 212, 34, 34, 213, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 214, 215, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 216, 217, 64, 218,
- 219, 220, 221, 222, 223, 125, 224, 225, 226, 227, 228, 229, 230, 231, 64,
- 64, 64, 64, 232, 233, 125, 125, 125, 125, 125, 125, 125, 125, 234, 125,
- 235, 236, 237, 125, 125, 238, 125, 125, 125, 239, 125, 125, 125, 125,
- 125, 240, 34, 241, 242, 125, 125, 125, 125, 125, 243, 244, 245, 125, 246,
- 247, 125, 125, 248, 249, 250, 251, 252, 125, 64, 253, 64, 64, 64, 64, 64,
- 254, 255, 256, 257, 258, 64, 64, 259, 260, 64, 261, 125, 125, 125, 125,
- 125, 125, 125, 125, 262, 263, 264, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 86, 265, 34, 266, 267, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 215, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 216, 217, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 218, 34, 34, 219, 34, 34, 220, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 221, 222, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 223, 224, 64,
+ 225, 226, 227, 228, 229, 230, 132, 231, 232, 233, 234, 235, 236, 237,
+ 238, 64, 64, 64, 64, 239, 240, 132, 132, 132, 132, 132, 132, 132, 132,
+ 241, 132, 242, 243, 244, 132, 132, 245, 132, 132, 132, 246, 132, 132,
+ 132, 132, 132, 247, 34, 248, 249, 132, 132, 132, 132, 132, 250, 251, 252,
+ 132, 253, 254, 132, 132, 255, 256, 257, 258, 259, 132, 64, 260, 64, 64,
+ 64, 64, 64, 261, 262, 263, 264, 265, 64, 64, 266, 267, 64, 268, 132, 132,
+ 132, 132, 132, 132, 132, 132, 269, 270, 271, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 86, 272, 34, 273, 274, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 268, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 269, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 270,
+ 34, 34, 34, 34, 34, 34, 34, 34, 275, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 276, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 277, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 271, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 109, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 272, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 273, 34, 274, 34, 34,
+ 278, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 279, 34, 280,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 275, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 281, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 276, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 34, 268, 34, 34, 277, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 282, 34, 34, 34, 34, 283,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 34, 275, 34, 34, 284, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 34, 34, 34, 278, 34, 34, 34, 34, 34, 34, 34, 34,
+ 34, 34, 34, 34, 34, 34, 34, 34, 34, 285, 34, 34, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
- 34, 34, 34, 34, 34, 34, 279, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 280, 125, 281, 282, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125,
- 125, 125, 125,
+ 34, 34, 34, 34, 34, 34, 286, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 287, 132, 288, 289, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132, 132,
+ 132, 132, 132,
};
static const unsigned short index2[] = {
@@ -2777,27 +2778,27 @@ static const unsigned short index2[] = {
342, 265, 265, 343, 343, 0, 5, 5, 5, 264, 264, 344, 345, 346, 126, 347,
348, 265, 265, 349, 349, 130, 5, 5, 5, 0, 0, 350, 351, 352, 0, 353, 354,
355, 355, 356, 356, 357, 5, 5, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 20,
- 20, 20, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 5, 4, 4, 5, 2, 2, 20, 20, 20, 20, 20, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 17, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 20, 20, 20, 20, 20, 0, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 20, 358, 101, 0, 0, 359, 360, 361, 362, 363,
- 364, 4, 4, 4, 4, 4, 101, 358, 25, 21, 22, 359, 360, 361, 362, 363, 364,
- 4, 4, 4, 4, 4, 0, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
- 101, 101, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 5,
- 5, 5, 5, 24, 5, 5, 5, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 120, 4, 4, 4, 4, 120, 4,
- 4, 19, 120, 120, 120, 19, 19, 120, 120, 120, 19, 4, 120, 4, 4, 365, 120,
- 120, 120, 120, 120, 4, 4, 4, 4, 4, 4, 120, 4, 366, 4, 120, 4, 367, 368,
- 120, 120, 365, 19, 120, 120, 369, 120, 19, 54, 54, 54, 54, 19, 4, 4, 19,
- 19, 120, 120, 4, 4, 4, 4, 4, 120, 19, 19, 19, 19, 4, 4, 4, 4, 370, 4, 26,
- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 371, 371,
- 371, 371, 371, 371, 371, 371, 371, 371, 371, 371, 371, 371, 371, 371,
+ 358, 358, 20, 20, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 5, 4, 4, 5, 2, 2, 20, 20, 20, 20, 20, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 17, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 20, 20, 20, 20, 20, 0, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 359, 101, 0, 0, 360, 361, 362, 363,
+ 364, 365, 4, 4, 4, 4, 4, 101, 359, 25, 21, 22, 360, 361, 362, 363, 364,
+ 365, 4, 4, 4, 4, 4, 0, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
+ 101, 101, 101, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 5, 5, 5, 5, 24, 5, 5, 5, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 120, 4, 4, 4, 4,
+ 120, 4, 4, 19, 120, 120, 120, 19, 19, 120, 120, 120, 19, 4, 120, 4, 4,
+ 366, 120, 120, 120, 120, 120, 4, 4, 4, 4, 4, 4, 120, 4, 367, 4, 120, 4,
+ 368, 369, 120, 120, 366, 19, 120, 120, 370, 120, 19, 54, 54, 54, 54, 19,
+ 4, 4, 19, 19, 120, 120, 4, 4, 4, 4, 4, 120, 19, 19, 19, 19, 4, 4, 4, 4,
+ 371, 4, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372, 372,
- 372, 372, 242, 242, 242, 29, 30, 242, 242, 242, 242, 26, 4, 4, 0, 0, 0,
- 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 372, 372, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373,
+ 373, 373, 373, 373, 242, 242, 242, 29, 30, 242, 242, 242, 242, 26, 4, 4,
+ 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
@@ -2808,26 +2809,26 @@ static const unsigned short index2[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 25, 21, 22, 359, 360, 361, 362, 363, 364, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 25, 21, 22, 359, 360, 361, 362, 363, 364, 26, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 25, 21, 22, 359, 360, 361, 362, 363, 364, 26,
- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 373, 373, 373, 373, 373,
- 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373, 373,
- 373, 373, 373, 373, 373, 373, 373, 374, 374, 374, 374, 374, 374, 374,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 25, 21, 22, 360, 361, 362, 363, 364, 365, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 25, 21, 22, 360, 361, 362, 363, 364, 365, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 25, 21, 22, 360, 361, 362, 363, 364,
+ 365, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 374, 374, 374, 374,
374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374, 374,
- 374, 374, 374, 374, 374, 358, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 25,
- 21, 22, 359, 360, 361, 362, 363, 364, 26, 358, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 374, 374, 374, 374, 374, 374, 374, 374, 375, 375, 375, 375, 375, 375,
+ 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, 375, 375,
+ 375, 375, 375, 375, 375, 375, 359, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 26, 25, 21, 22, 360, 361, 362, 363, 364, 365, 26, 359, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 25, 21, 22, 359, 360, 361, 362,
- 363, 364, 26, 25, 21, 22, 359, 360, 361, 362, 363, 364, 26, 25, 21, 22,
- 359, 360, 361, 362, 363, 364, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 25, 21, 22, 360, 361,
+ 362, 363, 364, 365, 26, 25, 21, 22, 360, 361, 362, 363, 364, 365, 26, 25,
+ 21, 22, 360, 361, 362, 363, 364, 365, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
@@ -2836,85 +2837,85 @@ static const unsigned short index2[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 135, 135, 135, 135, 135,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 135, 135, 135, 135,
135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135,
135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135,
135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135, 135,
- 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
+ 135, 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, 136,
- 136, 136, 136, 136, 136, 136, 136, 29, 30, 375, 376, 377, 378, 379, 29,
- 30, 29, 30, 29, 30, 380, 381, 382, 383, 19, 29, 30, 19, 29, 30, 19, 19,
- 19, 19, 19, 101, 101, 384, 384, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30,
- 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30,
- 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30,
- 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30,
- 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30,
- 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30,
- 19, 4, 4, 4, 4, 4, 4, 29, 30, 29, 30, 24, 24, 24, 29, 30, 0, 0, 0, 0, 0,
- 4, 4, 4, 4, 26, 4, 4, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385,
- 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385,
- 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 385, 0,
- 385, 0, 0, 0, 0, 0, 385, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 136, 136, 136, 136, 136, 136, 136, 136, 29, 30, 376, 377, 378, 379, 380,
+ 29, 30, 29, 30, 29, 30, 381, 382, 383, 384, 19, 29, 30, 19, 29, 30, 19,
+ 19, 19, 19, 19, 101, 101, 385, 385, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 19, 4, 4, 4, 4, 4, 4, 29, 30, 29, 30, 24, 24, 24, 29, 30, 0, 0, 0, 0,
+ 0, 4, 4, 4, 4, 26, 4, 4, 386, 386, 386, 386, 386, 386, 386, 386, 386,
+ 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386,
+ 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386, 386,
+ 386, 0, 386, 0, 0, 0, 0, 0, 386, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 102, 4, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54,
- 54, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54,
- 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54,
- 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 0, 24, 24, 24, 24, 24, 24, 24,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 102,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54,
+ 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 0,
+ 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54,
+ 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 0, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 386, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 387, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 0, 0, 0, 0, 1, 4, 4, 4, 4, 102, 54, 242, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 242, 242, 242,
- 242, 242, 242, 242, 242, 242, 24, 24, 24, 24, 17, 17, 4, 102, 102, 102,
- 102, 102, 4, 4, 242, 242, 242, 102, 54, 4, 4, 4, 0, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 1, 4, 4, 4, 4, 102, 54, 242, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 242, 242,
+ 242, 242, 242, 242, 242, 242, 242, 24, 24, 24, 24, 17, 17, 4, 102, 102,
+ 102, 102, 102, 4, 4, 242, 242, 242, 102, 54, 4, 4, 4, 0, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 24, 24, 5, 5, 102, 102, 54, 4,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 24, 24, 5, 5, 102, 102, 54,
+ 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 4, 102, 102, 102, 54, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 17, 102, 102, 102, 54, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 0, 4, 4, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 54,
+ 54, 54, 54, 54, 0, 4, 4, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 4, 4, 4, 4, 4, 4, 4,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54,
+ 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
@@ -2927,14 +2928,14 @@ static const unsigned short index2[] = {
26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 54, 54,
- 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
@@ -2943,7 +2944,7 @@ static const unsigned short index2[] = {
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
@@ -2952,7 +2953,7 @@ static const unsigned short index2[] = {
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
@@ -2961,501 +2962,551 @@ static const unsigned short index2[] = {
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 387, 54, 54, 387, 54, 54, 54, 387,
- 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 388, 54, 54, 388, 54, 54, 54, 388,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 388, 54, 388,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 387, 54, 387,
+ 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 388, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 388, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 387, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 387, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 387, 54,
- 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 387, 387, 387,
- 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 387,
- 387, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 388, 54, 388, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388,
+ 54, 388, 388, 388, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 388, 388, 388, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54,
- 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 387, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 387, 387, 54, 387, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 388, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 388, 388,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 387, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54,
+ 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54,
- 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 102, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0,
- 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54,
+ 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 388,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 102, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 102,
- 102, 102, 102, 102, 102, 4, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 102, 4, 4, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 54, 54, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 29, 30, 29, 30, 29, 30,
- 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 102, 102, 102,
+ 102, 102, 102, 4, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 102,
+ 4, 4, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 54,
+ 24, 5, 5, 5, 4, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 4, 102, 29, 30,
29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30,
- 29, 30, 54, 24, 5, 5, 5, 4, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 4,
- 102, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
- 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 101, 101, 24, 24, 54, 54, 54,
+ 29, 30, 29, 30, 29, 30, 29, 30, 101, 101, 24, 24, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 242, 242, 242, 242,
- 242, 242, 242, 242, 242, 242, 24, 24, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0,
- 0, 0, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
- 5, 102, 102, 102, 102, 102, 102, 102, 102, 102, 5, 5, 29, 30, 29, 30, 29,
- 30, 29, 30, 29, 30, 29, 30, 29, 30, 19, 19, 29, 30, 29, 30, 29, 30, 29,
- 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 242, 242, 242, 242, 242, 242,
+ 242, 242, 242, 242, 24, 24, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 102,
+ 102, 102, 102, 102, 102, 102, 102, 102, 5, 5, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 19, 19, 29, 30, 29, 30, 29, 30, 29, 30, 29,
30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
- 30, 101, 19, 19, 19, 19, 19, 19, 19, 19, 29, 30, 29, 30, 388, 29, 30, 29,
- 30, 29, 30, 29, 30, 29, 30, 102, 5, 5, 29, 30, 389, 19, 54, 29, 30, 29,
- 30, 390, 19, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
- 30, 29, 30, 29, 30, 391, 392, 393, 394, 391, 19, 395, 396, 397, 398, 29,
- 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 399, 400,
- 401, 29, 30, 29, 30, 0, 0, 0, 0, 0, 29, 30, 0, 19, 0, 19, 29, 30, 29, 30,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 101, 101, 101, 29, 30, 54, 101, 101, 19, 54, 54, 54, 54, 54, 54, 54, 24,
- 54, 54, 54, 24, 54, 54, 54, 54, 24, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 17, 17, 24, 24,
- 17, 4, 4, 4, 4, 24, 0, 0, 0, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 0, 0, 0,
- 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 101,
+ 19, 19, 19, 19, 19, 19, 19, 19, 29, 30, 29, 30, 389, 29, 30, 29, 30, 29,
+ 30, 29, 30, 29, 30, 102, 5, 5, 29, 30, 390, 19, 54, 29, 30, 29, 30, 391,
+ 19, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29,
+ 30, 29, 30, 392, 393, 394, 395, 392, 19, 396, 397, 398, 399, 29, 30, 29,
+ 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 29, 30, 400, 401, 402, 29,
+ 30, 29, 30, 0, 0, 0, 0, 0, 29, 30, 0, 19, 0, 19, 29, 30, 29, 30, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 101,
+ 101, 29, 30, 54, 101, 101, 19, 54, 54, 54, 54, 54, 54, 54, 24, 54, 54,
+ 54, 24, 54, 54, 54, 54, 24, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 17, 17, 24, 24, 17, 4, 4,
+ 4, 4, 24, 0, 0, 0, 26, 26, 26, 26, 26, 26, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 4, 4, 4,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
- 17, 17, 17, 17, 17, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 24, 24, 54, 54, 54, 54, 54, 54, 4, 4, 4, 54,
- 4, 54, 54, 24, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+ 17, 17, 17, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 6, 7, 8, 9, 10, 11, 12,
+ 13, 14, 15, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 54, 54, 54, 54, 54, 54, 4, 4, 4, 54, 4, 54,
+ 54, 24, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 17, 17, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 24,
- 24, 24, 17, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 17, 17, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 24, 24,
+ 24, 17, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 24, 17, 17, 24,
- 24, 24, 24, 17, 17, 24, 24, 17, 17, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 0, 102, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 4, 4, 54,
- 54, 54, 54, 54, 24, 102, 54, 54, 54, 54, 54, 54, 54, 54, 54, 6, 7, 8, 9,
- 10, 11, 12, 13, 14, 15, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 24, 17, 17, 24, 24,
+ 24, 24, 17, 17, 24, 24, 17, 17, 17, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 0, 102, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 4, 4, 54, 54,
+ 54, 54, 54, 24, 102, 54, 54, 54, 54, 54, 54, 54, 54, 54, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 24,
- 24, 24, 24, 24, 24, 17, 17, 24, 24, 17, 17, 24, 24, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 54, 54, 54, 24, 54, 54, 54, 54, 54, 54, 54, 54, 24, 17, 0, 0, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 4, 4, 4, 4, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 102, 54, 54, 54, 54, 54, 54, 4,
- 4, 4, 54, 17, 24, 17, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 24, 24,
+ 24, 24, 24, 24, 17, 17, 24, 24, 17, 17, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 54, 54, 54, 24, 54, 54, 54, 54, 54, 54, 54, 54, 24, 17, 0, 0, 6, 7, 8,
+ 9, 10, 11, 12, 13, 14, 15, 0, 0, 4, 4, 4, 4, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 102, 54, 54, 54, 54, 54, 54, 4, 4, 4,
+ 54, 17, 24, 17, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 24, 54, 24, 24, 24, 54, 54, 24, 24, 54, 54, 54, 54, 54, 24, 24, 54,
- 24, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 54, 54, 102, 4, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 17,
- 24, 24, 17, 17, 4, 4, 54, 102, 102, 17, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54,
- 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54,
- 54, 54, 54, 54, 54, 54, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+ 24, 54, 24, 24, 24, 54, 54, 24, 24, 54, 54, 54, 54, 54, 24, 24, 54, 24,
+ 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 54, 54, 102, 4, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 17, 24,
+ 24, 17, 17, 4, 4, 54, 102, 102, 17, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54,
+ 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54,
+ 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54,
+ 54, 54, 54, 54, 54, 0, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
- 19, 19, 19, 19, 19, 19, 402, 19, 19, 19, 19, 19, 19, 19, 5, 101, 101,
- 101, 101, 19, 19, 19, 19, 19, 19, 19, 19, 19, 101, 5, 5, 0, 0, 0, 0, 403,
- 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417,
- 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431,
- 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445,
- 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459,
- 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473,
- 474, 475, 476, 477, 478, 479, 480, 481, 482, 54, 54, 54, 54, 54, 54, 54,
+ 19, 19, 19, 19, 19, 403, 19, 19, 19, 19, 19, 19, 19, 5, 101, 101, 101,
+ 101, 19, 19, 19, 19, 19, 19, 19, 19, 19, 101, 5, 5, 0, 0, 0, 0, 404, 405,
+ 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419,
+ 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433,
+ 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447,
+ 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461,
+ 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475,
+ 476, 477, 478, 479, 480, 481, 482, 483, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 17, 17, 24, 17, 17, 24, 17, 17,
- 4, 17, 24, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 17, 17, 24, 17, 17, 24, 17, 17, 4,
+ 17, 24, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 387, 54,
+ 388, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 388, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 387,
+ 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 388, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 483, 484, 485, 486, 487, 488, 489, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 490, 491, 492, 493, 494, 0, 0, 0, 0, 0, 54, 24, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 0, 54, 54, 54, 54, 54, 0, 54, 0, 54, 54, 0, 54, 54, 0, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 484,
+ 485, 486, 487, 488, 489, 490, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 491,
+ 492, 493, 494, 495, 0, 0, 0, 0, 0, 54, 24, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 54,
+ 54, 54, 54, 54, 0, 54, 0, 54, 54, 0, 54, 54, 0, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 5, 5, 5, 5,
- 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 495,
- 495, 495, 495, 495, 495, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 496, 496, 496,
+ 496, 496, 496, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 4,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 495, 495,
- 4, 4, 4, 4, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
- 24, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 17, 17, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 17,
- 4, 4, 5, 0, 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0,
- 4, 4, 4, 4, 0, 0, 0, 0, 495, 54, 495, 54, 495, 0, 495, 54, 495, 54, 495,
- 54, 495, 54, 495, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 496, 496, 4, 4, 4,
+ 4, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 4, 4,
+ 4, 5, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 24, 24, 24, 24, 24, 24, 24, 24,
+ 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 17, 17, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 17, 17, 17, 4, 4, 5, 0,
+ 4, 5, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4,
+ 0, 0, 0, 0, 496, 54, 496, 54, 496, 0, 496, 54, 496, 54, 496, 54, 496, 54,
+ 496, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 0, 0, 20, 0, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 5, 4,
- 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 5, 4, 4, 4, 4, 4, 4, 16, 16, 16, 16,
+ 54, 0, 0, 20, 0, 4, 4, 4, 4, 4, 4, 5, 4, 4, 4, 4, 4, 4, 5, 4, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 5, 4, 4, 4, 4, 4, 4, 16, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
- 16, 16, 16, 16, 4, 4, 4, 5, 17, 5, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 102,
+ 16, 4, 4, 4, 5, 17, 5, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 17, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 102, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 496, 496, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 497, 497, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 0, 0, 0, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54,
- 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 0, 0, 0,
- 4, 4, 4, 5, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 20, 20, 20, 4, 4, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 0, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54,
+ 54, 54, 54, 0, 0, 0, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54,
+ 54, 0, 0, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 0, 0, 0, 4, 4, 4, 5,
+ 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20,
+ 20, 4, 4, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 0, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 4, 4,
- 4, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 4, 4, 4, 0, 0, 0,
+ 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242,
242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242,
242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242,
- 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 26,
- 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 26, 26, 4,
- 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, 0, 0, 0,
+ 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 242, 26, 26, 26, 26, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 26, 26, 4, 4, 4, 0, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 0, 0, 54, 54, 54, 54,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 24, 0, 0, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 26, 26,
+ 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54,
+ 26, 26, 26, 26, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54,
+ 54, 54, 54, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 242, 54,
+ 54, 54, 54, 54, 54, 54, 54, 242, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 242, 54, 54, 54, 54, 54, 54, 54, 54, 242, 0, 0, 0, 0, 0, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 24,
- 24, 24, 24, 24, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 24, 24, 24, 24,
+ 24, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 4,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 0, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 4, 242, 242, 242,
- 242, 242, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 497, 497,
- 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497,
- 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497,
- 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 498, 498, 498, 498,
+ 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 4, 242, 242, 242, 242, 242,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 498, 498, 498, 498,
498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498,
498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498,
- 498, 498, 498, 498, 498, 498, 498, 498, 54, 54, 54, 54, 54, 54, 54, 54,
+ 498, 498, 498, 498, 498, 498, 498, 498, 499, 499, 499, 499, 499, 499,
+ 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499,
+ 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499,
+ 499, 499, 499, 499, 499, 499, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 6,
- 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 497, 497, 497, 497,
- 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497,
- 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497, 497,
- 497, 497, 497, 497, 0, 0, 0, 0, 498, 498, 498, 498, 498, 498, 498, 498,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 498, 498, 498, 498, 498, 498, 498,
498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498,
- 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 0,
- 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498, 498,
+ 498, 0, 0, 0, 0, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499,
+ 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499,
+ 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 0, 0, 0, 0, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 4, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 0, 499, 499,
- 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 499, 0, 499,
- 499, 499, 499, 499, 499, 499, 0, 499, 499, 0, 500, 500, 500, 500, 500,
- 500, 500, 500, 500, 500, 500, 0, 500, 500, 500, 500, 500, 500, 500, 500,
- 500, 500, 500, 500, 500, 500, 500, 0, 500, 500, 500, 500, 500, 500, 500,
- 0, 500, 500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 500, 500,
+ 500, 500, 500, 500, 500, 500, 500, 500, 500, 0, 500, 500, 500, 500, 500,
+ 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 0, 500, 500, 500, 500,
+ 500, 500, 500, 0, 500, 500, 0, 501, 501, 501, 501, 501, 501, 501, 501,
+ 501, 501, 501, 0, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501,
+ 501, 501, 501, 501, 0, 501, 501, 501, 501, 501, 501, 501, 0, 501, 501, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 102, 102, 101, 101, 101, 0, 101, 101,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54,
+ 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 101, 102, 102, 101, 101, 101, 0, 101, 101, 101, 101, 101,
101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101,
- 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 0, 101, 101,
- 101, 101, 101, 101, 101, 101, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 101, 101, 101, 101, 101, 101, 101, 101, 101, 0, 101, 101, 101, 101, 101,
+ 101, 101, 101, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 0, 0, 54, 0, 54, 54, 54,
+ 0, 0, 0, 54, 54, 54, 54, 54, 54, 0, 0, 54, 0, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 0, 54, 54, 0, 0, 0, 54, 0, 0, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 4,
- 26, 26, 26, 26, 26, 26, 26, 26, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 4, 4, 26, 26, 26, 26,
- 26, 26, 26, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0,
- 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0,
+ 54, 0, 54, 54, 0, 0, 0, 54, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 4, 26, 26, 26, 26,
+ 26, 26, 26, 26, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 4, 4, 26, 26, 26, 26, 26, 26, 26, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 0, 0,
- 0, 0, 0, 26, 26, 26, 26, 26, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 26, 26, 26, 26, 26, 26, 0, 0,
- 0, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 54, 54, 0, 0, 0, 0, 0, 26, 26,
+ 26, 26, 26, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 26, 26, 26, 26, 26, 26, 0, 0, 0, 4, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 26, 26, 54, 54, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 26, 26, 26,
+ 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 26, 26, 54, 54, 26, 26, 26, 26, 26,
+ 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 0, 0, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
- 26, 26, 26, 26, 26, 26, 26, 54, 24, 24, 24, 0, 24, 24, 0, 0, 0, 0, 0, 24,
- 24, 24, 24, 54, 54, 54, 54, 0, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54,
+ 26, 26, 26, 26, 54, 24, 24, 24, 0, 24, 24, 0, 0, 0, 0, 0, 24, 24, 24, 24,
+ 54, 54, 54, 54, 0, 54, 54, 54, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 0, 0, 24, 24, 24, 0, 0, 0, 0, 24, 25, 21, 22, 359, 26,
- 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0,
- 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 26, 26, 4, 54,
+ 54, 0, 0, 24, 24, 24, 0, 0, 0, 0, 24, 25, 21, 22, 360, 26, 26, 26, 26,
+ 26, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 54, 54, 54, 54, 54, 54, 54, 54, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 26, 26, 4, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 24, 24, 0, 0, 0, 0, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54,
+ 54, 54, 54, 54, 54, 54, 4, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 24,
+ 24, 0, 0, 0, 0, 26, 26, 26, 26, 26, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 26, 26,
- 26, 26, 26, 26, 26, 26, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26,
- 26, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 26, 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 54, 54, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 26, 26, 26,
+ 26, 26, 26, 26, 26, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26,
+ 26, 26, 26, 26, 26, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
+ 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108, 108,
108, 108, 108, 108, 108, 108, 108, 108, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -3473,7 +3524,7 @@ static const unsigned short index2[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 25, 21, 22, 359, 360, 361, 362, 363, 364, 26, 26,
+ 0, 0, 0, 0, 0, 0, 0, 0, 25, 21, 22, 360, 361, 362, 363, 364, 365, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26,
26, 26, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
@@ -3498,7 +3549,7 @@ static const unsigned short index2[] = {
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 4, 4, 4, 4,
- 4, 4, 4, 0, 0, 0, 0, 25, 21, 22, 359, 360, 361, 362, 363, 364, 26, 26,
+ 4, 4, 4, 0, 0, 0, 0, 25, 21, 22, 360, 361, 362, 363, 364, 365, 26, 26,
26, 26, 26, 26, 26, 26, 26, 26, 26, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
24, 54, 54, 24, 24, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 24, 24, 17, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
@@ -3974,12 +4025,12 @@ static const unsigned short index2[] = {
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 0, 0, 26, 26, 26, 26, 26, 26, 26, 26, 26, 24, 24, 24, 24, 24, 24, 24,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 501, 501, 501, 501,
- 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501,
- 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501,
- 501, 501, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 502, 502, 502, 502,
+ 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502,
502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502, 502,
- 502, 502, 502, 502, 502, 502, 502, 502, 24, 24, 24, 24, 24, 24, 24, 102,
+ 502, 502, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503,
+ 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503,
+ 503, 503, 503, 503, 503, 503, 503, 503, 24, 24, 24, 24, 24, 24, 24, 102,
0, 0, 0, 0, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 4, 4, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -4023,15 +4074,15 @@ static const unsigned short index2[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 358, 358, 25, 21, 22, 359, 360, 361, 362, 363, 364, 26, 26, 4, 4,
+ 0, 0, 359, 359, 25, 21, 22, 360, 361, 362, 363, 364, 365, 26, 26, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 4, 4, 4, 4, 4, 4, 4, 4, 503, 503, 503, 503, 503, 503, 503, 503, 503,
- 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503,
- 503, 503, 503, 4, 4, 4, 4, 4, 4, 503, 503, 503, 503, 503, 503, 503, 503,
- 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503,
- 503, 503, 503, 503, 4, 4, 4, 4, 4, 4, 503, 503, 503, 503, 503, 503, 503,
- 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503, 503,
- 503, 503, 503, 503, 503, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 504, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 504, 4, 4, 4, 4, 4, 4, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 504, 504, 4, 4, 4, 4, 4, 4, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504, 504,
+ 504, 504, 504, 504, 504, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@@ -4089,31 +4140,30 @@ static const unsigned short index2[] = {
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 7,
- 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 54, 387, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 54, 388, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
@@ -4124,32 +4174,32 @@ static const unsigned short index2[] = {
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 388, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 387, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 388, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
@@ -4159,68 +4209,69 @@ static const unsigned short index2[] = {
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 387, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 54, 54, 54,
+ 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0,
- 0, 0, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
- 54, 54, 54, 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
+ 54, 54, 54, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
- 20, 20, 20, 20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
+ 20, 20, 20, 20, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
@@ -4233,8 +4284,8 @@ static const unsigned short index2[] = {
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
- 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
};
/* Returns the numeric value as double for Unicode characters
@@ -4282,6 +4333,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch)
case 0x24EA:
case 0x24FF:
case 0x3007:
+ case 0x6D1E:
case 0x96F6:
case 0xA620:
case 0xA6EF:
@@ -4713,6 +4765,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch)
case 0x1ECA0:
case 0x1ECB4:
return (double) 100000.0;
+ case 0x5146:
case 0x16B5E:
return (double) 1000000.0;
case 0x1ECA1:
@@ -4721,11 +4774,14 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch)
case 0x5104:
case 0x16B5F:
return (double) 100000000.0;
+ case 0x79ED:
+ return (double) 1000000000.0;
case 0x16B60:
return (double) 10000000000.0;
- case 0x5146:
case 0x16B61:
return (double) 1000000000000.0;
+ case 0x4EAC:
+ return (double) 1e+16;
case 0x216A:
case 0x217A:
case 0x246A:
@@ -4865,7 +4921,10 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch)
case 0x3221:
case 0x3281:
case 0x3483:
+ case 0x4E24:
case 0x4E8C:
+ case 0x4FE9:
+ case 0x5006:
case 0x5169:
case 0x5F0D:
case 0x5F10:
@@ -5008,6 +5067,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch)
case 0x1EC7B:
case 0x1ED0B:
return (double) 20.0;
+ case 0x7695:
case 0x1011A:
case 0x102F4:
case 0x109D3:
@@ -5894,6 +5954,7 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch)
case 0x3286:
case 0x3B4D:
case 0x4E03:
+ case 0x62D0:
case 0x67D2:
case 0x6F06:
case 0xA627:
@@ -6198,6 +6259,8 @@ double _PyUnicode_ToNumeric(Py_UCS4 ch)
case 0x4E5D:
case 0x5EFE:
case 0x7396:
+ case 0x920E:
+ case 0x94A9:
case 0xA629:
case 0xA6EE:
case 0xA8D9:
diff --git a/contrib/tools/python3/Objects/unionobject.c b/contrib/tools/python3/Objects/unionobject.c
index f509a161bb9..79103bc2ac2 100644
--- a/contrib/tools/python3/Objects/unionobject.c
+++ b/contrib/tools/python3/Objects/unionobject.c
@@ -3,7 +3,7 @@
#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
#include "pycore_typevarobject.h" // _PyTypeAlias_Type
#include "pycore_unionobject.h"
-#include "structmember.h"
+
static PyObject *make_union(PyObject *);
@@ -186,37 +186,30 @@ union_repr_item(_PyUnicodeWriter *writer, PyObject *p)
{
PyObject *qualname = NULL;
PyObject *module = NULL;
- PyObject *tmp;
PyObject *r = NULL;
- int err;
+ int rc;
if (p == (PyObject *)&_PyNone_Type) {
return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4);
}
- if (_PyObject_LookupAttr(p, &_Py_ID(__origin__), &tmp) < 0) {
- goto exit;
+ if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
+ (rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
+ {
+ // It looks like a GenericAlias
+ goto use_repr;
}
-
- if (tmp) {
- Py_DECREF(tmp);
- if (_PyObject_LookupAttr(p, &_Py_ID(__args__), &tmp) < 0) {
- goto exit;
- }
- if (tmp) {
- // It looks like a GenericAlias
- Py_DECREF(tmp);
- goto use_repr;
- }
+ if (rc < 0) {
+ goto exit;
}
- if (_PyObject_LookupAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
+ if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
goto exit;
}
if (qualname == NULL) {
goto use_repr;
}
- if (_PyObject_LookupAttr(p, &_Py_ID(__module__), &module) < 0) {
+ if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
goto exit;
}
if (module == NULL || module == Py_None) {
@@ -244,9 +237,9 @@ exit:
if (r == NULL) {
return -1;
}
- err = _PyUnicodeWriter_WriteStr(writer, r);
+ rc = _PyUnicodeWriter_WriteStr(writer, r);
Py_DECREF(r);
- return err;
+ return rc;
}
static PyObject *
@@ -273,21 +266,33 @@ error:
}
static PyMemberDef union_members[] = {
- {"__args__", T_OBJECT, offsetof(unionobject, args), READONLY},
+ {"__args__", _Py_T_OBJECT, offsetof(unionobject, args), Py_READONLY},
{0}
};
-static PyObject *
-union_getitem(PyObject *self, PyObject *item)
+// Populate __parameters__ if needed.
+static int
+union_init_parameters(unionobject *alias)
{
- unionobject *alias = (unionobject *)self;
- // Populate __parameters__ if needed.
+ int result = 0;
+ Py_BEGIN_CRITICAL_SECTION(alias);
if (alias->parameters == NULL) {
alias->parameters = _Py_make_parameters(alias->args);
if (alias->parameters == NULL) {
- return NULL;
+ result = -1;
}
}
+ Py_END_CRITICAL_SECTION();
+ return result;
+}
+
+static PyObject *
+union_getitem(PyObject *self, PyObject *item)
+{
+ unionobject *alias = (unionobject *)self;
+ if (union_init_parameters(alias) < 0) {
+ return NULL;
+ }
PyObject *newargs = _Py_subs_parameters(self, alias->args, alias->parameters, item);
if (newargs == NULL) {
@@ -321,17 +326,15 @@ static PyObject *
union_parameters(PyObject *self, void *Py_UNUSED(unused))
{
unionobject *alias = (unionobject *)self;
- if (alias->parameters == NULL) {
- alias->parameters = _Py_make_parameters(alias->args);
- if (alias->parameters == NULL) {
- return NULL;
- }
+ if (union_init_parameters(alias) < 0) {
+ return NULL;
}
return Py_NewRef(alias->parameters);
}
static PyGetSetDef union_properties[] = {
- {"__parameters__", union_parameters, (setter)NULL, "Type variables in the types.UnionType.", NULL},
+ {"__parameters__", union_parameters, (setter)NULL,
+ PyDoc_STR("Type variables in the types.UnionType."), NULL},
{0}
};
diff --git a/contrib/tools/python3/Objects/weakrefobject.c b/contrib/tools/python3/Objects/weakrefobject.c
index aee79fc1410..61f05514a48 100644
--- a/contrib/tools/python3/Objects/weakrefobject.c
+++ b/contrib/tools/python3/Objects/weakrefobject.c
@@ -1,25 +1,62 @@
#include "Python.h"
+#include "pycore_critical_section.h"
+#include "pycore_lock.h"
+#include "pycore_modsupport.h" // _PyArg_NoKwnames()
#include "pycore_object.h" // _PyObject_GET_WEAKREFS_LISTPTR()
-#include "structmember.h" // PyMemberDef
+#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
+#include "pycore_pystate.h"
+#include "pycore_weakref.h" // _PyWeakref_GET_REF()
+#ifdef Py_GIL_DISABLED
+/*
+ * Thread-safety for free-threaded builds
+ * ======================================
+ *
+ * In free-threaded builds we need to protect mutable state of:
+ *
+ * - The weakref (wr_object, hash, wr_callback)
+ * - The referenced object (its head-of-list pointer)
+ * - The linked list of weakrefs
+ *
+ * For now we've chosen to address this in a straightforward way:
+ *
+ * - The weakref's hash is protected using the weakref's per-object lock.
+ * - The other mutable is protected by a striped lock keyed on the referenced
+ * object's address.
+ * - The striped lock must be locked using `_Py_LOCK_DONT_DETACH` in order to
+ * support atomic deletion from WeakValueDictionaries. As a result, we must
+ * be careful not to perform any operations that could suspend while the
+ * lock is held.
+ *
+ * Since the world is stopped when the GC runs, it is free to clear weakrefs
+ * without acquiring any locks.
+ */
+
+#endif
#define GET_WEAKREFS_LISTPTR(o) \
((PyWeakReference **) _PyObject_GET_WEAKREFS_LISTPTR(o))
Py_ssize_t
-_PyWeakref_GetWeakrefCount(PyWeakReference *head)
+_PyWeakref_GetWeakrefCount(PyObject *obj)
{
- Py_ssize_t count = 0;
+ if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
+ return 0;
+ }
+ LOCK_WEAKREFS(obj);
+ Py_ssize_t count = 0;
+ PyWeakReference *head = *GET_WEAKREFS_LISTPTR(obj);
while (head != NULL) {
++count;
head = head->wr_next;
}
+ UNLOCK_WEAKREFS(obj);
return count;
}
-static PyObject *weakref_vectorcall(PyWeakReference *self, PyObject *const *args, size_t nargsf, PyObject *kwnames);
+static PyObject *weakref_vectorcall(PyObject *self, PyObject *const *args, size_t nargsf, PyObject *kwnames);
static void
init_weakref(PyWeakReference *self, PyObject *ob, PyObject *callback)
@@ -29,55 +66,56 @@ init_weakref(PyWeakReference *self, PyObject *ob, PyObject *callback)
self->wr_prev = NULL;
self->wr_next = NULL;
self->wr_callback = Py_XNewRef(callback);
- self->vectorcall = (vectorcallfunc)weakref_vectorcall;
+ self->vectorcall = weakref_vectorcall;
+#ifdef Py_GIL_DISABLED
+ self->weakrefs_lock = &WEAKREF_LIST_LOCK(ob);
+ _PyObject_SetMaybeWeakref(ob);
+ _PyObject_SetMaybeWeakref((PyObject *)self);
+#endif
}
-static PyWeakReference *
-new_weakref(PyObject *ob, PyObject *callback)
-{
- PyWeakReference *result;
-
- result = PyObject_GC_New(PyWeakReference, &_PyWeakref_RefType);
- if (result) {
- init_weakref(result, ob, callback);
- PyObject_GC_Track(result);
- }
- return result;
-}
-
-
-/* This function clears the passed-in reference and removes it from the
- * list of weak references for the referent. This is the only code that
- * removes an item from the doubly-linked list of weak references for an
- * object; it is also responsible for clearing the callback slot.
- */
+// Clear the weakref and steal its callback into `callback`, if provided.
static void
-clear_weakref(PyWeakReference *self)
+clear_weakref_lock_held(PyWeakReference *self, PyObject **callback)
{
- PyObject *callback = self->wr_callback;
-
if (self->wr_object != Py_None) {
PyWeakReference **list = GET_WEAKREFS_LISTPTR(self->wr_object);
-
- if (*list == self)
- /* If 'self' is the end of the list (and thus self->wr_next == NULL)
- then the weakref list itself (and thus the value of *list) will
- end up being set to NULL. */
- *list = self->wr_next;
- self->wr_object = Py_None;
- if (self->wr_prev != NULL)
+ if (*list == self) {
+ /* If 'self' is the end of the list (and thus self->wr_next ==
+ NULL) then the weakref list itself (and thus the value of *list)
+ will end up being set to NULL. */
+ FT_ATOMIC_STORE_PTR(*list, self->wr_next);
+ }
+ FT_ATOMIC_STORE_PTR(self->wr_object, Py_None);
+ if (self->wr_prev != NULL) {
self->wr_prev->wr_next = self->wr_next;
- if (self->wr_next != NULL)
+ }
+ if (self->wr_next != NULL) {
self->wr_next->wr_prev = self->wr_prev;
+ }
self->wr_prev = NULL;
self->wr_next = NULL;
}
if (callback != NULL) {
- Py_DECREF(callback);
+ *callback = self->wr_callback;
self->wr_callback = NULL;
}
}
+// Clear the weakref and its callback
+static void
+clear_weakref(PyWeakReference *self)
+{
+ PyObject *callback = NULL;
+ // self->wr_object may be Py_None if the GC cleared the weakref, so lock
+ // using the pointer in the weakref.
+ LOCK_WEAKREFS_FOR_WR(self);
+ clear_weakref_lock_held(self, &callback);
+ UNLOCK_WEAKREFS_FOR_WR(self);
+ Py_XDECREF(callback);
+}
+
+
/* Cyclic gc uses this to *just* clear the passed-in reference, leaving
* the callback intact and uncalled. It must be possible to call self's
* tp_dealloc() after calling this, so self has to be left in a sane enough
@@ -92,15 +130,9 @@ clear_weakref(PyWeakReference *self)
void
_PyWeakref_ClearRef(PyWeakReference *self)
{
- PyObject *callback;
-
assert(self != NULL);
assert(PyWeakref_Check(self));
- /* Preserve and restore the callback around clear_weakref. */
- callback = self->wr_callback;
- self->wr_callback = NULL;
- clear_weakref(self);
- self->wr_callback = callback;
+ clear_weakref_lock_held(self, NULL);
}
static void
@@ -123,13 +155,17 @@ gc_traverse(PyWeakReference *self, visitproc visit, void *arg)
static int
gc_clear(PyWeakReference *self)
{
- clear_weakref(self);
+ PyObject *callback;
+ // The world is stopped during GC in free-threaded builds. It's safe to
+ // call this without holding the lock.
+ clear_weakref_lock_held(self, &callback);
+ Py_XDECREF(callback);
return 0;
}
static PyObject *
-weakref_vectorcall(PyWeakReference *self, PyObject *const *args,
+weakref_vectorcall(PyObject *self, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
if (!_PyArg_NoKwnames("weakref", kwnames)) {
@@ -139,52 +175,57 @@ weakref_vectorcall(PyWeakReference *self, PyObject *const *args,
if (!_PyArg_CheckPositional("weakref", nargs, 0, 0)) {
return NULL;
}
- return Py_NewRef(PyWeakref_GET_OBJECT(self));
+ PyObject *obj = _PyWeakref_GET_REF(self);
+ if (obj == NULL) {
+ Py_RETURN_NONE;
+ }
+ return obj;
}
static Py_hash_t
-weakref_hash(PyWeakReference *self)
+weakref_hash_lock_held(PyWeakReference *self)
{
if (self->hash != -1)
return self->hash;
- PyObject* obj = PyWeakref_GET_OBJECT(self);
- if (obj == Py_None) {
+ PyObject* obj = _PyWeakref_GET_REF((PyObject*)self);
+ if (obj == NULL) {
PyErr_SetString(PyExc_TypeError, "weak object has gone away");
return -1;
}
- Py_INCREF(obj);
self->hash = PyObject_Hash(obj);
Py_DECREF(obj);
return self->hash;
}
+static Py_hash_t
+weakref_hash(PyWeakReference *self)
+{
+ Py_hash_t hash;
+ Py_BEGIN_CRITICAL_SECTION(self);
+ hash = weakref_hash_lock_held(self);
+ Py_END_CRITICAL_SECTION();
+ return hash;
+}
static PyObject *
-weakref_repr(PyWeakReference *self)
+weakref_repr(PyObject *self)
{
- PyObject *name, *repr;
- PyObject* obj = PyWeakref_GET_OBJECT(self);
-
- if (obj == Py_None) {
+ PyObject* obj = _PyWeakref_GET_REF(self);
+ if (obj == NULL) {
return PyUnicode_FromFormat("<weakref at %p; dead>", self);
}
- Py_INCREF(obj);
- name = _PyObject_LookupSpecial(obj, &_Py_ID(__name__));
+ PyObject *name = _PyObject_LookupSpecial(obj, &_Py_ID(__name__));
+ PyObject *repr;
if (name == NULL || !PyUnicode_Check(name)) {
repr = PyUnicode_FromFormat(
- "<weakref at %p; to '%s' at %p>",
- self,
- Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name,
- obj);
+ "<weakref at %p; to '%T' at %p>",
+ self, obj, obj);
}
else {
repr = PyUnicode_FromFormat(
- "<weakref at %p; to '%s' at %p (%U)>",
- self,
- Py_TYPE(PyWeakref_GET_OBJECT(self))->tp_name,
- obj,
- name);
+ "<weakref at %p; to '%T' at %p (%U)>",
+ self, obj, obj, name);
}
Py_DECREF(obj);
Py_XDECREF(name);
@@ -196,15 +237,18 @@ weakref_repr(PyWeakReference *self)
gone away, they are equal if they are identical. */
static PyObject *
-weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
+weakref_richcompare(PyObject* self, PyObject* other, int op)
{
if ((op != Py_EQ && op != Py_NE) ||
!PyWeakref_Check(self) ||
!PyWeakref_Check(other)) {
Py_RETURN_NOTIMPLEMENTED;
}
- if (PyWeakref_GET_OBJECT(self) == Py_None
- || PyWeakref_GET_OBJECT(other) == Py_None) {
+ PyObject* obj = _PyWeakref_GET_REF(self);
+ PyObject* other_obj = _PyWeakref_GET_REF(other);
+ if (obj == NULL || other_obj == NULL) {
+ Py_XDECREF(obj);
+ Py_XDECREF(other_obj);
int res = (self == other);
if (op == Py_NE)
res = !res;
@@ -213,10 +257,6 @@ weakref_richcompare(PyWeakReference* self, PyWeakReference* other, int op)
else
Py_RETURN_FALSE;
}
- PyObject* obj = PyWeakref_GET_OBJECT(self);
- PyObject* other_obj = PyWeakref_GET_OBJECT(other);
- Py_INCREF(obj);
- Py_INCREF(other_obj);
PyObject* res = PyObject_RichCompare(obj, other_obj, op);
Py_DECREF(obj);
Py_DECREF(other_obj);
@@ -278,6 +318,135 @@ insert_head(PyWeakReference *newref, PyWeakReference **list)
*list = newref;
}
+/* See if we can reuse either the basic ref or proxy in list instead of
+ * creating a new weakref
+ */
+static PyWeakReference *
+try_reuse_basic_ref(PyWeakReference *list, PyTypeObject *type,
+ PyObject *callback)
+{
+ if (callback != NULL) {
+ return NULL;
+ }
+
+ PyWeakReference *ref, *proxy;
+ get_basic_refs(list, &ref, &proxy);
+
+ PyWeakReference *cand = NULL;
+ if (type == &_PyWeakref_RefType) {
+ cand = ref;
+ }
+ if ((type == &_PyWeakref_ProxyType) ||
+ (type == &_PyWeakref_CallableProxyType)) {
+ cand = proxy;
+ }
+
+ if (cand != NULL && _Py_TryIncref((PyObject *) cand)) {
+ return cand;
+ }
+ return NULL;
+}
+
+static int
+is_basic_ref(PyWeakReference *ref)
+{
+ return (ref->wr_callback == NULL) && PyWeakref_CheckRefExact(ref);
+}
+
+static int
+is_basic_proxy(PyWeakReference *proxy)
+{
+ return (proxy->wr_callback == NULL) && PyWeakref_CheckProxy(proxy);
+}
+
+static int
+is_basic_ref_or_proxy(PyWeakReference *wr)
+{
+ return is_basic_ref(wr) || is_basic_proxy(wr);
+}
+
+/* Insert `newref` in the appropriate position in `list` */
+static void
+insert_weakref(PyWeakReference *newref, PyWeakReference **list)
+{
+ PyWeakReference *ref, *proxy;
+ get_basic_refs(*list, &ref, &proxy);
+
+ PyWeakReference *prev;
+ if (is_basic_ref(newref)) {
+ prev = NULL;
+ }
+ else if (is_basic_proxy(newref)) {
+ prev = ref;
+ }
+ else {
+ prev = (proxy == NULL) ? ref : proxy;
+ }
+
+ if (prev == NULL) {
+ insert_head(newref, list);
+ }
+ else {
+ insert_after(newref, prev);
+ }
+}
+
+static PyWeakReference *
+allocate_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback)
+{
+ PyWeakReference *newref = (PyWeakReference *) type->tp_alloc(type, 0);
+ if (newref == NULL) {
+ return NULL;
+ }
+ init_weakref(newref, obj, callback);
+ return newref;
+}
+
+static PyWeakReference *
+get_or_create_weakref(PyTypeObject *type, PyObject *obj, PyObject *callback)
+{
+ if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
+ PyErr_Format(PyExc_TypeError,
+ "cannot create weak reference to '%s' object",
+ Py_TYPE(obj)->tp_name);
+ return NULL;
+ }
+ if (callback == Py_None)
+ callback = NULL;
+
+ PyWeakReference **list = GET_WEAKREFS_LISTPTR(obj);
+ if ((type == &_PyWeakref_RefType) ||
+ (type == &_PyWeakref_ProxyType) ||
+ (type == &_PyWeakref_CallableProxyType))
+ {
+ LOCK_WEAKREFS(obj);
+ PyWeakReference *basic_ref = try_reuse_basic_ref(*list, type, callback);
+ if (basic_ref != NULL) {
+ UNLOCK_WEAKREFS(obj);
+ return basic_ref;
+ }
+ PyWeakReference *newref = allocate_weakref(type, obj, callback);
+ if (newref == NULL) {
+ UNLOCK_WEAKREFS(obj);
+ return NULL;
+ }
+ insert_weakref(newref, list);
+ UNLOCK_WEAKREFS(obj);
+ return newref;
+ }
+ else {
+ // We may not be able to safely allocate inside the lock
+ PyWeakReference *newref = allocate_weakref(type, obj, callback);
+ if (newref == NULL) {
+ return NULL;
+ }
+ LOCK_WEAKREFS(obj);
+ insert_weakref(newref, list);
+ UNLOCK_WEAKREFS(obj);
+ return newref;
+ }
+}
+
static int
parse_weakref_init_args(const char *funcname, PyObject *args, PyObject *kwargs,
PyObject **obp, PyObject **callbackp)
@@ -288,54 +457,11 @@ parse_weakref_init_args(const char *funcname, PyObject *args, PyObject *kwargs,
static PyObject *
weakref___new__(PyTypeObject *type, PyObject *args, PyObject *kwargs)
{
- PyWeakReference *self = NULL;
PyObject *ob, *callback = NULL;
-
if (parse_weakref_init_args("__new__", args, kwargs, &ob, &callback)) {
- PyWeakReference *ref, *proxy;
- PyWeakReference **list;
-
- if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
- PyErr_Format(PyExc_TypeError,
- "cannot create weak reference to '%s' object",
- Py_TYPE(ob)->tp_name);
- return NULL;
- }
- if (callback == Py_None)
- callback = NULL;
- list = GET_WEAKREFS_LISTPTR(ob);
- get_basic_refs(*list, &ref, &proxy);
- if (callback == NULL && type == &_PyWeakref_RefType) {
- if (ref != NULL) {
- /* We can re-use an existing reference. */
- return Py_NewRef(ref);
- }
- }
- /* We have to create a new reference. */
- /* Note: the tp_alloc() can trigger cyclic GC, so the weakref
- list on ob can be mutated. This means that the ref and
- proxy pointers we got back earlier may have been collected,
- so we need to compute these values again before we use
- them. */
- self = (PyWeakReference *) (type->tp_alloc(type, 0));
- if (self != NULL) {
- init_weakref(self, ob, callback);
- if (callback == NULL && type == &_PyWeakref_RefType) {
- insert_head(self, list);
- }
- else {
- PyWeakReference *prev;
-
- get_basic_refs(*list, &ref, &proxy);
- prev = (proxy == NULL) ? ref : proxy;
- if (prev == NULL)
- insert_head(self, list);
- else
- insert_after(self, prev);
- }
- }
+ return (PyObject *)get_or_create_weakref(type, ob, callback);
}
- return (PyObject *)self;
+ return NULL;
}
static int
@@ -354,7 +480,7 @@ weakref___init__(PyObject *self, PyObject *args, PyObject *kwargs)
static PyMemberDef weakref_members[] = {
- {"__callback__", T_OBJECT, offsetof(PyWeakReference, wr_callback), READONLY},
+ {"__callback__", _Py_T_OBJECT, offsetof(PyWeakReference, wr_callback), Py_READONLY},
{NULL} /* Sentinel */
};
@@ -372,13 +498,13 @@ _PyWeakref_RefType = {
.tp_dealloc = weakref_dealloc,
.tp_vectorcall_offset = offsetof(PyWeakReference, vectorcall),
.tp_call = PyVectorcall_Call,
- .tp_repr = (reprfunc)weakref_repr,
+ .tp_repr = weakref_repr,
.tp_hash = (hashfunc)weakref_hash,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_HAVE_VECTORCALL | Py_TPFLAGS_BASETYPE,
.tp_traverse = (traverseproc)gc_traverse,
.tp_clear = (inquiry)gc_clear,
- .tp_richcompare = (richcmpfunc)weakref_richcompare,
+ .tp_richcompare = weakref_richcompare,
.tp_methods = weakref_methods,
.tp_members = weakref_members,
.tp_init = weakref___init__,
@@ -388,15 +514,15 @@ _PyWeakref_RefType = {
};
-static int
-proxy_checkref(PyWeakReference *proxy)
+static bool
+proxy_check_ref(PyObject *obj)
{
- if (PyWeakref_GET_OBJECT(proxy) == Py_None) {
+ if (obj == NULL) {
PyErr_SetString(PyExc_ReferenceError,
"weakly-referenced object no longer exists");
- return 0;
+ return false;
}
- return 1;
+ return true;
}
@@ -406,16 +532,19 @@ proxy_checkref(PyWeakReference *proxy)
*/
#define UNWRAP(o) \
if (PyWeakref_CheckProxy(o)) { \
- if (!proxy_checkref((PyWeakReference *)o)) \
+ o = _PyWeakref_GET_REF(o); \
+ if (!proxy_check_ref(o)) { \
return NULL; \
- o = PyWeakref_GET_OBJECT(o); \
+ } \
+ } \
+ else { \
+ Py_INCREF(o); \
}
#define WRAP_UNARY(method, generic) \
static PyObject * \
method(PyObject *proxy) { \
UNWRAP(proxy); \
- Py_INCREF(proxy); \
PyObject* res = generic(proxy); \
Py_DECREF(proxy); \
return res; \
@@ -426,8 +555,6 @@ proxy_checkref(PyWeakReference *proxy)
method(PyObject *x, PyObject *y) { \
UNWRAP(x); \
UNWRAP(y); \
- Py_INCREF(x); \
- Py_INCREF(y); \
PyObject* res = generic(x, y); \
Py_DECREF(x); \
Py_DECREF(y); \
@@ -442,11 +569,9 @@ proxy_checkref(PyWeakReference *proxy)
method(PyObject *proxy, PyObject *v, PyObject *w) { \
UNWRAP(proxy); \
UNWRAP(v); \
- if (w != NULL) \
+ if (w != NULL) { \
UNWRAP(w); \
- Py_INCREF(proxy); \
- Py_INCREF(v); \
- Py_XINCREF(w); \
+ } \
PyObject* res = generic(proxy, v, w); \
Py_DECREF(proxy); \
Py_DECREF(v); \
@@ -458,7 +583,6 @@ proxy_checkref(PyWeakReference *proxy)
static PyObject * \
method(PyObject *proxy, PyObject *Py_UNUSED(ignored)) { \
UNWRAP(proxy); \
- Py_INCREF(proxy); \
PyObject* res = PyObject_CallMethodNoArgs(proxy, &_Py_ID(SPECIAL)); \
Py_DECREF(proxy); \
return res; \
@@ -472,23 +596,32 @@ WRAP_UNARY(proxy_str, PyObject_Str)
WRAP_TERNARY(proxy_call, PyObject_Call)
static PyObject *
-proxy_repr(PyWeakReference *proxy)
+proxy_repr(PyObject *proxy)
{
- return PyUnicode_FromFormat(
- "<weakproxy at %p to %s at %p>",
- proxy,
- Py_TYPE(PyWeakref_GET_OBJECT(proxy))->tp_name,
- PyWeakref_GET_OBJECT(proxy));
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ PyObject *repr;
+ if (obj != NULL) {
+ repr = PyUnicode_FromFormat(
+ "<weakproxy at %p; to '%T' at %p>",
+ proxy, obj, obj);
+ Py_DECREF(obj);
+ }
+ else {
+ repr = PyUnicode_FromFormat(
+ "<weakproxy at %p; dead>",
+ proxy);
+ }
+ return repr;
}
static int
-proxy_setattr(PyWeakReference *proxy, PyObject *name, PyObject *value)
+proxy_setattr(PyObject *proxy, PyObject *name, PyObject *value)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return -1;
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
int res = PyObject_SetAttr(obj, name, value);
Py_DECREF(obj);
return res;
@@ -499,7 +632,10 @@ proxy_richcompare(PyObject *proxy, PyObject *v, int op)
{
UNWRAP(proxy);
UNWRAP(v);
- return PyObject_RichCompare(proxy, v, op);
+ PyObject* res = PyObject_RichCompare(proxy, v, op);
+ Py_DECREF(proxy);
+ Py_DECREF(v);
+ return res;
}
/* number slots */
@@ -539,13 +675,12 @@ WRAP_BINARY(proxy_matmul, PyNumber_MatrixMultiply)
WRAP_BINARY(proxy_imatmul, PyNumber_InPlaceMatrixMultiply)
static int
-proxy_bool(PyWeakReference *proxy)
+proxy_bool(PyObject *proxy)
{
- PyObject *o = PyWeakref_GET_OBJECT(proxy);
- if (!proxy_checkref(proxy)) {
+ PyObject *o = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(o)) {
return -1;
}
- Py_INCREF(o);
int res = PyObject_IsTrue(o);
Py_DECREF(o);
return res;
@@ -555,8 +690,6 @@ static void
proxy_dealloc(PyWeakReference *self)
{
PyObject_GC_UnTrack(self);
- if (self->wr_callback != NULL)
- PyObject_GC_UnTrack((PyObject *)self);
clear_weakref(self);
PyObject_GC_Del(self);
}
@@ -564,13 +697,12 @@ proxy_dealloc(PyWeakReference *self)
/* sequence slots */
static int
-proxy_contains(PyWeakReference *proxy, PyObject *value)
+proxy_contains(PyObject *proxy, PyObject *value)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return -1;
-
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
int res = PySequence_Contains(obj, value);
Py_DECREF(obj);
return res;
@@ -579,13 +711,12 @@ proxy_contains(PyWeakReference *proxy, PyObject *value)
/* mapping slots */
static Py_ssize_t
-proxy_length(PyWeakReference *proxy)
+proxy_length(PyObject *proxy)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return -1;
-
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
Py_ssize_t res = PyObject_Length(obj);
Py_DECREF(obj);
return res;
@@ -594,13 +725,12 @@ proxy_length(PyWeakReference *proxy)
WRAP_BINARY(proxy_getitem, PyObject_GetItem)
static int
-proxy_setitem(PyWeakReference *proxy, PyObject *key, PyObject *value)
+proxy_setitem(PyObject *proxy, PyObject *key, PyObject *value)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return -1;
-
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
int res;
if (value == NULL) {
res = PyObject_DelItem(obj, key);
@@ -614,31 +744,31 @@ proxy_setitem(PyWeakReference *proxy, PyObject *key, PyObject *value)
/* iterator slots */
static PyObject *
-proxy_iter(PyWeakReference *proxy)
+proxy_iter(PyObject *proxy)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return NULL;
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
- Py_INCREF(obj);
+ }
PyObject* res = PyObject_GetIter(obj);
Py_DECREF(obj);
return res;
}
static PyObject *
-proxy_iternext(PyWeakReference *proxy)
+proxy_iternext(PyObject *proxy)
{
- if (!proxy_checkref(proxy))
+ PyObject *obj = _PyWeakref_GET_REF(proxy);
+ if (!proxy_check_ref(obj)) {
return NULL;
-
- PyObject *obj = PyWeakref_GET_OBJECT(proxy);
+ }
if (!PyIter_Check(obj)) {
PyErr_Format(PyExc_TypeError,
"Weakref proxy referenced a non-iterator '%.200s' object",
Py_TYPE(obj)->tp_name);
+ Py_DECREF(obj);
return NULL;
}
- Py_INCREF(obj);
PyObject* res = PyIter_Next(obj);
Py_DECREF(obj);
return res;
@@ -666,7 +796,7 @@ static PyNumberMethods proxy_as_number = {
proxy_neg, /*nb_negative*/
proxy_pos, /*nb_positive*/
proxy_abs, /*nb_absolute*/
- (inquiry)proxy_bool, /*nb_bool*/
+ proxy_bool, /*nb_bool*/
proxy_invert, /*nb_invert*/
proxy_lshift, /*nb_lshift*/
proxy_rshift, /*nb_rshift*/
@@ -696,20 +826,20 @@ static PyNumberMethods proxy_as_number = {
};
static PySequenceMethods proxy_as_sequence = {
- (lenfunc)proxy_length, /*sq_length*/
+ proxy_length, /*sq_length*/
0, /*sq_concat*/
0, /*sq_repeat*/
0, /*sq_item*/
0, /*sq_slice*/
0, /*sq_ass_item*/
- 0, /*sq_ass_slice*/
- (objobjproc)proxy_contains, /* sq_contains */
+ 0, /*sq_ass_slice*/
+ proxy_contains, /* sq_contains */
};
static PyMappingMethods proxy_as_mapping = {
- (lenfunc)proxy_length, /*mp_length*/
+ proxy_length, /*mp_length*/
proxy_getitem, /*mp_subscript*/
- (objobjargproc)proxy_setitem, /*mp_ass_subscript*/
+ proxy_setitem, /*mp_ass_subscript*/
};
@@ -725,7 +855,7 @@ _PyWeakref_ProxyType = {
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (reprfunc)proxy_repr, /* tp_repr */
+ proxy_repr, /* tp_repr */
&proxy_as_number, /* tp_as_number */
&proxy_as_sequence, /* tp_as_sequence */
&proxy_as_mapping, /* tp_as_mapping */
@@ -734,7 +864,7 @@ _PyWeakref_ProxyType = {
0, /* tp_call */
proxy_str, /* tp_str */
proxy_getattr, /* tp_getattro */
- (setattrofunc)proxy_setattr, /* tp_setattro */
+ proxy_setattr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
@@ -742,9 +872,9 @@ _PyWeakref_ProxyType = {
(inquiry)gc_clear, /* tp_clear */
proxy_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)proxy_iter, /* tp_iter */
- (iternextfunc)proxy_iternext, /* tp_iternext */
- proxy_methods, /* tp_methods */
+ proxy_iter, /* tp_iter */
+ proxy_iternext, /* tp_iternext */
+ proxy_methods, /* tp_methods */
};
@@ -760,7 +890,7 @@ _PyWeakref_CallableProxyType = {
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_as_async */
- (unaryfunc)proxy_repr, /* tp_repr */
+ proxy_repr, /* tp_repr */
&proxy_as_number, /* tp_as_number */
&proxy_as_sequence, /* tp_as_sequence */
&proxy_as_mapping, /* tp_as_mapping */
@@ -768,7 +898,7 @@ _PyWeakref_CallableProxyType = {
proxy_call, /* tp_call */
proxy_str, /* tp_str */
proxy_getattr, /* tp_getattro */
- (setattrofunc)proxy_setattr, /* tp_setattro */
+ proxy_setattr, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */
0, /* tp_doc */
@@ -776,131 +906,43 @@ _PyWeakref_CallableProxyType = {
(inquiry)gc_clear, /* tp_clear */
proxy_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset */
- (getiterfunc)proxy_iter, /* tp_iter */
- (iternextfunc)proxy_iternext, /* tp_iternext */
+ proxy_iter, /* tp_iter */
+ proxy_iternext, /* tp_iternext */
};
-
-
PyObject *
PyWeakref_NewRef(PyObject *ob, PyObject *callback)
{
- PyWeakReference *result = NULL;
- PyWeakReference **list;
- PyWeakReference *ref, *proxy;
-
- if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
- PyErr_Format(PyExc_TypeError,
- "cannot create weak reference to '%s' object",
- Py_TYPE(ob)->tp_name);
- return NULL;
- }
- list = GET_WEAKREFS_LISTPTR(ob);
- get_basic_refs(*list, &ref, &proxy);
- if (callback == Py_None)
- callback = NULL;
- if (callback == NULL)
- /* return existing weak reference if it exists */
- result = ref;
- if (result != NULL)
- Py_INCREF(result);
- else {
- /* Note: new_weakref() can trigger cyclic GC, so the weakref
- list on ob can be mutated. This means that the ref and
- proxy pointers we got back earlier may have been collected,
- so we need to compute these values again before we use
- them. */
- result = new_weakref(ob, callback);
- if (result != NULL) {
- get_basic_refs(*list, &ref, &proxy);
- if (callback == NULL) {
- if (ref == NULL)
- insert_head(result, list);
- else {
- /* Someone else added a ref without a callback
- during GC. Return that one instead of this one
- to avoid violating the invariants of the list
- of weakrefs for ob. */
- Py_SETREF(result, (PyWeakReference*)Py_NewRef(ref));
- }
- }
- else {
- PyWeakReference *prev;
-
- prev = (proxy == NULL) ? ref : proxy;
- if (prev == NULL)
- insert_head(result, list);
- else
- insert_after(result, prev);
- }
- }
- }
- return (PyObject *) result;
+ return (PyObject *)get_or_create_weakref(&_PyWeakref_RefType, ob,
+ callback);
}
-
PyObject *
PyWeakref_NewProxy(PyObject *ob, PyObject *callback)
{
- PyWeakReference *result = NULL;
- PyWeakReference **list;
- PyWeakReference *ref, *proxy;
-
- if (!_PyType_SUPPORTS_WEAKREFS(Py_TYPE(ob))) {
- PyErr_Format(PyExc_TypeError,
- "cannot create weak reference to '%s' object",
- Py_TYPE(ob)->tp_name);
- return NULL;
+ PyTypeObject *type = &_PyWeakref_ProxyType;
+ if (PyCallable_Check(ob)) {
+ type = &_PyWeakref_CallableProxyType;
}
- list = GET_WEAKREFS_LISTPTR(ob);
- get_basic_refs(*list, &ref, &proxy);
- if (callback == Py_None)
- callback = NULL;
- if (callback == NULL)
- /* attempt to return an existing weak reference if it exists */
- result = proxy;
- if (result != NULL)
- Py_INCREF(result);
- else {
- /* Note: new_weakref() can trigger cyclic GC, so the weakref
- list on ob can be mutated. This means that the ref and
- proxy pointers we got back earlier may have been collected,
- so we need to compute these values again before we use
- them. */
- result = new_weakref(ob, callback);
- if (result != NULL) {
- PyWeakReference *prev;
+ return (PyObject *)get_or_create_weakref(type, ob, callback);
+}
- if (PyCallable_Check(ob)) {
- Py_SET_TYPE(result, &_PyWeakref_CallableProxyType);
- }
- else {
- Py_SET_TYPE(result, &_PyWeakref_ProxyType);
- }
- get_basic_refs(*list, &ref, &proxy);
- if (callback == NULL) {
- if (proxy != NULL) {
- /* Someone else added a proxy without a callback
- during GC. Return that one instead of this one
- to avoid violating the invariants of the list
- of weakrefs for ob. */
- Py_SETREF(result, (PyWeakReference*)Py_NewRef(proxy));
- goto skip_insert;
- }
- prev = ref;
- }
- else
- prev = (proxy == NULL) ? ref : proxy;
- if (prev == NULL)
- insert_head(result, list);
- else
- insert_after(result, prev);
- skip_insert:
- ;
- }
+int
+PyWeakref_GetRef(PyObject *ref, PyObject **pobj)
+{
+ if (ref == NULL) {
+ *pobj = NULL;
+ PyErr_BadInternalCall();
+ return -1;
}
- return (PyObject *) result;
+ if (!PyWeakref_Check(ref)) {
+ *pobj = NULL;
+ PyErr_SetString(PyExc_TypeError, "expected a weakref");
+ return -1;
+ }
+ *pobj = _PyWeakref_GET_REF(ref);
+ return (*pobj != NULL);
}
@@ -911,7 +953,12 @@ PyWeakref_GetObject(PyObject *ref)
PyErr_BadInternalCall();
return NULL;
}
- return PyWeakref_GET_OBJECT(ref);
+ PyObject *obj = _PyWeakref_GET_REF(ref);
+ if (obj == NULL) {
+ return Py_None;
+ }
+ Py_DECREF(obj);
+ return obj; // borrowed reference
}
/* Note that there's an inlined copy-paste of handle_callback() in gcmodule.c's
@@ -946,67 +993,82 @@ PyObject_ClearWeakRefs(PyObject *object)
PyErr_BadInternalCall();
return;
}
+
list = GET_WEAKREFS_LISTPTR(object);
- /* Remove the callback-less basic and proxy references */
- if (*list != NULL && (*list)->wr_callback == NULL) {
- clear_weakref(*list);
- if (*list != NULL && (*list)->wr_callback == NULL)
- clear_weakref(*list);
+ if (FT_ATOMIC_LOAD_PTR(*list) == NULL) {
+ // Fast path for the common case
+ return;
}
- if (*list != NULL) {
- PyWeakReference *current = *list;
- Py_ssize_t count = _PyWeakref_GetWeakrefCount(current);
- PyObject *exc = PyErr_GetRaisedException();
-
- if (count == 1) {
- PyObject *callback = current->wr_callback;
- current->wr_callback = NULL;
- clear_weakref(current);
- if (callback != NULL) {
- if (Py_REFCNT((PyObject *)current) > 0) {
- handle_callback(current, callback);
- }
- Py_DECREF(callback);
- }
+ /* Remove the callback-less basic and proxy references, which always appear
+ at the head of the list.
+ */
+ for (int done = 0; !done;) {
+ LOCK_WEAKREFS(object);
+ if (*list != NULL && is_basic_ref_or_proxy(*list)) {
+ PyObject *callback;
+ clear_weakref_lock_held(*list, &callback);
+ assert(callback == NULL);
}
- else {
- PyObject *tuple;
- Py_ssize_t i = 0;
+ done = (*list == NULL) || !is_basic_ref_or_proxy(*list);
+ UNLOCK_WEAKREFS(object);
+ }
- tuple = PyTuple_New(count * 2);
- if (tuple == NULL) {
- _PyErr_ChainExceptions1(exc);
- return;
- }
+ /* Deal with non-canonical (subtypes or refs with callbacks) references. */
+ Py_ssize_t num_weakrefs = _PyWeakref_GetWeakrefCount(object);
+ if (num_weakrefs == 0) {
+ return;
+ }
- for (i = 0; i < count; ++i) {
- PyWeakReference *next = current->wr_next;
+ PyObject *exc = PyErr_GetRaisedException();
+ PyObject *tuple = PyTuple_New(num_weakrefs * 2);
+ if (tuple == NULL) {
+ _PyWeakref_ClearWeakRefsNoCallbacks(object);
+ PyErr_WriteUnraisable(NULL);
+ PyErr_SetRaisedException(exc);
+ return;
+ }
- if (Py_REFCNT((PyObject *)current) > 0) {
- PyTuple_SET_ITEM(tuple, i * 2, Py_NewRef(current));
- PyTuple_SET_ITEM(tuple, i * 2 + 1, current->wr_callback);
- }
- else {
- Py_DECREF(current->wr_callback);
- }
- current->wr_callback = NULL;
- clear_weakref(current);
- current = next;
+ Py_ssize_t num_items = 0;
+ for (int done = 0; !done;) {
+ PyObject *callback = NULL;
+ LOCK_WEAKREFS(object);
+ PyWeakReference *cur = *list;
+ if (cur != NULL) {
+ clear_weakref_lock_held(cur, &callback);
+ if (_Py_TryIncref((PyObject *) cur)) {
+ assert(num_items / 2 < num_weakrefs);
+ PyTuple_SET_ITEM(tuple, num_items, (PyObject *) cur);
+ PyTuple_SET_ITEM(tuple, num_items + 1, callback);
+ num_items += 2;
+ callback = NULL;
}
- for (i = 0; i < count; ++i) {
- PyObject *callback = PyTuple_GET_ITEM(tuple, i * 2 + 1);
+ }
+ done = (*list == NULL);
+ UNLOCK_WEAKREFS(object);
- /* The tuple may have slots left to NULL */
- if (callback != NULL) {
- PyObject *item = PyTuple_GET_ITEM(tuple, i * 2);
- handle_callback((PyWeakReference *)item, callback);
- }
- }
- Py_DECREF(tuple);
+ Py_XDECREF(callback);
+ }
+
+ for (Py_ssize_t i = 0; i < num_items; i += 2) {
+ PyObject *callback = PyTuple_GET_ITEM(tuple, i + 1);
+ if (callback != NULL) {
+ PyObject *weakref = PyTuple_GET_ITEM(tuple, i);
+ handle_callback((PyWeakReference *)weakref, callback);
}
- assert(!PyErr_Occurred());
- PyErr_SetRaisedException(exc);
+ }
+
+ Py_DECREF(tuple);
+
+ assert(!PyErr_Occurred());
+ PyErr_SetRaisedException(exc);
+}
+
+void
+PyUnstable_Object_ClearWeakRefsNoCallbacks(PyObject *obj)
+{
+ if (_PyType_SUPPORTS_WEAKREFS(Py_TYPE(obj))) {
+ _PyWeakref_ClearWeakRefsNoCallbacks(obj);
}
}
@@ -1019,12 +1081,32 @@ PyObject_ClearWeakRefs(PyObject *object)
void
_PyStaticType_ClearWeakRefs(PyInterpreterState *interp, PyTypeObject *type)
{
- static_builtin_state *state = _PyStaticType_GetState(interp, type);
+ managed_static_type_state *state = _PyStaticType_GetState(interp, type);
PyObject **list = _PyStaticType_GET_WEAKREFS_LISTPTR(state);
- while (*list != NULL) {
- /* Note that clear_weakref() pops the first ref off the type's
- weaklist before clearing its wr_object and wr_callback.
- That is how we're able to loop over the list. */
- clear_weakref((PyWeakReference *)*list);
+ // This is safe to do without holding the lock in free-threaded builds;
+ // there is only one thread running and no new threads can be created.
+ while (*list) {
+ _PyWeakref_ClearRef((PyWeakReference *)*list);
}
}
+
+void
+_PyWeakref_ClearWeakRefsNoCallbacks(PyObject *obj)
+{
+ /* Modeled after GET_WEAKREFS_LISTPTR().
+
+ This is never triggered for static types so we can avoid the
+ (slightly) more costly _PyObject_GET_WEAKREFS_LISTPTR(). */
+ PyWeakReference **list = _PyObject_GET_WEAKREFS_LISTPTR_FROM_OFFSET(obj);
+ LOCK_WEAKREFS(obj);
+ while (*list) {
+ _PyWeakref_ClearRef(*list);
+ }
+ UNLOCK_WEAKREFS(obj);
+}
+
+int
+_PyWeakref_IsDead(PyObject *weakref)
+{
+ return _PyWeakref_IS_DEAD(weakref);
+}