diff options
author | robot-piglet <[email protected]> | 2025-07-09 10:22:03 +0300 |
---|---|---|
committer | robot-piglet <[email protected]> | 2025-07-09 10:41:10 +0300 |
commit | f91edcd669b2b4e97378bae7b57f6589e68a538f (patch) | |
tree | c76a2875faa0ec6ab7d4cfe245ba138114447041 /contrib/python | |
parent | f979375c0346ff1acee7005d4c0d22d954d4dfa3 (diff) |
Intermediate changes
commit_hash:d8fa10d7fba592f861e1470374c72cf22f0d5a2b
Diffstat (limited to 'contrib/python')
-rw-r--r-- | contrib/python/multidict/.dist-info/METADATA | 2 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/__init__.py | 2 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multidict.c | 1121 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multidict_py.py | 659 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/dict.h | 17 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/hashtable.h | 1890 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/htkeys.h | 414 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/istr.h | 23 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/iter.h | 47 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/pair_list.h | 1633 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/parser.h | 48 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h | 747 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/state.h | 17 | ||||
-rw-r--r-- | contrib/python/multidict/multidict/_multilib/views.h | 485 | ||||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-c.pickle.0 (renamed from contrib/python/multidict/tests/cimultidict-c-extension.pickle.0) | 0 | ||||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-c.pickle.1 (renamed from contrib/python/multidict/tests/cimultidict-c-extension.pickle.1) | bin | 71 -> 71 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-c.pickle.2 (renamed from contrib/python/multidict/tests/cimultidict-c-extension.pickle.2) | bin | 70 -> 70 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-c.pickle.3 (renamed from contrib/python/multidict/tests/cimultidict-c-extension.pickle.3) | bin | 70 -> 70 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-c.pickle.4 (renamed from contrib/python/multidict/tests/cimultidict-c-extension.pickle.4) | bin | 73 -> 73 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-c.pickle.5 (renamed from contrib/python/multidict/tests/cimultidict-c-extension.pickle.5) | bin | 73 -> 73 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-py.pickle.0 (renamed from contrib/python/multidict/tests/cimultidict-pure-python.pickle.0) | 0 | ||||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-py.pickle.1 (renamed from contrib/python/multidict/tests/cimultidict-pure-python.pickle.1) | bin | 74 -> 74 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-py.pickle.2 (renamed from contrib/python/multidict/tests/cimultidict-pure-python.pickle.2) | bin | 73 -> 73 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-py.pickle.3 (renamed from contrib/python/multidict/tests/cimultidict-pure-python.pickle.3) | bin | 73 -> 73 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-py.pickle.4 (renamed from contrib/python/multidict/tests/cimultidict-pure-python.pickle.4) | bin | 76 -> 76 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/cimultidict-py.pickle.5 (renamed from contrib/python/multidict/tests/cimultidict-pure-python.pickle.5) | bin | 76 -> 76 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/conftest.py | 6 | ||||
-rw-r--r-- | contrib/python/multidict/tests/istr-c.pickle.0 (renamed from contrib/python/multidict/tests/istr-c-extension.pickle.0) | 0 | ||||
-rw-r--r-- | contrib/python/multidict/tests/istr-c.pickle.1 (renamed from contrib/python/multidict/tests/istr-c-extension.pickle.1) | bin | 47 -> 47 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-c.pickle.2 (renamed from contrib/python/multidict/tests/istr-c-extension.pickle.2) | bin | 48 -> 48 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-c.pickle.3 (renamed from contrib/python/multidict/tests/istr-c-extension.pickle.3) | bin | 48 -> 48 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-c.pickle.4 (renamed from contrib/python/multidict/tests/istr-c-extension.pickle.4) | bin | 54 -> 54 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-c.pickle.5 (renamed from contrib/python/multidict/tests/istr-c-extension.pickle.5) | bin | 54 -> 54 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-py.pickle.0 (renamed from contrib/python/multidict/tests/istr-pure-python.pickle.0) | 0 | ||||
-rw-r--r-- | contrib/python/multidict/tests/istr-py.pickle.1 (renamed from contrib/python/multidict/tests/istr-pure-python.pickle.1) | bin | 100 -> 100 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-py.pickle.2 (renamed from contrib/python/multidict/tests/istr-pure-python.pickle.2) | bin | 51 -> 51 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-py.pickle.3 (renamed from contrib/python/multidict/tests/istr-pure-python.pickle.3) | bin | 51 -> 51 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-py.pickle.4 (renamed from contrib/python/multidict/tests/istr-pure-python.pickle.4) | bin | 57 -> 57 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/istr-py.pickle.5 (renamed from contrib/python/multidict/tests/istr-pure-python.pickle.5) | bin | 57 -> 57 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-c.pickle.0 (renamed from contrib/python/multidict/tests/multidict-c-extension.pickle.0) | 0 | ||||
-rw-r--r-- | contrib/python/multidict/tests/multidict-c.pickle.1 (renamed from contrib/python/multidict/tests/multidict-c-extension.pickle.1) | bin | 69 -> 69 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-c.pickle.2 (renamed from contrib/python/multidict/tests/multidict-c-extension.pickle.2) | bin | 68 -> 68 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-c.pickle.3 (renamed from contrib/python/multidict/tests/multidict-c-extension.pickle.3) | bin | 68 -> 68 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-c.pickle.4 (renamed from contrib/python/multidict/tests/multidict-c-extension.pickle.4) | bin | 71 -> 71 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-c.pickle.5 (renamed from contrib/python/multidict/tests/multidict-c-extension.pickle.5) | bin | 71 -> 71 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-py.pickle.0 (renamed from contrib/python/multidict/tests/multidict-pure-python.pickle.0) | 0 | ||||
-rw-r--r-- | contrib/python/multidict/tests/multidict-py.pickle.1 (renamed from contrib/python/multidict/tests/multidict-pure-python.pickle.1) | bin | 72 -> 72 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-py.pickle.2 (renamed from contrib/python/multidict/tests/multidict-pure-python.pickle.2) | bin | 71 -> 71 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-py.pickle.3 (renamed from contrib/python/multidict/tests/multidict-pure-python.pickle.3) | bin | 71 -> 71 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-py.pickle.4 (renamed from contrib/python/multidict/tests/multidict-pure-python.pickle.4) | bin | 74 -> 74 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/multidict-py.pickle.5 (renamed from contrib/python/multidict/tests/multidict-pure-python.pickle.5) | bin | 74 -> 74 bytes | |||
-rw-r--r-- | contrib/python/multidict/tests/test_circular_imports.py | 12 | ||||
-rw-r--r-- | contrib/python/multidict/tests/test_multidict.py | 24 | ||||
-rw-r--r-- | contrib/python/multidict/tests/test_multidict_benchmarks.py | 12 | ||||
-rw-r--r-- | contrib/python/multidict/tests/test_mutable_multidict.py | 91 | ||||
-rw-r--r-- | contrib/python/multidict/tests/test_types.py | 3 | ||||
-rw-r--r-- | contrib/python/multidict/tests/test_update.py | 29 | ||||
-rw-r--r-- | contrib/python/multidict/ya.make | 2 |
58 files changed, 4213 insertions, 3071 deletions
diff --git a/contrib/python/multidict/.dist-info/METADATA b/contrib/python/multidict/.dist-info/METADATA index a6ee0d00f6a..4c018b118d2 100644 --- a/contrib/python/multidict/.dist-info/METADATA +++ b/contrib/python/multidict/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: multidict -Version: 6.4.4 +Version: 6.5.1 Summary: multidict implementation Home-page: https://github.com/aio-libs/multidict Author: Andrew Svetlov diff --git a/contrib/python/multidict/multidict/__init__.py b/contrib/python/multidict/multidict/__init__.py index 28d72cb79c1..1d8a2ae54c5 100644 --- a/contrib/python/multidict/multidict/__init__.py +++ b/contrib/python/multidict/multidict/__init__.py @@ -22,7 +22,7 @@ __all__ = ( "getversion", ) -__version__ = "6.4.4" +__version__ = "6.5.1" if TYPE_CHECKING or not USE_EXTENSIONS: diff --git a/contrib/python/multidict/multidict/_multidict.c b/contrib/python/multidict/multidict/_multidict.c index f31a7881000..cd958931153 100644 --- a/contrib/python/multidict/multidict/_multidict.c +++ b/contrib/python/multidict/multidict/_multidict.c @@ -1,42 +1,42 @@ -#include "Python.h" -#include "structmember.h" - -#include "_multilib/pythoncapi_compat.h" +#include <Python.h> +#include <structmember.h> #include "_multilib/dict.h" +#include "_multilib/hashtable.h" #include "_multilib/istr.h" #include "_multilib/iter.h" -#include "_multilib/pair_list.h" #include "_multilib/parser.h" +#include "_multilib/pythoncapi_compat.h" #include "_multilib/state.h" #include "_multilib/views.h" - #define MultiDict_CheckExact(state, obj) Py_IS_TYPE(obj, state->MultiDictType) -#define MultiDict_Check(state, obj) \ - (MultiDict_CheckExact(state, obj) \ - || PyObject_TypeCheck(obj, state->MultiDictType)) -#define CIMultiDict_CheckExact(state, obj) Py_IS_TYPE(obj, state->CIMultiDictType) -#define CIMultiDict_Check(state, obj) \ - (CIMultiDict_CheckExact(state, obj) \ - || PyObject_TypeCheck(obj, state->CIMultiDictType)) -#define AnyMultiDict_Check(state, obj) \ - (MultiDict_CheckExact(state, obj) \ - || CIMultiDict_CheckExact(state, obj) \ - || PyObject_TypeCheck(obj, state->MultiDictType)) -#define MultiDictProxy_CheckExact(state, obj) Py_IS_TYPE(obj, state->MultiDictProxyType) -#define MultiDictProxy_Check(state, obj) \ - (MultiDictProxy_CheckExact(state, obj) \ - || PyObject_TypeCheck(obj, state->MultiDictProxyType)) +#define MultiDict_Check(state, obj) \ + (MultiDict_CheckExact(state, obj) || \ + PyObject_TypeCheck(obj, state->MultiDictType)) +#define CIMultiDict_CheckExact(state, obj) \ + Py_IS_TYPE(obj, state->CIMultiDictType) +#define CIMultiDict_Check(state, obj) \ + (CIMultiDict_CheckExact(state, obj) || \ + PyObject_TypeCheck(obj, state->CIMultiDictType)) +#define AnyMultiDict_Check(state, obj) \ + (MultiDict_CheckExact(state, obj) || \ + CIMultiDict_CheckExact(state, obj) || \ + PyObject_TypeCheck(obj, state->MultiDictType)) +#define MultiDictProxy_CheckExact(state, obj) \ + Py_IS_TYPE(obj, state->MultiDictProxyType) +#define MultiDictProxy_Check(state, obj) \ + (MultiDictProxy_CheckExact(state, obj) || \ + PyObject_TypeCheck(obj, state->MultiDictProxyType)) #define CIMultiDictProxy_CheckExact(state, obj) \ Py_IS_TYPE(obj, state->CIMultiDictProxyType) -#define CIMultiDictProxy_Check(state, obj) \ - (CIMultiDictProxy_CheckExact(state, obj) \ - || PyObject_TypeCheck(obj, state->CIMultiDictProxyType)) -#define AnyMultiDictProxy_Check(state, obj) \ - (MultiDictProxy_CheckExact(state, obj) \ - || CIMultiDictProxy_CheckExact(state, obj) \ - || PyObject_TypeCheck(obj, state->MultiDictProxyType)) +#define CIMultiDictProxy_Check(state, obj) \ + (CIMultiDictProxy_CheckExact(state, obj) || \ + PyObject_TypeCheck(obj, state->CIMultiDictProxyType)) +#define AnyMultiDictProxy_Check(state, obj) \ + (MultiDictProxy_CheckExact(state, obj) || \ + CIMultiDictProxy_CheckExact(state, obj) || \ + PyObject_TypeCheck(obj, state->MultiDictProxyType)) /******************** Internal Methods ********************/ @@ -45,10 +45,12 @@ _multidict_getone(MultiDictObject *self, PyObject *key, PyObject *_default) { PyObject *val = NULL; - if (pair_list_get_one(&self->pairs, key, &val) <0) { + if (md_get_one(self, key, &val) < 0) { return NULL; } + ASSERT_CONSISTENT(self, false); + if (val == NULL) { if (_default != NULL) { Py_INCREF(_default); @@ -62,22 +64,12 @@ _multidict_getone(MultiDictObject *self, PyObject *key, PyObject *_default) } } - static inline int -_multidict_extend(MultiDictObject *self, PyObject *arg, - PyObject *kwds, const char *name, int do_add) +_multidict_extend(MultiDictObject *self, PyObject *arg, PyObject *kwds, + const char *name, bool update) { - mod_state *state = self->pairs.state; - PyObject *used = NULL; - PyObject *seq = NULL; - pair_list_t *list; - - if (!do_add) { - used = PyDict_New(); - if (used == NULL) { - goto fail; - } - } + mod_state *state = self->state; + PyObject *seq = NULL; if (kwds && !PyArg_ValidateKeywordArguments(kwds)) { goto fail; @@ -85,17 +77,25 @@ _multidict_extend(MultiDictObject *self, PyObject *arg, if (arg != NULL) { if (AnyMultiDict_Check(state, arg)) { - list = &((MultiDictObject*)arg)->pairs; - if (pair_list_update_from_pair_list(&self->pairs, used, list) < 0) { + MultiDictObject *other = (MultiDictObject *)arg; + if (md_update_from_ht(self, other, update) < 0) { goto fail; } } else if (AnyMultiDictProxy_Check(state, arg)) { - list = &((MultiDictProxyObject*)arg)->md->pairs; - if (pair_list_update_from_pair_list(&self->pairs, used, list) < 0) { + MultiDictObject *other = ((MultiDictProxyObject *)arg)->md; + if (md_update_from_ht(self, other, update) < 0) { goto fail; } } else if (PyDict_CheckExact(arg)) { - if (pair_list_update_from_dict(&self->pairs, used, arg) < 0) { + if (md_update_from_dict(self, arg, update) < 0) { + goto fail; + } + } else if (PyList_CheckExact(arg)) { + if (md_update_from_seq(self, arg, update) < 0) { + goto fail; + } + } else if (PyTuple_CheckExact(arg)) { + if (md_update_from_seq(self, arg, update) < 0) { goto fail; } } else { @@ -105,67 +105,84 @@ _multidict_extend(MultiDictObject *self, PyObject *arg, seq = Py_NewRef(arg); } - if (pair_list_update_from_seq(&self->pairs, used, seq) < 0) { + if (md_update_from_seq(self, seq, update) < 0) { goto fail; } } } if (kwds != NULL) { - if (pair_list_update_from_dict(&self->pairs, used, kwds) < 0) { + if (md_update_from_dict(self, kwds, update) < 0) { goto fail; } } - if (!do_add) { - if (pair_list_post_update(&self->pairs, used) < 0) { + if (update) { + if (md_post_update(self) < 0) { goto fail; } } + + ASSERT_CONSISTENT(self, false); Py_CLEAR(seq); - Py_CLEAR(used); return 0; fail: Py_CLEAR(seq); - Py_CLEAR(used); return -1; } - static inline Py_ssize_t -_multidict_extend_parse_args(PyObject *args, PyObject *kwds, +_multidict_extend_parse_args(mod_state *state, PyObject *args, PyObject *kwds, const char *name, PyObject **parg) { Py_ssize_t size = 0; Py_ssize_t s; if (args) { - size = PyTuple_GET_SIZE(args); - if (size > 1) { + s = PyTuple_GET_SIZE(args); + if (s > 1) { PyErr_Format( - PyExc_TypeError, - "%s takes from 1 to 2 positional arguments but %zd were given", - name, size + 1, NULL - ); + PyExc_TypeError, + "%s takes from 1 to 2 positional arguments but %zd were given", + name, + s + 1, + NULL); *parg = NULL; return -1; } } - if (size == 1) { + if (s == 1) { *parg = Py_NewRef(PyTuple_GET_ITEM(args, 0)); - s = PyObject_Length(*parg); - if (s < 0) { - // e.g. cannot calc size of generator object - PyErr_Clear(); + if (PyTuple_CheckExact(*parg)) { + size += PyTuple_GET_SIZE(*parg); + } else if (PyList_CheckExact(*parg)) { + size += PyList_GET_SIZE(*parg); + } else if (PyDict_CheckExact(*parg)) { + size += PyDict_GET_SIZE(*parg); + } else if (MultiDict_CheckExact(state, *parg) || + CIMultiDict_CheckExact(state, *parg)) { + MultiDictObject *md = (MultiDictObject *)*parg; + size += md_len(md); + } else if (MultiDictProxy_CheckExact(state, *parg) || + CIMultiDictProxy_CheckExact(state, *parg)) { + MultiDictObject *md = ((MultiDictProxyObject *)*parg)->md; + size += md_len(md); } else { - size += s; + s = PyObject_LengthHint(*parg, 0); + if (s < 0) { + // e.g. cannot calc size of generator object + PyErr_Clear(); + } else { + size += s; + } } } else { *parg = NULL; } if (kwds != NULL) { - s = PyDict_Size(kwds); + assert((PyDict_CheckExact(kwds))); + s = PyDict_GET_SIZE(kwds); if (s < 0) { return -1; } @@ -175,71 +192,81 @@ _multidict_extend_parse_args(PyObject *args, PyObject *kwds, return size; } -static inline PyObject * -multidict_copy(MultiDictObject *self) +static inline int +_multidict_clone_fast(mod_state *state, MultiDictObject *self, bool is_ci, + PyObject *arg, PyObject *kwds) { - MultiDictObject *new_multidict = NULL; - - new_multidict = (MultiDictObject*)PyType_GenericNew( - Py_TYPE(self), NULL, NULL); - if (new_multidict == NULL) { - goto fail; + int ret = 0; + if (arg != NULL && kwds == NULL) { + MultiDictObject *other = NULL; + if (AnyMultiDict_Check(state, arg)) { + other = (MultiDictObject *)arg; + } else if (AnyMultiDictProxy_Check(state, arg)) { + other = ((MultiDictProxyObject *)arg)->md; + } + if (other != NULL && other->is_ci == is_ci) { + if (md_clone_from_ht(self, other) < 0) { + ret = -1; + goto done; + } + ret = 1; + goto done; + } } +done: + return ret; +} - if (Py_TYPE(self)->tp_init((PyObject*)new_multidict, NULL, NULL) < 0) { +static inline PyObject * +multidict_copy(MultiDictObject *self) +{ + PyObject *ret = PyType_GenericNew(Py_TYPE(self), NULL, NULL); + if (ret == NULL) { goto fail; } - if (pair_list_update_from_pair_list(&new_multidict->pairs, - NULL, &self->pairs) < 0) { + MultiDictObject *new_md = (MultiDictObject *)ret; + if (md_clone_from_ht(new_md, self) < 0) { goto fail; } - return (PyObject*)new_multidict; + ASSERT_CONSISTENT(new_md, false); + return ret; fail: - Py_CLEAR(new_multidict); + Py_XDECREF(ret); return NULL; } static inline PyObject * _multidict_proxy_copy(MultiDictProxyObject *self, PyTypeObject *type) { - MultiDictObject *new_multidict = NULL; - new_multidict = (MultiDictObject*)PyType_GenericNew(type, NULL, NULL); - if (new_multidict == NULL) { - goto fail; - } - if (type->tp_init((PyObject*)new_multidict, NULL, NULL) < 0) { - goto fail; - } - if (pair_list_update_from_pair_list(&new_multidict->pairs, - NULL, &self->md->pairs) < 0) { - goto fail; - } - return (PyObject*)new_multidict; -fail: - Py_CLEAR(new_multidict); - return NULL; + return multidict_copy(self->md); } - /******************** Base Methods ********************/ static inline PyObject * multidict_getall(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *list = NULL, - *key = NULL, - *_default = NULL; - - if (parse2("getall", args, nargs, kwnames, 1, - "key", &key, "default", &_default) < 0) { + PyObject *list = NULL, *key = NULL, *_default = NULL; + + if (parse2("getall", + args, + nargs, + kwnames, + 1, + "key", + &key, + "default", + &_default) < 0) { return NULL; } - if (pair_list_get_all(&self->pairs, key, &list) <0) { + if (md_get_all(self, key, &list) < 0) { return NULL; } + ASSERT_CONSISTENT(self, false); + if (list == NULL) { if (_default != NULL) { Py_INCREF(_default); @@ -257,61 +284,78 @@ static inline PyObject * multidict_getone(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *key = NULL, - *_default = NULL; - - if (parse2("getone", args, nargs, kwnames, 1, - "key", &key, "default", &_default) < 0) { + PyObject *key = NULL, *_default = NULL; + + if (parse2("getone", + args, + nargs, + kwnames, + 1, + "key", + &key, + "default", + &_default) < 0) { return NULL; } return _multidict_getone(self, key, _default); } static inline PyObject * -multidict_get(MultiDictObject *self, PyObject *const *args, - Py_ssize_t nargs, PyObject *kwnames) +multidict_get(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames) { - PyObject *key = NULL, - *_default = NULL, - *ret; - - if (parse2("get", args, nargs, kwnames, 1, - "key", &key, "default", &_default) < 0) { + PyObject *key = NULL; + PyObject *_default = NULL; + bool decref_default = false; + + if (parse2("get", + args, + nargs, + kwnames, + 1, + "key", + &key, + "default", + &_default) < 0) { return NULL; } if (_default == NULL) { - // fixme, _default is potentially dangerous borrowed ref here - _default = Py_None; + _default = Py_GetConstant(Py_CONSTANT_NONE); + if (_default == NULL) { + return NULL; + } + decref_default = true; + } + ASSERT_CONSISTENT(self, false); + PyObject *ret = _multidict_getone(self, key, _default); + if (decref_default) { + Py_CLEAR(_default); } - ret = _multidict_getone(self, key, _default); return ret; } -static inline PyObject * +static PyObject * multidict_keys(MultiDictObject *self) { return multidict_keysview_new(self); } -static inline PyObject * +static PyObject * multidict_items(MultiDictObject *self) { return multidict_itemsview_new(self); } -static inline PyObject * +static PyObject * multidict_values(MultiDictObject *self) { return multidict_valuesview_new(self); } -static inline PyObject * +static PyObject * multidict_reduce(MultiDictObject *self) { - PyObject *items = NULL, - *items_list = NULL, - *args = NULL, - *result = NULL; + PyObject *items = NULL, *items_list = NULL, *args = NULL, *result = NULL; items = multidict_itemsview_new(self); if (items == NULL) { @@ -337,7 +381,7 @@ ret: return result; } -static inline PyObject * +static PyObject * multidict_repr(MultiDictObject *self) { int tmp = Py_ReprEnter((PyObject *)self); @@ -347,53 +391,54 @@ multidict_repr(MultiDictObject *self) if (tmp > 0) { return PyUnicode_FromString("..."); } - PyObject *name = PyObject_GetAttrString((PyObject *)Py_TYPE(self), "__name__"); + PyObject *name = + PyObject_GetAttr((PyObject *)Py_TYPE(self), self->state->str_name); if (name == NULL) { Py_ReprLeave((PyObject *)self); return NULL; } - PyObject *ret = pair_list_repr(&self->pairs, name, true, true); + PyObject *ret = md_repr(self, name, true, true); Py_ReprLeave((PyObject *)self); Py_CLEAR(name); return ret; } -static inline Py_ssize_t +static Py_ssize_t multidict_mp_len(MultiDictObject *self) { - return pair_list_len(&self->pairs); + return md_len(self); } -static inline PyObject * +static PyObject * multidict_mp_subscript(MultiDictObject *self, PyObject *key) { return _multidict_getone(self, key, NULL); } -static inline int +static int multidict_mp_as_subscript(MultiDictObject *self, PyObject *key, PyObject *val) { if (val == NULL) { - return pair_list_del(&self->pairs, key); + return md_del(self, key); } else { - return pair_list_replace(&self->pairs, key, val); + return md_replace(self, key, val); } } -static inline int +static int multidict_sq_contains(MultiDictObject *self, PyObject *key) { - return pair_list_contains(&self->pairs, key, NULL); + return md_contains(self, key, NULL); } -static inline PyObject * +static PyObject * multidict_tp_iter(MultiDictObject *self) { return multidict_keys_iter_new(self); } -static inline PyObject * -multidict_tp_richcompare(PyObject *self, PyObject *other, int op) +static PyObject * +multidict_tp_richcompare(MultiDictObject *self, PyObject *other, int op) { int cmp; @@ -401,7 +446,7 @@ multidict_tp_richcompare(PyObject *self, PyObject *other, int op) Py_RETURN_NOTIMPLEMENTED; } - if (self == other) { + if ((PyObject *)self == other) { cmp = 1; if (op == Py_NE) { cmp = !cmp; @@ -409,17 +454,11 @@ multidict_tp_richcompare(PyObject *self, PyObject *other, int op) return PyBool_FromLong(cmp); } - mod_state *state = ((MultiDictObject*)self)->pairs.state; + mod_state *state = self->state; if (AnyMultiDict_Check(state, other)) { - cmp = pair_list_eq( - &((MultiDictObject*)self)->pairs, - &((MultiDictObject*)other)->pairs - ); + cmp = md_eq(self, (MultiDictObject *)other); } else if (AnyMultiDictProxy_Check(state, other)) { - cmp = pair_list_eq( - &((MultiDictObject*)self)->pairs, - &((MultiDictProxyObject*)other)->md->pairs - ); + cmp = md_eq(self, ((MultiDictProxyObject *)other)->md); } else { bool fits = false; fits = PyDict_Check(other); @@ -434,10 +473,9 @@ multidict_tp_richcompare(PyObject *self, PyObject *other, int op) Py_CLEAR(keys); } if (fits) { - cmp = pair_list_eq_to_mapping(&((MultiDictObject*)self)->pairs, - other); + cmp = md_eq_to_mapping(self, other); } else { - cmp = 0; // e.g., multidict is not equal to a list + cmp = 0; // e.g., multidict is not equal to a list } } if (cmp < 0) { @@ -449,153 +487,192 @@ multidict_tp_richcompare(PyObject *self, PyObject *other, int op) return PyBool_FromLong(cmp); } -static inline void +static void multidict_tp_dealloc(MultiDictObject *self) { PyObject_GC_UnTrack(self); Py_TRASHCAN_BEGIN(self, multidict_tp_dealloc) - PyObject_ClearWeakRefs((PyObject *)self); - pair_list_dealloc(&self->pairs); + PyObject_ClearWeakRefs((PyObject *)self); + md_clear(self); Py_TYPE(self)->tp_free((PyObject *)self); - Py_TRASHCAN_END // there should be no code after this + Py_TRASHCAN_END // there should be no code after this } -static inline int +static int multidict_tp_traverse(MultiDictObject *self, visitproc visit, void *arg) { Py_VISIT(Py_TYPE(self)); - return pair_list_traverse(&self->pairs, visit, arg); + return md_traverse(self, visit, arg); } -static inline int +static int multidict_tp_clear(MultiDictObject *self) { - return pair_list_clear(&self->pairs); + return md_clear(self); } PyDoc_STRVAR(multidict_getall_doc, -"Return a list of all values matching the key."); + "Return a list of all values matching the key."); -PyDoc_STRVAR(multidict_getone_doc, -"Get first value matching the key."); +PyDoc_STRVAR(multidict_getone_doc, "Get first value matching the key."); -PyDoc_STRVAR(multidict_get_doc, -"Get first value matching the key.\n\nThe method is alias for .getone()."); +PyDoc_STRVAR( + multidict_get_doc, + "Get first value matching the key.\n\nThe method is alias for .getone()."); PyDoc_STRVAR(multidict_keys_doc, -"Return a new view of the dictionary's keys."); + "Return a new view of the dictionary's keys."); -PyDoc_STRVAR(multidict_items_doc, -"Return a new view of the dictionary's items *(key, value) pairs)."); +PyDoc_STRVAR( + multidict_items_doc, + "Return a new view of the dictionary's items *(key, value) pairs)."); PyDoc_STRVAR(multidict_values_doc, -"Return a new view of the dictionary's values."); + "Return a new view of the dictionary's values."); /******************** MultiDict ********************/ -static inline int +static int multidict_tp_init(MultiDictObject *self, PyObject *args, PyObject *kwds) { mod_state *state = get_mod_state_by_def((PyObject *)self); PyObject *arg = NULL; - Py_ssize_t size = _multidict_extend_parse_args(args, kwds, "MultiDict", &arg); + Py_ssize_t size = + _multidict_extend_parse_args(state, args, kwds, "MultiDict", &arg); if (size < 0) { goto fail; } - if (pair_list_init(&self->pairs, state, size) < 0) { + int tmp = _multidict_clone_fast(state, self, false, args, kwds); + if (tmp < 0) { goto fail; + } else if (tmp == 1) { + goto done; } - if (_multidict_extend(self, arg, kwds, "MultiDict", 1) < 0) { + if (md_init(self, state, false, size) < 0) { goto fail; } + if (_multidict_extend(self, arg, kwds, "MultiDict", false) < 0) { + goto fail; + } +done: Py_CLEAR(arg); + ASSERT_CONSISTENT(self, false); return 0; fail: Py_CLEAR(arg); return -1; } -static inline PyObject * -multidict_add(MultiDictObject *self, PyObject *const *args, - Py_ssize_t nargs, PyObject *kwnames) +static PyObject * +multidict_add(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames) { - PyObject *key = NULL, - *val = NULL; + PyObject *key = NULL, *val = NULL; - if (parse2("add", args, nargs, kwnames, 2, - "key", &key, "value", &val) < 0) { + if (parse2("add", args, nargs, kwnames, 2, "key", &key, "value", &val) < + 0) { return NULL; } - if (pair_list_add(&self->pairs, key, val) < 0) { + if (md_add(self, key, val) < 0) { return NULL; } - + ASSERT_CONSISTENT(self, false); Py_RETURN_NONE; } -static inline PyObject * +static PyObject * multidict_extend(MultiDictObject *self, PyObject *args, PyObject *kwds) { PyObject *arg = NULL; - Py_ssize_t size = _multidict_extend_parse_args(args, kwds, "extend", &arg); + Py_ssize_t size = + _multidict_extend_parse_args(self->state, args, kwds, "extend", &arg); if (size < 0) { goto fail; } - pair_list_grow(&self->pairs, size); - if (_multidict_extend(self, arg, kwds, "extend", 1) < 0) { + if (md_reserve(self, size) < 0) { + goto fail; + } + if (_multidict_extend(self, arg, kwds, "extend", false) < 0) { goto fail; } Py_CLEAR(arg); + ASSERT_CONSISTENT(self, false); Py_RETURN_NONE; fail: Py_CLEAR(arg); return NULL; } -static inline PyObject * +static PyObject * multidict_clear(MultiDictObject *self) { - if (pair_list_clear(&self->pairs) < 0) { + if (md_clear(self) < 0) { return NULL; } + ASSERT_CONSISTENT(self, false); Py_RETURN_NONE; } -static inline PyObject * +static PyObject * multidict_setdefault(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *key = NULL, - *_default = NULL; - - if (parse2("setdefault", args, nargs, kwnames, 1, - "key", &key, "default", &_default) < 0) { + PyObject *key = NULL; + PyObject *_default = NULL; + bool decref_default = false; + PyObject *ret = NULL; + + if (parse2("setdefault", + args, + nargs, + kwnames, + 1, + "key", + &key, + "default", + &_default) < 0) { return NULL; } if (_default == NULL) { - // fixme, _default is potentially dangerous borrowed ref here - _default = Py_None; + _default = Py_GetConstant(Py_CONSTANT_NONE); + if (_default == NULL) { + return NULL; + } + decref_default = true; + } + ASSERT_CONSISTENT(self, false); + if (md_set_default(self, key, _default, &ret) < 0) { + return NULL; + } + if (decref_default) { + Py_CLEAR(_default); } - return pair_list_set_default(&self->pairs, key, _default); + return ret; } -static inline PyObject * +static PyObject * multidict_popone(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *key = NULL, - *_default = NULL, - *ret_val = NULL; - - if (parse2("popone", args, nargs, kwnames, 1, - "key", &key, "default", &_default) < 0) { + PyObject *key = NULL, *_default = NULL, *ret_val = NULL; + + if (parse2("popone", + args, + nargs, + kwnames, + 1, + "key", + &key, + "default", + &_default) < 0) { return NULL; } - if (pair_list_pop_one(&self->pairs, key, &ret_val) < 0) { + if (md_pop_one(self, key, &ret_val) < 0) { return NULL; } + ASSERT_CONSISTENT(self, false); if (ret_val == NULL) { if (_default != NULL) { Py_INCREF(_default); @@ -609,26 +686,28 @@ multidict_popone(MultiDictObject *self, PyObject *const *args, } } -static inline PyObject * -multidict_pop( - MultiDictObject *self, - PyObject *const *args, - Py_ssize_t nargs, - PyObject *kwnames -) +static PyObject * +multidict_pop(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames) { - PyObject *key = NULL, - *_default = NULL, - *ret_val = NULL; - - if (parse2("pop", args, nargs, kwnames, 1, - "key", &key, "default", &_default) < 0) { + PyObject *key = NULL, *_default = NULL, *ret_val = NULL; + + if (parse2("pop", + args, + nargs, + kwnames, + 1, + "key", + &key, + "default", + &_default) < 0) { return NULL; } - if (pair_list_pop_one(&self->pairs, key, &ret_val) < 0) { + if (md_pop_one(self, key, &ret_val) < 0) { return NULL; } + ASSERT_CONSISTENT(self, false); if (ret_val == NULL) { if (_default != NULL) { Py_INCREF(_default); @@ -642,22 +721,28 @@ multidict_pop( } } -static inline PyObject * +static PyObject * multidict_popall(MultiDictObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *key = NULL, - *_default = NULL, - *ret_val = NULL; - - if (parse2("popall", args, nargs, kwnames, 1, - "key", &key, "default", &_default) < 0) { + PyObject *key = NULL, *_default = NULL, *ret_val = NULL; + + if (parse2("popall", + args, + nargs, + kwnames, + 1, + "key", + &key, + "default", + &_default) < 0) { return NULL; } - if (pair_list_pop_all(&self->pairs, key, &ret_val) < 0) { + if (md_pop_all(self, key, &ret_val) < 0) { return NULL; } + ASSERT_CONSISTENT(self, false); if (ret_val == NULL) { if (_default != NULL) { Py_INCREF(_default); @@ -671,23 +756,29 @@ multidict_popall(MultiDictObject *self, PyObject *const *args, } } -static inline PyObject * +static PyObject * multidict_popitem(MultiDictObject *self) { - return pair_list_pop_item(&self->pairs); + return md_pop_item(self); } -static inline PyObject * +static PyObject * multidict_update(MultiDictObject *self, PyObject *args, PyObject *kwds) { PyObject *arg = NULL; - if (_multidict_extend_parse_args(args, kwds, "update", &arg) < 0) { + Py_ssize_t size = + _multidict_extend_parse_args(self->state, args, kwds, "update", &arg); + if (size < 0) { goto fail; } - if (_multidict_extend(self, arg, kwds, "update", 0) < 0) { + if (md_reserve(self, size) < 0) { + goto fail; + } + if (_multidict_extend(self, arg, kwds, "update", true) < 0) { goto fail; } Py_CLEAR(arg); + ASSERT_CONSISTENT(self, false); Py_RETURN_NONE; fail: Py_CLEAR(arg); @@ -695,186 +786,135 @@ fail: } PyDoc_STRVAR(multidict_add_doc, -"Add the key and value, not overwriting any previous value."); + "Add the key and value, not overwriting any previous value."); -PyDoc_STRVAR(multidict_copy_doc, -"Return a copy of itself."); +PyDoc_STRVAR(multidict_copy_doc, "Return a copy of itself."); PyDoc_STRVAR(multdicit_method_extend_doc, -"Extend current MultiDict with more values.\n\ + "Extend current MultiDict with more values.\n\ This method must be used instead of update."); -PyDoc_STRVAR(multidict_clear_doc, -"Remove all items from MultiDict"); +PyDoc_STRVAR(multidict_clear_doc, "Remove all items from MultiDict"); -PyDoc_STRVAR(multidict_setdefault_doc, -"Return value for key, set value to default if key is not present."); +PyDoc_STRVAR( + multidict_setdefault_doc, + "Return value for key, set value to default if key is not present."); -PyDoc_STRVAR(multidict_popone_doc, -"Remove the last occurrence of key and return the corresponding value.\n\n\ +PyDoc_STRVAR( + multidict_popone_doc, + "Remove the last occurrence of key and return the corresponding value.\n\n\ If key is not found, default is returned if given, otherwise KeyError is \ raised.\n"); -PyDoc_STRVAR(multidict_pop_doc, -"Remove the last occurrence of key and return the corresponding value.\n\n\ +PyDoc_STRVAR( + multidict_pop_doc, + "Remove the last occurrence of key and return the corresponding value.\n\n\ If key is not found, default is returned if given, otherwise KeyError is \ raised.\n"); -PyDoc_STRVAR(multidict_popall_doc, -"Remove all occurrences of key and return the list of corresponding values.\n\n\ +PyDoc_STRVAR( + multidict_popall_doc, + "Remove all occurrences of key and return the list of corresponding values.\n\n\ If key is not found, default is returned if given, otherwise KeyError is \ raised.\n"); PyDoc_STRVAR(multidict_popitem_doc, -"Remove and return an arbitrary (key, value) pair."); + "Remove and return an arbitrary (key, value) pair."); PyDoc_STRVAR(multidict_update_doc, -"Update the dictionary from *other*, overwriting existing keys."); + "Update the dictionary from *other*, overwriting existing keys."); -PyDoc_STRVAR(sizeof__doc__, -"D.__sizeof__() -> size of D in memory, in bytes"); +PyDoc_STRVAR(sizeof__doc__, "D.__sizeof__() -> size of D in memory, in bytes"); -static inline PyObject * -_multidict_sizeof(MultiDictObject *self) +static PyObject * +multidict_sizeof(MultiDictObject *self) { Py_ssize_t size = sizeof(MultiDictObject); - if (self->pairs.pairs != self->pairs.buffer) { - size += (Py_ssize_t)sizeof(pair_t) * self->pairs.capacity; - } + if (self->keys != &empty_htkeys) size += htkeys_sizeof(self->keys); return PyLong_FromSsize_t(size); } - static PyMethodDef multidict_methods[] = { - { - "getall", - (PyCFunction)multidict_getall, - METH_FASTCALL | METH_KEYWORDS, - multidict_getall_doc - }, - { - "getone", - (PyCFunction)multidict_getone, - METH_FASTCALL | METH_KEYWORDS, - multidict_getone_doc - }, - { - "get", - (PyCFunction)multidict_get, - METH_FASTCALL | METH_KEYWORDS, - multidict_get_doc - }, - { - "keys", - (PyCFunction)multidict_keys, - METH_NOARGS, - multidict_keys_doc - }, - { - "items", - (PyCFunction)multidict_items, - METH_NOARGS, - multidict_items_doc - }, - { - "values", - (PyCFunction)multidict_values, - METH_NOARGS, - multidict_values_doc - }, - { - "add", - (PyCFunction)multidict_add, - METH_FASTCALL | METH_KEYWORDS, - multidict_add_doc - }, - { - "copy", - (PyCFunction)multidict_copy, - METH_NOARGS, - multidict_copy_doc - }, - { - "extend", - (PyCFunction)multidict_extend, - METH_VARARGS | METH_KEYWORDS, - multdicit_method_extend_doc - }, - { - "clear", - (PyCFunction)multidict_clear, - METH_NOARGS, - multidict_clear_doc - }, - { - "setdefault", - (PyCFunction)multidict_setdefault, - METH_FASTCALL | METH_KEYWORDS, - multidict_setdefault_doc - }, - { - "popone", - (PyCFunction)multidict_popone, - METH_FASTCALL | METH_KEYWORDS, - multidict_popone_doc - }, - { - "pop", - (PyCFunction)multidict_pop, - METH_FASTCALL | METH_KEYWORDS, - multidict_pop_doc - }, - { - "popall", - (PyCFunction)multidict_popall, - METH_FASTCALL | METH_KEYWORDS, - multidict_popall_doc - }, - { - "popitem", - (PyCFunction)multidict_popitem, - METH_NOARGS, - multidict_popitem_doc - }, - { - "update", - (PyCFunction)multidict_update, - METH_VARARGS | METH_KEYWORDS, - multidict_update_doc - }, + {"getall", + (PyCFunction)multidict_getall, + METH_FASTCALL | METH_KEYWORDS, + multidict_getall_doc}, + {"getone", + (PyCFunction)multidict_getone, + METH_FASTCALL | METH_KEYWORDS, + multidict_getone_doc}, + {"get", + (PyCFunction)multidict_get, + METH_FASTCALL | METH_KEYWORDS, + multidict_get_doc}, + {"keys", (PyCFunction)multidict_keys, METH_NOARGS, multidict_keys_doc}, + {"items", (PyCFunction)multidict_items, METH_NOARGS, multidict_items_doc}, + {"values", + (PyCFunction)multidict_values, + METH_NOARGS, + multidict_values_doc}, + {"add", + (PyCFunction)multidict_add, + METH_FASTCALL | METH_KEYWORDS, + multidict_add_doc}, + {"copy", (PyCFunction)multidict_copy, METH_NOARGS, multidict_copy_doc}, + {"extend", + (PyCFunction)multidict_extend, + METH_VARARGS | METH_KEYWORDS, + multdicit_method_extend_doc}, + {"clear", (PyCFunction)multidict_clear, METH_NOARGS, multidict_clear_doc}, + {"setdefault", + (PyCFunction)multidict_setdefault, + METH_FASTCALL | METH_KEYWORDS, + multidict_setdefault_doc}, + {"popone", + (PyCFunction)multidict_popone, + METH_FASTCALL | METH_KEYWORDS, + multidict_popone_doc}, + {"pop", + (PyCFunction)multidict_pop, + METH_FASTCALL | METH_KEYWORDS, + multidict_pop_doc}, + {"popall", + (PyCFunction)multidict_popall, + METH_FASTCALL | METH_KEYWORDS, + multidict_popall_doc}, + {"popitem", + (PyCFunction)multidict_popitem, + METH_NOARGS, + multidict_popitem_doc}, + {"update", + (PyCFunction)multidict_update, + METH_VARARGS | METH_KEYWORDS, + multidict_update_doc}, { "__reduce__", (PyCFunction)multidict_reduce, METH_NOARGS, NULL, }, - { - "__class_getitem__", - (PyCFunction)Py_GenericAlias, - METH_O | METH_CLASS, - NULL - }, + {"__class_getitem__", + (PyCFunction)Py_GenericAlias, + METH_O | METH_CLASS, + NULL}, { "__sizeof__", - (PyCFunction)_multidict_sizeof, + (PyCFunction)multidict_sizeof, METH_NOARGS, sizeof__doc__, }, - { - NULL, - NULL - } /* sentinel */ + {NULL, NULL} /* sentinel */ }; - -PyDoc_STRVAR(MultDict_doc, -"Dictionary with the support for duplicate keys."); +PyDoc_STRVAR(MultDict_doc, "Dictionary with the support for duplicate keys."); #ifndef MANAGED_WEAKREFS static PyMemberDef multidict_members[] = { - {"__weaklistoffset__", Py_T_PYSSIZET, - offsetof(MultiDictObject, weaklist), Py_READONLY}, - {NULL} /* Sentinel */ + {"__weaklistoffset__", + Py_T_PYSSIZET, + offsetof(MultiDictObject, weaklist), + Py_READONLY}, + {NULL} /* Sentinel */ }; #endif @@ -907,7 +947,7 @@ static PyType_Slot multidict_slots[] = { static PyType_Spec multidict_spec = { .name = "multidict._multidict.MultiDict", .basicsize = sizeof(MultiDictObject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE #if PY_VERSION_HEX >= 0x030a00f0 | Py_TPFLAGS_IMMUTABLETYPE #endif @@ -918,36 +958,42 @@ static PyType_Spec multidict_spec = { .slots = multidict_slots, }; - /******************** CIMultiDict ********************/ -static inline int +static int cimultidict_tp_init(MultiDictObject *self, PyObject *args, PyObject *kwds) { mod_state *state = get_mod_state_by_def((PyObject *)self); PyObject *arg = NULL; - Py_ssize_t size = _multidict_extend_parse_args(args, kwds, "CIMultiDict", &arg); + Py_ssize_t size = + _multidict_extend_parse_args(state, args, kwds, "CIMultiDict", &arg); if (size < 0) { goto fail; } - - if (ci_pair_list_init(&self->pairs, state, size) < 0) { + int tmp = _multidict_clone_fast(state, self, true, args, kwds); + if (tmp < 0) { goto fail; + } else if (tmp == 1) { + goto done; } - - if (_multidict_extend(self, arg, kwds, "CIMultiDict", 1) < 0) { + if (md_init(self, state, true, size) < 0) { + goto fail; + } + if (_multidict_extend(self, arg, kwds, "CIMultiDict", false) < 0) { goto fail; } +done: Py_CLEAR(arg); + ASSERT_CONSISTENT(self, false); return 0; fail: Py_CLEAR(arg); return -1; } - -PyDoc_STRVAR(CIMultDict_doc, -"Dictionary with the support for duplicate case-insensitive keys."); +PyDoc_STRVAR( + CIMultDict_doc, + "Dictionary with the support for duplicate case-insensitive keys."); static PyType_Slot cimultidict_slots[] = { {Py_tp_doc, (void *)CIMultDict_doc}, @@ -968,42 +1014,42 @@ static PyType_Spec cimultidict_spec = { /******************** MultiDictProxy ********************/ -static inline int +static int multidict_proxy_tp_init(MultiDictProxyObject *self, PyObject *args, PyObject *kwds) { mod_state *state = get_mod_state_by_def((PyObject *)self); - PyObject *arg = NULL; - MultiDictObject *md = NULL; + PyObject *arg = NULL; + MultiDictObject *md = NULL; - if (!PyArg_UnpackTuple(args, "multidict._multidict.MultiDictProxy", - 0, 1, &arg)) - { + if (!PyArg_UnpackTuple( + args, "multidict._multidict.MultiDictProxy", 0, 1, &arg)) { return -1; } if (arg == NULL) { PyErr_Format( PyExc_TypeError, - "__init__() missing 1 required positional argument: 'arg'" - ); + "__init__() missing 1 required positional argument: 'arg'"); + return -1; + } + if (kwds != NULL) { + PyErr_Format(PyExc_TypeError, + "__init__() doesn't accept keyword arguments"); return -1; } if (!AnyMultiDictProxy_Check(state, arg) && - !AnyMultiDict_Check(state, arg)) - { - PyErr_Format( - PyExc_TypeError, - "ctor requires MultiDict or MultiDictProxy instance, " - "not <class '%s'>", - Py_TYPE(arg)->tp_name - ); + !AnyMultiDict_Check(state, arg)) { + PyErr_Format(PyExc_TypeError, + "ctor requires MultiDict or MultiDictProxy instance, " + "not <class '%s'>", + Py_TYPE(arg)->tp_name); return -1; } if (AnyMultiDictProxy_Check(state, arg)) { - md = ((MultiDictProxyObject*)arg)->md; + md = ((MultiDictProxyObject *)arg)->md; } else { - md = (MultiDictObject*)arg; + md = (MultiDictObject *)arg; } Py_INCREF(md); self->md = md; @@ -1011,94 +1057,92 @@ multidict_proxy_tp_init(MultiDictProxyObject *self, PyObject *args, return 0; } -static inline PyObject * +static PyObject * multidict_proxy_getall(MultiDictProxyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { return multidict_getall(self->md, args, nargs, kwnames); } -static inline PyObject * +static PyObject * multidict_proxy_getone(MultiDictProxyObject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { return multidict_getone(self->md, args, nargs, kwnames); } -static inline PyObject * +static PyObject * multidict_proxy_get(MultiDictProxyObject *self, PyObject *const *args, - Py_ssize_t nargs, PyObject *kwnames) + Py_ssize_t nargs, PyObject *kwnames) { return multidict_get(self->md, args, nargs, kwnames); } -static inline PyObject * +static PyObject * multidict_proxy_keys(MultiDictProxyObject *self) { - return multidict_keys(self->md); + return multidict_keysview_new(self->md); } -static inline PyObject * +static PyObject * multidict_proxy_items(MultiDictProxyObject *self) { - return multidict_items(self->md); + return multidict_itemsview_new(self->md); } -static inline PyObject * +static PyObject * multidict_proxy_values(MultiDictProxyObject *self) { - return multidict_values(self->md); + return multidict_valuesview_new(self->md); } -static inline PyObject * +static PyObject * multidict_proxy_copy(MultiDictProxyObject *self) { - return _multidict_proxy_copy(self, self->md->pairs.state->MultiDictType); + return _multidict_proxy_copy(self, self->md->state->MultiDictType); } -static inline PyObject * +static PyObject * multidict_proxy_reduce(MultiDictProxyObject *self) { PyErr_Format( - PyExc_TypeError, - "can't pickle %s objects", Py_TYPE(self)->tp_name - ); + PyExc_TypeError, "can't pickle %s objects", Py_TYPE(self)->tp_name); return NULL; } -static inline Py_ssize_t +static Py_ssize_t multidict_proxy_mp_len(MultiDictProxyObject *self) { - return multidict_mp_len(self->md); + return md_len(self->md); } -static inline PyObject * +static PyObject * multidict_proxy_mp_subscript(MultiDictProxyObject *self, PyObject *key) { - return multidict_mp_subscript(self->md, key); + return _multidict_getone(self->md, key, NULL); } -static inline int +static int multidict_proxy_sq_contains(MultiDictProxyObject *self, PyObject *key) { - return multidict_sq_contains(self->md, key); + return md_contains(self->md, key, NULL); } -static inline PyObject * +static PyObject * multidict_proxy_tp_iter(MultiDictProxyObject *self) { - return multidict_tp_iter(self->md); + return multidict_keys_iter_new(self->md); } -static inline PyObject * +static PyObject * multidict_proxy_tp_richcompare(MultiDictProxyObject *self, PyObject *other, int op) { - return multidict_tp_richcompare((PyObject*)self->md, other, op); + return multidict_tp_richcompare(self->md, other, op); } -static inline void +static void multidict_proxy_tp_dealloc(MultiDictProxyObject *self) { PyObject_GC_UnTrack(self); @@ -1107,7 +1151,7 @@ multidict_proxy_tp_dealloc(MultiDictProxyObject *self) Py_TYPE(self)->tp_free((PyObject *)self); } -static inline int +static int multidict_proxy_tp_traverse(MultiDictProxyObject *self, visitproc visit, void *arg) { @@ -1116,95 +1160,70 @@ multidict_proxy_tp_traverse(MultiDictProxyObject *self, visitproc visit, return 0; } -static inline int +static int multidict_proxy_tp_clear(MultiDictProxyObject *self) { Py_CLEAR(self->md); return 0; } -static inline PyObject * +static PyObject * multidict_proxy_repr(MultiDictProxyObject *self) { - PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__"); - if (name == NULL) - return NULL; - PyObject *ret = pair_list_repr(&self->md->pairs, name, true, true); + PyObject *name = + PyObject_GetAttr((PyObject *)Py_TYPE(self), self->md->state->str_name); + if (name == NULL) return NULL; + PyObject *ret = md_repr(self->md, name, true, true); Py_CLEAR(name); return ret; } - static PyMethodDef multidict_proxy_methods[] = { - { - "getall", - (PyCFunction)multidict_proxy_getall, - METH_FASTCALL | METH_KEYWORDS, - multidict_getall_doc - }, - { - "getone", - (PyCFunction)multidict_proxy_getone, - METH_FASTCALL | METH_KEYWORDS, - multidict_getone_doc - }, - { - "get", - (PyCFunction)multidict_proxy_get, - METH_FASTCALL | METH_KEYWORDS, - multidict_get_doc - }, - { - "keys", - (PyCFunction)multidict_proxy_keys, - METH_NOARGS, - multidict_keys_doc - }, - { - "items", - (PyCFunction)multidict_proxy_items, - METH_NOARGS, - multidict_items_doc - }, - { - "values", - (PyCFunction)multidict_proxy_values, - METH_NOARGS, - multidict_values_doc - }, - { - "copy", - (PyCFunction)multidict_proxy_copy, - METH_NOARGS, - multidict_copy_doc - }, - { - "__reduce__", - (PyCFunction)multidict_proxy_reduce, - METH_NOARGS, - NULL - }, - { - "__class_getitem__", - (PyCFunction)Py_GenericAlias, - METH_O | METH_CLASS, - NULL - }, - { - NULL, - NULL - } /* sentinel */ + {"getall", + (PyCFunction)multidict_proxy_getall, + METH_FASTCALL | METH_KEYWORDS, + multidict_getall_doc}, + {"getone", + (PyCFunction)multidict_proxy_getone, + METH_FASTCALL | METH_KEYWORDS, + multidict_getone_doc}, + {"get", + (PyCFunction)multidict_proxy_get, + METH_FASTCALL | METH_KEYWORDS, + multidict_get_doc}, + {"keys", + (PyCFunction)multidict_proxy_keys, + METH_NOARGS, + multidict_keys_doc}, + {"items", + (PyCFunction)multidict_proxy_items, + METH_NOARGS, + multidict_items_doc}, + {"values", + (PyCFunction)multidict_proxy_values, + METH_NOARGS, + multidict_values_doc}, + {"copy", + (PyCFunction)multidict_proxy_copy, + METH_NOARGS, + multidict_copy_doc}, + {"__reduce__", (PyCFunction)multidict_proxy_reduce, METH_NOARGS, NULL}, + {"__class_getitem__", + (PyCFunction)Py_GenericAlias, + METH_O | METH_CLASS, + NULL}, + {NULL, NULL} /* sentinel */ }; - -PyDoc_STRVAR(MultDictProxy_doc, -"Read-only proxy for MultiDict instance."); +PyDoc_STRVAR(MultDictProxy_doc, "Read-only proxy for MultiDict instance."); #ifndef MANAGED_WEAKREFS static PyMemberDef multidict_proxy_members[] = { - {"__weaklistoffset__", Py_T_PYSSIZET, - offsetof(MultiDictProxyObject, weaklist), Py_READONLY}, - {NULL} /* Sentinel */ + {"__weaklistoffset__", + Py_T_PYSSIZET, + offsetof(MultiDictProxyObject, weaklist), + Py_READONLY}, + {NULL} /* Sentinel */ }; #endif @@ -1236,7 +1255,7 @@ static PyType_Slot multidict_proxy_slots[] = { static PyType_Spec multidict_proxy_spec = { .name = "multidict._multidict.MultiDictProxy", .basicsize = sizeof(MultiDictProxyObject), - .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE #if PY_VERSION_HEX >= 0x030a00f0 | Py_TPFLAGS_IMMUTABLETYPE #endif @@ -1249,41 +1268,42 @@ static PyType_Spec multidict_proxy_spec = { /******************** CIMultiDictProxy ********************/ -static inline int +static int cimultidict_proxy_tp_init(MultiDictProxyObject *self, PyObject *args, PyObject *kwds) { mod_state *state = get_mod_state_by_def((PyObject *)self); - PyObject *arg = NULL; - MultiDictObject *md = NULL; + PyObject *arg = NULL; + MultiDictObject *md = NULL; - if (!PyArg_UnpackTuple(args, "multidict._multidict.CIMultiDictProxy", - 1, 1, &arg)) - { + if (!PyArg_UnpackTuple( + args, "multidict._multidict.CIMultiDictProxy", 1, 1, &arg)) { return -1; } if (arg == NULL) { PyErr_Format( PyExc_TypeError, - "__init__() missing 1 required positional argument: 'arg'" - ); + "__init__() missing 1 required positional argument: 'arg'"); return -1; } - if (!CIMultiDictProxy_Check(state, arg) - && !CIMultiDict_Check(state, arg)) { - PyErr_Format( - PyExc_TypeError, - "ctor requires CIMultiDict or CIMultiDictProxy instance, " - "not <class '%s'>", - Py_TYPE(arg)->tp_name - ); + if (kwds != NULL) { + PyErr_Format(PyExc_TypeError, + "__init__() doesn't accept keyword arguments"); + return -1; + } + if (!CIMultiDictProxy_Check(state, arg) && + !CIMultiDict_Check(state, arg)) { + PyErr_Format(PyExc_TypeError, + "ctor requires CIMultiDict or CIMultiDictProxy instance, " + "not <class '%s'>", + Py_TYPE(arg)->tp_name); return -1; } if (CIMultiDictProxy_Check(state, arg)) { - md = ((MultiDictProxyObject*)arg)->md; + md = ((MultiDictProxyObject *)arg)->md; } else { - md = (MultiDictObject*)arg; + md = (MultiDictObject *)arg; } Py_INCREF(md); self->md = md; @@ -1291,30 +1311,22 @@ cimultidict_proxy_tp_init(MultiDictProxyObject *self, PyObject *args, return 0; } -static inline PyObject * +static PyObject * cimultidict_proxy_copy(MultiDictProxyObject *self) { - return _multidict_proxy_copy(self, self->md->pairs.state->CIMultiDictType); + return _multidict_proxy_copy(self, self->md->state->CIMultiDictType); } +PyDoc_STRVAR(CIMultDictProxy_doc, "Read-only proxy for CIMultiDict instance."); -PyDoc_STRVAR(CIMultDictProxy_doc, -"Read-only proxy for CIMultiDict instance."); - -PyDoc_STRVAR(cimultidict_proxy_copy_doc, -"Return copy of itself"); +PyDoc_STRVAR(cimultidict_proxy_copy_doc, "Return copy of itself"); static PyMethodDef cimultidict_proxy_methods[] = { - { - "copy", - (PyCFunction)cimultidict_proxy_copy, - METH_NOARGS, - cimultidict_proxy_copy_doc - }, - { - NULL, - NULL - } /* sentinel */ + {"copy", + (PyCFunction)cimultidict_proxy_copy, + METH_NOARGS, + cimultidict_proxy_copy_doc}, + {NULL, NULL} /* sentinel */ }; static PyType_Slot cimultidict_proxy_slots[] = { @@ -1337,20 +1349,20 @@ static PyType_Spec cimultidict_proxy_spec = { /******************** Other functions ********************/ -static inline PyObject * -getversion(PyObject *self, PyObject *md) +static PyObject * +getversion(PyObject *self, PyObject *arg) { mod_state *state = get_mod_state(self); - pair_list_t *pairs = NULL; - if (AnyMultiDict_Check(state, md)) { - pairs = &((MultiDictObject*)md)->pairs; - } else if (AnyMultiDictProxy_Check(state, md)) { - pairs = &((MultiDictProxyObject*)md)->md->pairs; + MultiDictObject *md; + if (AnyMultiDict_Check(state, arg)) { + md = (MultiDictObject *)arg; + } else if (AnyMultiDictProxy_Check(state, arg)) { + md = ((MultiDictProxyObject *)arg)->md; } else { PyErr_Format(PyExc_TypeError, "unexpected type"); return NULL; } - return PyLong_FromUnsignedLong(pair_list_version(pairs)); + return PyLong_FromUnsignedLong(md_version(md)); } /******************** Module ********************/ @@ -1375,8 +1387,9 @@ module_traverse(PyObject *mod, visitproc visit, void *arg) Py_VISIT(state->ItemsIterType); Py_VISIT(state->ValuesIterType); - Py_VISIT(state->str_lower); Py_VISIT(state->str_canonical); + Py_VISIT(state->str_lower); + Py_VISIT(state->str_name); return 0; } @@ -1401,13 +1414,14 @@ module_clear(PyObject *mod) Py_CLEAR(state->ItemsIterType); Py_CLEAR(state->ValuesIterType); - Py_CLEAR(state->str_lower); Py_CLEAR(state->str_canonical); + Py_CLEAR(state->str_lower); + Py_CLEAR(state->str_name); return 0; } -static inline void +static void module_free(void *mod) { (void)module_clear((PyObject *)mod); @@ -1415,10 +1429,9 @@ module_free(void *mod) static PyMethodDef module_methods[] = { {"getversion", (PyCFunction)getversion, METH_O}, - {NULL, NULL} /* sentinel */ + {NULL, NULL} /* sentinel */ }; - static int module_exec(PyObject *mod) { @@ -1434,6 +1447,10 @@ module_exec(PyObject *mod) if (state->str_canonical == NULL) { goto fail; } + state->str_name = PyUnicode_InternFromString("__name__"); + if (state->str_name == NULL) { + goto fail; + } if (multidict_views_init(mod, state) < 0) { goto fail; @@ -1512,7 +1529,6 @@ fail: return -1; } - static struct PyModuleDef_Slot module_slots[] = { {Py_mod_exec, module_exec}, #if PY_VERSION_HEX >= 0x030c00f0 @@ -1524,7 +1540,6 @@ static struct PyModuleDef_Slot module_slots[] = { {0, NULL}, }; - static PyModuleDef multidict_module = { .m_base = PyModuleDef_HEAD_INIT, .m_name = "_multidict", diff --git a/contrib/python/multidict/multidict/_multidict_py.py b/contrib/python/multidict/multidict/_multidict_py.py index 4a24ff41bd1..f9fa25671a0 100644 --- a/contrib/python/multidict/multidict/_multidict_py.py +++ b/contrib/python/multidict/multidict/_multidict_py.py @@ -1,9 +1,9 @@ import enum +import functools import reprlib import sys from array import array from collections.abc import ( - Callable, ItemsView, Iterable, Iterator, @@ -11,9 +11,11 @@ from collections.abc import ( Mapping, ValuesView, ) +from dataclasses import dataclass from typing import ( TYPE_CHECKING, Any, + ClassVar, Generic, NoReturn, Optional, @@ -81,11 +83,12 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): return False key, value = item try: - ident = self._md._identity(key) + identity = self._md._identity(key) except TypeError: return False - for i, k, v in self._md._items: - if ident == i and value == v: + hash_ = hash(identity) + for slot, idx, e in self._md._keys.iter_hash(hash_): + if e.identity == identity and value == e.value: return True return False @@ -93,28 +96,29 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): return _Iter(len(self), self._iter(self._md._version)) def _iter(self, version: int) -> Iterator[tuple[str, _V]]: - for i, k, v in self._md._items: + for e in self._md._keys.iter_entries(): if version != self._md._version: raise RuntimeError("Dictionary changed during iteration") - yield self._md._key(k), v + yield self._md._key(e.key), e.value @reprlib.recursive_repr() def __repr__(self) -> str: lst = [] - for i, k, v in self._md._items: - lst.append(f"'{k}': {v!r}") + for e in self._md._keys.iter_entries(): + lst.append(f"'{e.key}': {e.value!r}") body = ", ".join(lst) return f"<{self.__class__.__name__}({body})>" def _parse_item( self, arg: Union[tuple[str, _V], _T] - ) -> Optional[tuple[str, str, _V]]: + ) -> Optional[tuple[int, str, str, _V]]: if not isinstance(arg, tuple): return None if len(arg) != 2: return None try: - return (self._md._identity(arg[0]), arg[0], arg[1]) + identity = self._md._identity(arg[0]) + return (hash(identity), identity, arg[0], arg[1]) except TypeError: return None @@ -125,7 +129,7 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): if item is None: continue else: - tmp.add((item[0], item[2])) + tmp.add((item[1], item[3])) return tmp def __and__(self, other: Iterable[Any]) -> set[tuple[str, _V]]: @@ -138,10 +142,12 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): item = self._parse_item(arg) if item is None: continue - identity, key, value = item - for i, k, v in self._md._items: - if i == identity and v == value: - ret.add((k, v)) + hash_, identity, key, value = item + for slot, idx, e in self._md._keys.iter_hash(hash_): + e.hash = -1 + if e.identity == identity and e.value == value: + ret.add((e.key, e.value)) + self._md._keys.restore_hash(hash_) return ret def __rand__(self, other: Iterable[_T]) -> set[_T]: @@ -154,9 +160,9 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): item = self._parse_item(arg) if item is None: continue - identity, key, value = item - for i, k, v in self._md._items: - if i == identity and v == value: + hash_, identity, key, value = item + for slot, idx, e in self._md._keys.iter_hash(hash_): + if e.identity == identity and e.value == value: ret.add(arg) break return ret @@ -168,13 +174,13 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): except TypeError: return NotImplemented for arg in it: - item: Optional[tuple[str, str, _V]] = self._parse_item(arg) + item: Optional[tuple[int, str, str, _V]] = self._parse_item(arg) if item is None: ret.add(arg) continue - identity, key, value = item - for i, k, v in self._md._items: - if i == identity and v == value: + hash_, identity, key, value = item + for slot, idx, e in self._md._keys.iter_hash(hash_): + if e.identity == identity and e.value == value: # pragma: no branch break else: ret.add(arg) @@ -187,9 +193,9 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): return NotImplemented tmp = self._tmp_set(ret) - for i, k, v in self._md._items: - if (i, v) not in tmp: - ret.add((k, v)) + for e in self._md._keys.iter_entries(): + if (e.identity, e.value) not in tmp: + ret.add((e.key, e.value)) return ret def __sub__(self, other: Iterable[_T]) -> set[Union[tuple[str, _V], _T]]: @@ -200,9 +206,9 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): return NotImplemented tmp = self._tmp_set(it) - for i, k, v in self._md._items: - if (i, v) not in tmp: - ret.add((k, v)) + for e in self._md._keys.iter_entries(): + if (e.identity, e.value) not in tmp: + ret.add((e.key, e.value)) return ret @@ -218,9 +224,9 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): ret.add(arg) continue - identity, key, value = item - for i, k, v in self._md._items: - if i == identity and v == value: + hash_, identity, key, value = item + for slot, idx, e in self._md._keys.iter_hash(hash_): + if e.identity == identity and e.value == value: # pragma: no branch break else: ret.add(arg) @@ -243,17 +249,17 @@ class _ItemsView(_ViewBase[_V], ItemsView[str, _V]): if item is None: continue - identity, key, value = item - for i, k, v in self._md._items: - if i == identity and v == value: + hash_, identity, key, value = item + for slot, idx, e in self._md._keys.iter_hash(hash_): + if e.identity == identity and e.value == value: # pragma: no branch return False return True class _ValuesView(_ViewBase[_V], ValuesView[_V]): def __contains__(self, value: object) -> bool: - for i, k, v in self._md._items: - if v == value: + for e in self._md._keys.iter_entries(): + if e.value == value: return True return False @@ -261,16 +267,16 @@ class _ValuesView(_ViewBase[_V], ValuesView[_V]): return _Iter(len(self), self._iter(self._md._version)) def _iter(self, version: int) -> Iterator[_V]: - for i, k, v in self._md._items: + for e in self._md._keys.iter_entries(): if version != self._md._version: raise RuntimeError("Dictionary changed during iteration") - yield v + yield e.value @reprlib.recursive_repr() def __repr__(self) -> str: lst = [] - for i, k, v in self._md._items: - lst.append(repr(v)) + for e in self._md._keys.iter_entries(): + lst.append(repr(e.value)) body = ", ".join(lst) return f"<{self.__class__.__name__}({body})>" @@ -280,8 +286,9 @@ class _KeysView(_ViewBase[_V], KeysView[str]): if not isinstance(key, str): return False identity = self._md._identity(key) - for i, k, v in self._md._items: - if i == identity: + hash_ = hash(identity) + for slot, idx, e in self._md._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch return True return False @@ -289,15 +296,15 @@ class _KeysView(_ViewBase[_V], KeysView[str]): return _Iter(len(self), self._iter(self._md._version)) def _iter(self, version: int) -> Iterator[str]: - for i, k, v in self._md._items: + for e in self._md._keys.iter_entries(): if version != self._md._version: raise RuntimeError("Dictionary changed during iteration") - yield self._md._key(k) + yield self._md._key(e.key) def __repr__(self) -> str: lst = [] - for i, k, v in self._md._items: - lst.append(f"'{k}'") + for e in self._md._keys.iter_entries(): + lst.append(f"'{e.key}'") body = ", ".join(lst) return f"<{self.__class__.__name__}({body})>" @@ -311,9 +318,11 @@ class _KeysView(_ViewBase[_V], KeysView[str]): if not isinstance(key, str): continue identity = self._md._identity(key) - for i, k, v in self._md._items: - if i == identity: - ret.add(k) + hash_ = hash(identity) + for slot, idx, e in self._md._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + ret.add(e.key) + break return ret def __rand__(self, other: Iterable[_T]) -> set[_T]: @@ -325,10 +334,8 @@ class _KeysView(_ViewBase[_V], KeysView[str]): for key in it: if not isinstance(key, str): continue - identity = self._md._identity(key) - for i, k, v in self._md._items: - if i == identity: - ret.add(key) + if key in self._md: + ret.add(key) return cast(set[_T], ret) def __or__(self, other: Iterable[_T]) -> set[Union[str, _T]]: @@ -341,11 +348,7 @@ class _KeysView(_ViewBase[_V], KeysView[str]): if not isinstance(key, str): ret.add(key) continue - identity = self._md._identity(key) - for i, k, v in self._md._items: - if i == identity: - break - else: + if key not in self._md: ret.add(key) return ret @@ -362,9 +365,9 @@ class _KeysView(_ViewBase[_V], KeysView[str]): identity = self._md._identity(key) tmp.add(identity) - for i, k, v in self._md._items: - if i not in tmp: - ret.add(k) + for e in self._md._keys.iter_entries(): + if e.identity not in tmp: + ret.add(e.key) return ret def __sub__(self, other: Iterable[object]) -> set[str]: @@ -377,9 +380,10 @@ class _KeysView(_ViewBase[_V], KeysView[str]): if not isinstance(key, str): continue identity = self._md._identity(key) - for i, k, v in self._md._items: - if i == identity: - ret.discard(k) + hash_ = hash(identity) + for slot, idx, e in self._md._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + ret.discard(e.key) break return ret @@ -391,11 +395,8 @@ class _KeysView(_ViewBase[_V], KeysView[str]): for key in other: if not isinstance(key, str): continue - identity = self._md._identity(key) - for i, k, v in self._md._items: - if i == identity: - ret.discard(key) # type: ignore[arg-type] - break + if key in self._md: + ret.discard(key) # type: ignore[arg-type] return ret def __xor__(self, other: Iterable[_T]) -> set[Union[str, _T]]: @@ -413,15 +414,13 @@ class _KeysView(_ViewBase[_V], KeysView[str]): for key in other: if not isinstance(key, str): continue - identity = self._md._identity(key) - for i, k, v in self._md._items: - if i == identity: - return False + if key in self._md: + return False return True class _CSMixin: - _ci: bool = False + _ci: ClassVar[bool] = False def _key(self, key: str) -> str: return key @@ -434,7 +433,7 @@ class _CSMixin: class _CIMixin: - _ci: bool = True + _ci: ClassVar[bool] = True def _key(self, key: str) -> str: if type(key) is istr: @@ -455,15 +454,197 @@ class _CIMixin: raise TypeError("MultiDict keys should be either str or subclasses of str") +def estimate_log2_keysize(n: int) -> int: + # 7 == HT_MINSIZE - 1 + return (((n * 3 + 1) // 2) | 7).bit_length() + + +@dataclass +class _Entry(Generic[_V]): + hash: int + identity: str + key: str + value: _V + + +@dataclass +class _HtKeys(Generic[_V]): # type: ignore[misc] + LOG_MINSIZE: ClassVar[int] = 3 + MINSIZE: ClassVar[int] = 8 + PREALLOCATED_INDICES: ClassVar[dict[int, array]] = { # type: ignore[type-arg] + log2_size: array( + "b" if log2_size < 8 else "h", (-1 for i in range(1 << log2_size)) + ) + for log2_size in range(3, 10) + } + + log2_size: int + usable: int + + indices: array # type: ignore[type-arg] # in py3.9 array is not generic + entries: list[Optional[_Entry[_V]]] + + @functools.cached_property + def nslots(self) -> int: + return 1 << self.log2_size + + @functools.cached_property + def mask(self) -> int: + return self.nslots - 1 + + if sys.implementation.name != "pypy": + + def __sizeof__(self) -> int: + return ( + object.__sizeof__(self) + + sys.getsizeof(self.indices) + + sys.getsizeof(self.entries) + ) + + @classmethod + def new(cls, log2_size: int, entries: list[Optional[_Entry[_V]]]) -> Self: + size = 1 << log2_size + usable = (size << 1) // 3 + if log2_size < 10: + indices = cls.PREALLOCATED_INDICES[log2_size].__copy__() + elif log2_size < 16: + indices = array("h", (-1 for i in range(size))) + elif log2_size < 32: + indices = array("l", (-1 for i in range(size))) + else: # pragma: no cover # don't test huge multidicts + indices = array("q", (-1 for i in range(size))) + ret = cls( + log2_size=log2_size, + usable=usable, + indices=indices, + entries=entries, + ) + return ret + + def clone(self) -> "_HtKeys[_V]": + entries = [ + _Entry(e.hash, e.identity, e.key, e.value) if e is not None else None + for e in self.entries + ] + + return _HtKeys( + log2_size=self.log2_size, + usable=self.usable, + indices=self.indices.__copy__(), + entries=entries, + ) + + def build_indices(self, update: bool) -> None: + mask = self.mask + indices = self.indices + for idx, e in enumerate(self.entries): + assert e is not None + hash_ = e.hash + if update: + if hash_ == -1: + hash_ = hash(e.identity) + else: + assert hash_ != -1 + i = hash_ & mask + perturb = hash_ & sys.maxsize + while indices[i] != -1: + perturb >>= 5 + i = mask & (i * 5 + perturb + 1) + indices[i] = idx + + def find_empty_slot(self, hash_: int) -> int: + mask = self.mask + indices = self.indices + i = hash_ & mask + perturb = hash_ & sys.maxsize + ix = indices[i] + while ix != -1: + perturb >>= 5 + i = (i * 5 + perturb + 1) & mask + ix = indices[i] + return i + + def iter_hash(self, hash_: int) -> Iterator[tuple[int, int, _Entry[_V]]]: + mask = self.mask + indices = self.indices + entries = self.entries + i = hash_ & mask + perturb = hash_ & sys.maxsize + ix = indices[i] + while ix != -1: + if ix != -2: + e = entries[ix] + if e.hash == hash_: + yield i, ix, e + perturb >>= 5 + i = (i * 5 + perturb + 1) & mask + ix = indices[i] + + def del_idx(self, hash_: int, idx: int) -> None: + mask = self.mask + indices = self.indices + i = hash_ & mask + perturb = hash_ & sys.maxsize + ix = indices[i] + while ix != idx: + perturb >>= 5 + i = (i * 5 + perturb + 1) & mask + ix = indices[i] + indices[i] = -2 + + def iter_entries(self) -> Iterator[_Entry[_V]]: + return filter(None, self.entries) + + def restore_hash(self, hash_: int) -> None: + mask = self.mask + indices = self.indices + entries = self.entries + i = hash_ & mask + perturb = hash_ & sys.maxsize + ix = indices[i] + while ix != -1: + if ix != -2: + entry = entries[ix] + if entry.hash == -1: + entry.hash = hash_ + perturb >>= 5 + i = (i * 5 + perturb + 1) & mask + ix = indices[i] + + class MultiDict(_CSMixin, MutableMultiMapping[_V]): """Dictionary with the support for duplicate keys.""" + __slots__ = ("_keys", "_used", "_version") + def __init__(self, arg: MDArg[_V] = None, /, **kwargs: _V): - self._items: list[tuple[str, str, _V]] = [] + self._used = 0 v = _version v[0] += 1 self._version = v[0] - self._extend(arg, kwargs, self.__class__.__name__, self._extend_items) + if not kwargs: + md = None + if isinstance(arg, MultiDictProxy): + md = arg._md + elif isinstance(arg, MultiDict): + md = arg + if md is not None and md._ci is self._ci: + self._from_md(md) + return + + items = self._parse_args(arg, kwargs) + log2_size = estimate_log2_keysize(len(items)) + if log2_size > 17: # pragma: no cover + # Don't overallocate really huge keys space in init + log2_size = 17 + self._keys: _HtKeys[_V] = _HtKeys.new(log2_size, []) + self._extend_items(items) + + def _from_md(self, md: "MultiDict[_V]") -> None: + # Copy everything as-is without compacting the new multidict, + # otherwise it requires reindexing + self._keys = md._keys.clone() + self._used = md._used @overload def getall(self, key: str) -> list[_V]: ... @@ -474,7 +655,15 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): ) -> Union[list[_V], _T]: """Return a list of all values matching the key.""" identity = self._identity(key) - res = [v for i, k, v in self._items if i == identity] + hash_ = hash(identity) + res = [] + + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + res.append(e.value) + e.hash = -1 + self._keys.restore_hash(hash_) + if res: return res if not res and default is not sentinel: @@ -493,9 +682,10 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): Raises KeyError if the key is not found and no default is provided. """ identity = self._identity(key) - for i, k, v in self._items: - if i == identity: - return v + hash_ = hash(identity) + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + return e.value if default is not sentinel: return default raise KeyError("Key not found: %r" % key) @@ -520,7 +710,7 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): return iter(self.keys()) def __len__(self) -> int: - return len(self._items) + return self._used def keys(self) -> KeysView[str]: """Return a new view of the dictionary's keys.""" @@ -540,15 +730,15 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): if isinstance(other, MultiDictProxy): return self == other._md if isinstance(other, MultiDict): - lft = self._items - rht = other._items - if len(lft) != len(rht): + lft = self._keys + rht = other._keys + if self._used != other._used: return False - for (i1, k2, v1), (i2, k2, v2) in zip(lft, rht): - if i1 != i2 or v1 != v2: + for e1, e2 in zip(lft.iter_entries(), rht.iter_entries()): + if e1.identity != e2.identity or e1.value != e2.value: return False return True - if len(self._items) != len(other): + if self._used != len(other): return False for k, v in self.items(): nv = other.get(k, sentinel) @@ -560,33 +750,35 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): if not isinstance(key, str): return False identity = self._identity(key) - for i, k, v in self._items: - if i == identity: + hash_ = hash(identity) + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch return True return False @reprlib.recursive_repr() def __repr__(self) -> str: - body = ", ".join(f"'{k}': {v!r}" for i, k, v in self._items) + body = ", ".join(f"'{e.key}': {e.value!r}" for e in self._keys.iter_entries()) return f"<{self.__class__.__name__}({body})>" if sys.implementation.name != "pypy": def __sizeof__(self) -> int: - return object.__sizeof__(self) + sys.getsizeof(self._items) + return object.__sizeof__(self) + sys.getsizeof(self._keys) def __reduce__(self) -> tuple[type[Self], tuple[list[tuple[str, _V]]]]: return (self.__class__, (list(self.items()),)) def add(self, key: str, value: _V) -> None: identity = self._identity(key) - self._items.append((identity, key, value)) + hash_ = hash(identity) + self._add_with_hash(_Entry(hash_, identity, key, value)) self._incr_version() def copy(self) -> Self: """Return a copy of itself.""" cls = self.__class__ - return cls(self.items()) + return cls(self) __copy__ = copy @@ -595,28 +787,35 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): This method must be used instead of update. """ - self._extend(arg, kwargs, "extend", self._extend_items) + items = self._parse_args(arg, kwargs) + newsize = self._used + len(items) + self._resize(estimate_log2_keysize(newsize), False) + self._extend_items(items) - def _extend( + def _parse_args( self, arg: MDArg[_V], kwargs: Mapping[str, _V], - name: str, - method: Callable[[list[tuple[str, str, _V]]], None], - ) -> None: + ) -> list[_Entry[_V]]: + identity_func = self._identity if arg: if isinstance(arg, MultiDictProxy): arg = arg._md if isinstance(arg, MultiDict): if self._ci is not arg._ci: - items = [(self._identity(k), k, v) for _, k, v in arg._items] + items = [] + for e in arg._keys.iter_entries(): + identity = identity_func(e.key) + items.append(_Entry(hash(identity), identity, e.key, e.value)) else: - items = arg._items - if kwargs: - items = items.copy() + items = [ + _Entry(e.hash, e.identity, e.key, e.value) + for e in arg._keys.iter_entries() + ] if kwargs: for key, value in kwargs.items(): - items.append((self._identity(key), key, value)) + identity = identity_func(key) + items.append(_Entry(hash(identity), identity, key, value)) else: if hasattr(arg, "keys"): arg = cast(SupportsKeys[_V], arg) @@ -631,34 +830,57 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): f"multidict update sequence element #{pos}" f"has length {len(item)}; 2 is required" ) - items.append((self._identity(item[0]), item[0], item[1])) - - method(items) + identity = identity_func(item[0]) + items.append(_Entry(hash(identity), identity, item[0], item[1])) else: - method([(self._identity(key), key, value) for key, value in kwargs.items()]) + items = [] + for key, value in kwargs.items(): + identity = identity_func(key) + items.append(_Entry(hash(identity), identity, key, value)) + + return items - def _extend_items(self, items: Iterable[tuple[str, str, _V]]) -> None: - for identity, key, value in items: - self._items.append((identity, key, value)) + def _extend_items(self, items: Iterable[_Entry[_V]]) -> None: + for e in items: + self._add_with_hash(e) self._incr_version() def clear(self) -> None: """Remove all items from MultiDict.""" - self._items.clear() + self._used = 0 + self._keys = _HtKeys.new(_HtKeys.LOG_MINSIZE, []) self._incr_version() # Mapping interface # def __setitem__(self, key: str, value: _V) -> None: - self._replace(key, value) + identity = self._identity(key) + hash_ = hash(identity) + found = False + + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + if not found: + e.key = key + e.value = value + e.hash = -1 + found = True + self._incr_version() + elif e.hash != -1: # pragma: no branch + self._del_at(slot, idx) + + if not found: + self._add_with_hash(_Entry(hash_, identity, key, value)) + else: + self._keys.restore_hash(hash_) def __delitem__(self, key: str) -> None: - identity = self._identity(key) - items = self._items found = False - for i in range(len(items) - 1, -1, -1): - if items[i][0] == identity: - del items[i] + identity = self._identity(key) + hash_ = hash(identity) + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + self._del_at(slot, idx) found = True if not found: raise KeyError(key) @@ -674,9 +896,10 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): def setdefault(self, key: str, default: Union[_V, None] = None) -> Union[_V, None]: # type: ignore[misc] """Return value for key, set value to default if key is not present.""" identity = self._identity(key) - for i, k, v in self._items: - if i == identity: - return v + hash_ = hash(identity) + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + return e.value self.add(key, default) # type: ignore[arg-type] return default @@ -694,10 +917,11 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): """ identity = self._identity(key) - for i in range(len(self._items)): - if self._items[i][0] == identity: - value = self._items[i][2] - del self._items[i] + hash_ = hash(identity) + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + value = e.value + self._del_at(slot, idx) self._incr_version() return value if default is sentinel: @@ -725,100 +949,140 @@ class MultiDict(_CSMixin, MutableMultiMapping[_V]): """ found = False identity = self._identity(key) + hash_ = hash(identity) ret = [] - for i in range(len(self._items) - 1, -1, -1): - item = self._items[i] - if item[0] == identity: - ret.append(item[2]) - del self._items[i] - self._incr_version() + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch found = True + ret.append(e.value) + self._del_at(slot, idx) + self._incr_version() + if not found: if default is sentinel: raise KeyError(key) else: return default else: - ret.reverse() return ret def popitem(self) -> tuple[str, _V]: """Remove and return an arbitrary (key, value) pair.""" - if self._items: - i, k, v = self._items.pop() - self._incr_version() - return self._key(k), v - else: + if self._used <= 0: raise KeyError("empty multidict") - def update(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None: - """Update the dictionary from *other*, overwriting existing keys.""" - self._extend(arg, kwargs, "update", self._update_items) + pos = len(self._keys.entries) - 1 + entry = self._keys.entries.pop() - def _update_items(self, items: list[tuple[str, str, _V]]) -> None: - if not items: - return - used_keys: dict[str, int] = {} - for identity, key, value in items: - start = used_keys.get(identity, 0) - for i in range(start, len(self._items)): - item = self._items[i] - if item[0] == identity: - used_keys[identity] = i + 1 - self._items[i] = (identity, key, value) - break - else: - self._items.append((identity, key, value)) - used_keys[identity] = len(self._items) - - # drop tails - i = 0 - while i < len(self._items): - item = self._items[i] - identity = item[0] - pos = used_keys.get(identity) - if pos is None: - i += 1 - continue - if i >= pos: - del self._items[i] - else: - i += 1 + while entry is None: + pos -= 1 + entry = self._keys.entries.pop() + ret = self._key(entry.key), entry.value + self._keys.del_idx(entry.hash, pos) + self._used -= 1 self._incr_version() + return ret - def _replace(self, key: str, value: _V) -> None: - identity = self._identity(key) - items = self._items - - for i in range(len(items)): - item = items[i] - if item[0] == identity: - items[i] = (identity, key, value) - # i points to last found item - rgt = i - self._incr_version() - break - else: - self._items.append((identity, key, value)) - self._incr_version() + def update(self, arg: MDArg[_V] = None, /, **kwargs: _V) -> None: + """Update the dictionary from *other*, overwriting existing keys.""" + items = self._parse_args(arg, kwargs) + newsize = self._used + len(items) + log2_size = estimate_log2_keysize(newsize) + if log2_size > 17: # pragma: no cover + # Don't overallocate really huge keys space in update, + # duplicate keys could reduce the resulting anount of entries + log2_size = 17 + if log2_size > self._keys.log2_size: + self._resize(log2_size, False) + self._update_items(items) + + def _update_items(self, items: list[_Entry[_V]]) -> None: + if not items: return + for entry in items: + found = False + hash_ = entry.hash + identity = entry.identity + for slot, idx, e in self._keys.iter_hash(hash_): + if e.identity == identity: # pragma: no branch + if not found: + found = True + e.key = entry.key + e.value = entry.value + e.hash = -1 + else: + self._del_at_for_upd(e) + if not found: + self._add_with_hash_for_upd(entry) + + keys = self._keys + indices = keys.indices + entries = keys.entries + for slot in range(keys.nslots): + idx = indices[slot] + if idx >= 0: + e2 = entries[idx] + assert e2 is not None + if e2.key is None: + entries[idx] = None # type: ignore[unreachable] + indices[slot] = -2 + self._used -= 1 + if e2.hash == -1: + e2.hash = hash(e2.identity) - # remove all tail items - # Mypy bug: https://github.com/python/mypy/issues/14209 - i = rgt + 1 # type: ignore[possibly-undefined] - while i < len(items): - item = items[i] - if item[0] == identity: - del items[i] - else: - i += 1 + self._incr_version() def _incr_version(self) -> None: v = _version v[0] += 1 self._version = v[0] + def _resize(self, log2_newsize: int, update: bool) -> None: + oldkeys = self._keys + newentries = self._used + + if len(oldkeys.entries) == newentries: + entries = oldkeys.entries + else: + entries = [e for e in oldkeys.entries if e is not None] + newkeys: _HtKeys[_V] = _HtKeys.new(log2_newsize, entries) + newkeys.usable -= newentries + newkeys.build_indices(update) + self._keys = newkeys + + def _add_with_hash(self, entry: _Entry[_V]) -> None: + if self._keys.usable <= 0: + self._resize((self._used * 3 | _HtKeys.MINSIZE - 1).bit_length(), False) + keys = self._keys + slot = keys.find_empty_slot(entry.hash) + keys.indices[slot] = len(keys.entries) + keys.entries.append(entry) + self._incr_version() + self._used += 1 + keys.usable -= 1 + + def _add_with_hash_for_upd(self, entry: _Entry[_V]) -> None: + if self._keys.usable <= 0: + self._resize((self._used * 3 | _HtKeys.MINSIZE - 1).bit_length(), True) + keys = self._keys + slot = keys.find_empty_slot(entry.hash) + keys.indices[slot] = len(keys.entries) + entry.hash = -1 + keys.entries.append(entry) + self._incr_version() + self._used += 1 + keys.usable -= 1 + + def _del_at(self, slot: int, idx: int) -> None: + self._keys.entries[idx] = None + self._keys.indices[slot] = -2 + self._used -= 1 + + def _del_at_for_upd(self, entry: _Entry[_V]) -> None: + entry.key = None # type: ignore[assignment] + entry.value = None # type: ignore[assignment] + class CIMultiDict(_CIMixin, MultiDict[_V]): """Dictionary with the support for duplicate case-insensitive keys.""" @@ -827,13 +1091,14 @@ class CIMultiDict(_CIMixin, MultiDict[_V]): class MultiDictProxy(_CSMixin, MultiMapping[_V]): """Read-only proxy for MultiDict instance.""" + __slots__ = ("_md",) + _md: MultiDict[_V] def __init__(self, arg: Union[MultiDict[_V], "MultiDictProxy[_V]"]): if not isinstance(arg, (MultiDict, MultiDictProxy)): raise TypeError( - "ctor requires MultiDict or MultiDictProxy instance" - f", not {type(arg)}" + f"ctor requires MultiDict or MultiDictProxy instance, not {type(arg)}" ) if isinstance(arg, MultiDictProxy): self._md = arg._md @@ -919,7 +1184,7 @@ class MultiDictProxy(_CSMixin, MultiMapping[_V]): def copy(self) -> MultiDict[_V]: """Return a copy of itself.""" - return MultiDict(self.items()) + return MultiDict(self._md) class CIMultiDictProxy(_CIMixin, MultiDictProxy[_V]): @@ -936,7 +1201,7 @@ class CIMultiDictProxy(_CIMixin, MultiDictProxy[_V]): def copy(self) -> CIMultiDict[_V]: """Return a copy of itself.""" - return CIMultiDict(self.items()) + return CIMultiDict(self._md) def getversion(md: Union[MultiDict[object], MultiDictProxy[object]]) -> int: diff --git a/contrib/python/multidict/multidict/_multilib/dict.h b/contrib/python/multidict/multidict/_multilib/dict.h index fa07fdf4ac3..2f7c83be2e2 100644 --- a/contrib/python/multidict/multidict/_multilib/dict.h +++ b/contrib/python/multidict/multidict/_multilib/dict.h @@ -5,20 +5,26 @@ extern "C" { #endif +#include "htkeys.h" #include "pythoncapi_compat.h" -#include "pair_list.h" +#include "state.h" #if PY_VERSION_HEX >= 0x030c00f0 #define MANAGED_WEAKREFS #endif - -typedef struct { // 16 or 24 for GC prefix - PyObject_HEAD // 16 +typedef struct { + PyObject_HEAD #ifndef MANAGED_WEAKREFS PyObject *weaklist; #endif - pair_list_t pairs; + mod_state *state; + Py_ssize_t used; + + uint64_t version; + bool is_ci; + + htkeys_t *keys; } MultiDictObject; typedef struct { @@ -29,7 +35,6 @@ typedef struct { MultiDictObject *md; } MultiDictProxyObject; - #ifdef __cplusplus } #endif diff --git a/contrib/python/multidict/multidict/_multilib/hashtable.h b/contrib/python/multidict/multidict/_multilib/hashtable.h new file mode 100644 index 00000000000..53d39a58cda --- /dev/null +++ b/contrib/python/multidict/multidict/_multilib/hashtable.h @@ -0,0 +1,1890 @@ +#include "pythoncapi_compat.h" + +#ifndef _MULTIDICT_HASHTABLE_H +#define _MULTIDICT_HASHTABLE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include "dict.h" +#include "htkeys.h" +#include "istr.h" +#include "state.h" + +typedef struct _md_pos { + Py_ssize_t pos; + uint64_t version; +} md_pos_t; + +typedef struct _md_finder { + MultiDictObject *md; + htkeysiter_t iter; + uint64_t version; + Py_hash_t hash; + PyObject *identity; // borrowed ref +} md_finder_t; + +/* +The multidict's implementation is close to Python's dict except for multiple +keys. + +It starts from the empty hashtable, which grows by a power of 2 starting from +8: 8, 16, 32, 64, 128, ... The amount of items is 2/3 of the hashtable size +(1/3 of the table is never allocated). + +The table is resized if needed, and bulk updates (extend(), update(), and +constructor calls) pre-allocate many items at once, reducing the amount of +potential hashtable resizes. + +Item deletion puts DKIX_DUMMY special index in the hashtable. In opposite to +the standard dict, DKIX_DUMMY is never replaced with an index of the new entry +except by hashtable indices rebuild. It allows to keep the insertion order for +multiple equal keys. The index table rebuild happens on the keys table size +changeing and if the number of DKIX_DUMMY slots grows to 1/4 of the total +amount. + +The iteration for operations like getall() is a little tricky. The next index +calculation could return the already visited index before reaching the end. To +eliminate duplicates, the code marks already visited entries by entry->hash = +-1. -1 hash is an invalid hash value that could be used as a marker. After the +iteration finishes, all marked entries are restored. Double iteration over the +indices still has O(1) amortized time, it is ok. + +`.add()`, `val = md[key]`, `md[key] = val`, `md.setdefault()` all have O(1). +`.getall()` / `.popall()` have O(N) where N is the amount of returned items. +`.update()` / `extend()` have O(N+M) where N and M are amount of items +in the left and right arguments. + +`.copy()` and constuction from multidict is super fast. +*/ + +/* GROWTH_RATE. Growth rate upon hitting maximum load. + * Currently set to used*3. + * This means that dicts double in size when growing without deletions, + * but have more head room when the number of deletions is on a par with the + * number of insertions. See also bpo-17563 and bpo-33205. + * + * GROWTH_RATE was set to used*4 up to version 3.2. + * GROWTH_RATE was set to used*2 in version 3.3.0 + * GROWTH_RATE was set to used*2 + capacity/2 in 3.4.0-3.6.0. + */ +static inline Py_ssize_t +GROWTH_RATE(MultiDictObject *md) +{ + return md->used * 3; +} + +static inline int +_md_check_consistency(MultiDictObject *md, bool update); +static inline int +_md_dump(MultiDictObject *md); + +#ifndef NDEBUG +#define ASSERT_CONSISTENT(md, update) assert(_md_check_consistency(md, update)) +#else +#define ASSERT_CONSISTENT(md, update) assert(1) +#endif + +static inline int +_str_cmp(PyObject *s1, PyObject *s2) +{ + PyObject *ret = PyUnicode_RichCompare(s1, s2, Py_EQ); + if (Py_IsTrue(ret)) { + Py_DECREF(ret); + return 1; + } else if (ret == NULL) { + return -1; + } else { + Py_DECREF(ret); + return 0; + } +} + +static inline PyObject * +_key_to_identity(mod_state *state, PyObject *key) +{ + if (IStr_Check(state, key)) { + return Py_NewRef(((istrobject *)key)->canonical); + } + if (PyUnicode_CheckExact(key)) { + return Py_NewRef(key); + } + if (PyUnicode_Check(key)) { + return PyUnicode_FromObject(key); + } + PyErr_SetString(PyExc_TypeError, + "MultiDict keys should be either str " + "or subclasses of str"); + return NULL; +} + +static inline PyObject * +_ci_key_to_identity(mod_state *state, PyObject *key) +{ + if (IStr_Check(state, key)) { + return Py_NewRef(((istrobject *)key)->canonical); + } + if (PyUnicode_Check(key)) { + PyObject *ret = PyObject_CallMethodNoArgs(key, state->str_lower); + if (ret == NULL) { + goto fail; + } + if (!PyUnicode_CheckExact(ret)) { + PyObject *tmp = PyUnicode_FromObject(ret); + Py_CLEAR(ret); + if (tmp == NULL) { + return NULL; + } + ret = tmp; + } + return ret; + } +fail: + PyErr_SetString(PyExc_TypeError, + "CIMultiDict keys should be either str " + "or subclasses of str"); + return NULL; +} + +static inline PyObject * +_arg_to_key(mod_state *state, PyObject *key, PyObject *identity) +{ + if (PyUnicode_Check(key)) { + return Py_NewRef(key); + } + PyErr_SetString(PyExc_TypeError, + "MultiDict keys should be either str " + "or subclasses of str"); + return NULL; +} + +static inline PyObject * +_ci_arg_to_key(mod_state *state, PyObject *key, PyObject *identity) +{ + if (IStr_Check(state, key)) { + return Py_NewRef(key); + } + if (PyUnicode_Check(key)) { + return IStr_New(state, key, identity); + } + PyErr_SetString(PyExc_TypeError, + "CIMultiDict keys should be either str " + "or subclasses of str"); + return NULL; +} + +static inline int +_md_resize(MultiDictObject *md, uint8_t log2_newsize, bool update) +{ + htkeys_t *oldkeys, *newkeys; + + if (log2_newsize >= SIZEOF_SIZE_T * 8) { + PyErr_NoMemory(); + return -1; + } + assert(log2_newsize >= HT_LOG_MINSIZE); + + oldkeys = md->keys; + + /* Allocate a new table. */ + newkeys = htkeys_new(log2_newsize); + assert(newkeys); + if (newkeys == NULL) { + return -1; + } + // New table must be large enough. + assert(newkeys->usable >= md->used); + + Py_ssize_t numentries = md->used; + entry_t *oldentries = htkeys_entries(oldkeys); + entry_t *newentries = htkeys_entries(newkeys); + if (oldkeys->nentries == numentries) { + memcpy(newentries, oldentries, numentries * sizeof(entry_t)); + } else { + entry_t *new_ep = newentries; + entry_t *old_ep = oldentries; + for (Py_ssize_t i = 0; i < oldkeys->nentries; ++i, ++old_ep) { + if (old_ep->identity != NULL) { + *new_ep++ = *old_ep; + } + } + } + + if (htkeys_build_indices(newkeys, newentries, numentries, update) < 0) { + return -1; + } + + md->keys = newkeys; + + if (oldkeys != &empty_htkeys) { + htkeys_free(oldkeys); + } + + md->keys->usable = md->keys->usable - numentries; + md->keys->nentries = numentries; + ASSERT_CONSISTENT(md, update); + return 0; +} + +static inline int +_md_resize_for_insert(MultiDictObject *md) +{ + return _md_resize(md, calculate_log2_keysize(GROWTH_RATE(md)), false); +} + +static inline int +_md_resize_for_update(MultiDictObject *md) +{ + return _md_resize(md, calculate_log2_keysize(GROWTH_RATE(md)), true); +} + +static inline int +_md_reserve(MultiDictObject *md, Py_ssize_t extra_size, bool update) +{ + uint8_t new_size = estimate_log2_keysize(extra_size + md->used); + if (new_size > md->keys->log2_size) { + return _md_resize(md, new_size, update); + } + return 0; +} + +static inline int +md_reserve(MultiDictObject *md, Py_ssize_t extra_size) +{ + return _md_reserve(md, extra_size, false); +} + +static inline int +md_init(MultiDictObject *md, mod_state *state, bool is_ci, Py_ssize_t minused) +{ + md->state = state; + md->is_ci = is_ci; + md->used = 0; + md->version = NEXT_VERSION(md->state); + + const uint8_t log2_max_presize = 17; + const Py_ssize_t max_presize = ((Py_ssize_t)1) << log2_max_presize; + uint8_t log2_newsize; + htkeys_t *new_keys; + + if (minused <= USABLE_FRACTION(HT_MINSIZE)) { + md->keys = &empty_htkeys; + ASSERT_CONSISTENT(md, false); + return 0; + } + /* There are no strict guarantee that returned dict can contain minused + * items without resize. So we create medium size dict instead of very + * large dict or MemoryError. + */ + if (minused > USABLE_FRACTION(max_presize)) { + log2_newsize = log2_max_presize; + } else { + log2_newsize = estimate_log2_keysize(minused); + } + + new_keys = htkeys_new(log2_newsize); + if (new_keys == NULL) return -1; + md->keys = new_keys; + ASSERT_CONSISTENT(md, false); + return 0; +} + +static inline int +md_clone_from_ht(MultiDictObject *md, MultiDictObject *other) +{ + ASSERT_CONSISTENT(other, false); + md->state = other->state; + md->used = other->used; + md->version = other->version; + md->is_ci = other->is_ci; + if (other->keys != &empty_htkeys) { + size_t size = htkeys_sizeof(other->keys); + htkeys_t *keys = PyMem_Malloc(size); + if (keys == NULL) { + PyErr_NoMemory(); + return -1; + } + memcpy(keys, other->keys, size); + entry_t *entry = htkeys_entries(keys); + for (Py_ssize_t idx = 0; idx < keys->nentries; idx++, entry++) { + Py_XINCREF(entry->identity); + Py_XINCREF(entry->key); + Py_XINCREF(entry->value); + } + md->keys = keys; + } else { + md->keys = &empty_htkeys; + } + ASSERT_CONSISTENT(md, false); + return 0; +} + +static inline PyObject * +md_calc_identity(MultiDictObject *md, PyObject *key) +{ + if (md->is_ci) return _ci_key_to_identity(md->state, key); + return _key_to_identity(md->state, key); +} + +static inline PyObject * +md_calc_key(MultiDictObject *md, PyObject *key, PyObject *identity) +{ + if (md->is_ci) return _ci_arg_to_key(md->state, key, identity); + return _arg_to_key(md->state, key, identity); +} + +static inline Py_ssize_t +md_len(MultiDictObject *md) +{ + return md->used; +} + +static inline PyObject * +_md_ensure_key(MultiDictObject *md, entry_t *entry) +{ + assert(entry >= htkeys_entries(md->keys)); + assert(entry < htkeys_entries(md->keys) + md->keys->nentries); + PyObject *key = md_calc_key(md, entry->key, entry->identity); + if (key == NULL) { + return NULL; + } + if (key != entry->key) { + Py_SETREF(entry->key, key); + } else { + Py_CLEAR(key); + } + return Py_NewRef(entry->key); +} + +static inline int +_md_add_with_hash_steal_refs(MultiDictObject *md, Py_hash_t hash, + PyObject *identity, PyObject *key, + PyObject *value) +{ + htkeys_t *keys = md->keys; + if (keys->usable <= 0 || keys == &empty_htkeys) { + /* Need to resize. */ + if (_md_resize_for_insert(md) < 0) { + return -1; + } + keys = md->keys; // updated by resizing + } + + Py_ssize_t hashpos = htkeys_find_empty_slot(keys, hash); + htkeys_set_index(keys, hashpos, keys->nentries); + + entry_t *entry = htkeys_entries(keys) + keys->nentries; + + entry->identity = identity; + entry->key = key; + entry->value = value; + entry->hash = hash; + + md->version = NEXT_VERSION(md->state); + md->used += 1; + keys->usable -= 1; + keys->nentries += 1; + return 0; +} + +static inline int +_md_add_with_hash(MultiDictObject *md, Py_hash_t hash, PyObject *identity, + PyObject *key, PyObject *value) +{ + Py_INCREF(identity); + Py_INCREF(key); + Py_INCREF(value); + return _md_add_with_hash_steal_refs(md, hash, identity, key, value); +} + +static inline int +_md_add_for_upd_steal_refs(MultiDictObject *md, Py_hash_t hash, + PyObject *identity, PyObject *key, PyObject *value) +{ + htkeys_t *keys = md->keys; + if (keys->usable <= 0 || keys == &empty_htkeys) { + /* Need to resize. */ + if (_md_resize_for_update(md) < 0) { + return -1; + } + keys = md->keys; // updated by resizing + } + Py_ssize_t hashpos = htkeys_find_empty_slot(keys, hash); + htkeys_set_index(keys, hashpos, keys->nentries); + + entry_t *entry = htkeys_entries(keys) + keys->nentries; + + entry->identity = identity; + entry->key = key; + entry->value = value; + entry->hash = -1; + + md->version = NEXT_VERSION(md->state); + md->used += 1; + keys->usable -= 1; + keys->nentries += 1; + return 0; +} + +static inline int +_md_add_for_upd(MultiDictObject *md, Py_hash_t hash, PyObject *identity, + PyObject *key, PyObject *value) +{ + Py_INCREF(identity); + Py_INCREF(key); + Py_INCREF(value); + return _md_add_for_upd_steal_refs(md, hash, identity, key, value); +} + +static inline int +md_add(MultiDictObject *md, PyObject *key, PyObject *value) +{ + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + int ret = _md_add_with_hash(md, hash, identity, key, value); + ASSERT_CONSISTENT(md, false); + Py_DECREF(identity); + return ret; +fail: + Py_XDECREF(identity); + return -1; +} + +static inline int +_md_del_at(MultiDictObject *md, size_t slot, entry_t *entry) +{ + htkeys_t *keys = md->keys; + assert(keys != &empty_htkeys); + Py_CLEAR(entry->identity); + Py_CLEAR(entry->key); + Py_CLEAR(entry->value); + htkeys_set_index(keys, slot, DKIX_DUMMY); + md->used -= 1; + return 0; +} + +static inline int +_md_del_at_for_upd(MultiDictObject *md, size_t slot, entry_t *entry) +{ + /* half deletion, + the entry could be replaced later with key and value set + or it will be finally cleaned up with identity=NULL, + used -= 1, and setting the hash to DKIX_DUMMY + in md_post_update() + */ + assert(md->keys != &empty_htkeys); + Py_CLEAR(entry->key); + Py_CLEAR(entry->value); + return 0; +} + +static inline int +md_del(MultiDictObject *md, PyObject *key) +{ + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + + bool found = false; + + htkeysiter_t iter; + htkeysiter_init(&iter, md->keys, hash); + + entry_t *entries = htkeys_entries(md->keys); + + for (; iter.index != DKIX_EMPTY; htkeysiter_next(&iter)) { + if (iter.index < 0) { + continue; + } + entry_t *entry = entries + iter.index; + if (hash != entry->hash) { + continue; + } + int tmp = _str_cmp(entry->identity, identity); + if (tmp < 0) { + goto fail; + } + if (tmp == 0) { + continue; + } + + found = true; + if (_md_del_at(md, iter.slot, entry) < 0) { + goto fail; + } + } + + if (!found) { + PyErr_SetObject(PyExc_KeyError, key); + goto fail; + } else { + md->version = NEXT_VERSION(md->state); + } + Py_DECREF(identity); + ASSERT_CONSISTENT(md, false); + return 0; +fail: + Py_XDECREF(identity); + return -1; +} + +static inline uint64_t +md_version(MultiDictObject *md) +{ + return md->version; +} + +static inline void +md_init_pos(MultiDictObject *md, md_pos_t *pos) +{ + pos->pos = 0; + pos->version = md->version; +} + +static inline int +md_next(MultiDictObject *md, md_pos_t *pos, PyObject **pidentity, + PyObject **pkey, PyObject **pvalue) +{ + int ret = 0; + + if (pos->version != md->version) { + PyErr_SetString(PyExc_RuntimeError, + "MultiDict is changed during iteration"); + ret = -1; + goto cleanup; + } + + if (pos->pos >= md->keys->nentries) { + goto cleanup; + } + + entry_t *entries = htkeys_entries(md->keys); + entry_t *entry = entries + pos->pos; + + while (entry->identity == NULL) { + pos->pos += 1; + if (pos->pos >= md->keys->nentries) { + goto cleanup; + } + entry += 1; + } + + if (pidentity) { + *pidentity = Py_NewRef(entry->identity); + } + + if (pkey) { + assert(entry->key != NULL); + *pkey = _md_ensure_key(md, entry); + if (*pkey == NULL) { + assert(PyErr_Occurred()); + ret = -1; + goto cleanup; + } + } + if (pvalue) { + *pvalue = Py_NewRef(entry->value); + } + + ++pos->pos; + return 1; +cleanup: + if (pidentity) { + *pidentity = NULL; + } + if (pkey) { + *pkey = NULL; + } + if (pvalue) { + *pvalue = NULL; + } + return ret; +} + +static inline int +md_init_finder(MultiDictObject *md, PyObject *identity, md_finder_t *finder) +{ + finder->version = md->version; + finder->md = md; + finder->identity = identity; + finder->hash = _unicode_hash(identity); + if (finder->hash == -1) { + return -1; + } + htkeysiter_init(&finder->iter, finder->md->keys, finder->hash); + return 0; +} + +static inline Py_ssize_t +md_finder_slot(md_finder_t *finder) +{ + assert(finder->md != NULL); + return finder->iter.slot; +} + +static inline Py_ssize_t +md_finder_index(md_finder_t *finder) +{ + assert(finder->md != NULL); + assert(finder->iter.index >= 0); + return finder->iter.index; +} + +static inline int +md_find_next(md_finder_t *finder, PyObject **pkey, PyObject **pvalue) +{ + int ret = 0; + assert(finder->iter.keys == finder->md->keys); + if (finder->iter.keys != finder->md->keys || + finder->version != finder->md->version) { + ret = -1; + PyErr_SetString(PyExc_RuntimeError, + "MultiDict is changed during iteration"); + goto cleanup; + } + + entry_t *entries = htkeys_entries(finder->md->keys); + + for (; finder->iter.index != DKIX_EMPTY; htkeysiter_next(&finder->iter)) { + if (finder->iter.index < 0) { + continue; + } + entry_t *entry = entries + finder->iter.index; + if (entry->hash != finder->hash) { + continue; + } + int tmp = _str_cmp(finder->identity, entry->identity); + if (tmp < 0) { + ret = -1; + goto cleanup; + } + if (tmp == 0) { + continue; + } + + /* found, mark the entry as visited */ + entry->hash = -1; + + if (pkey) { + *pkey = _md_ensure_key(finder->md, entry); + if (*pkey == NULL) { + ret = -1; + goto cleanup; + } + } + if (pvalue) { + *pvalue = Py_NewRef(entry->value); + } + return 1; + } + ret = 0; +cleanup: + if (pkey) { + *pkey = NULL; + } + if (pvalue) { + *pvalue = NULL; + } + return ret; +} + +static inline void +md_finder_cleanup(md_finder_t *finder) +{ + if (finder->md == NULL) { + return; + } + + htkeysiter_init(&finder->iter, finder->md->keys, finder->hash); + entry_t *entries = htkeys_entries(finder->md->keys); + for (; finder->iter.index != DKIX_EMPTY; htkeysiter_next(&finder->iter)) { + if (finder->iter.index < 0) { + continue; + } + entry_t *entry = entries + finder->iter.index; + if (entry->hash == -1) { + entry->hash = finder->hash; + } + } + ASSERT_CONSISTENT(finder->md, false); + finder->md = NULL; +} + +static inline int +md_contains(MultiDictObject *md, PyObject *key, PyObject **pret) +{ + if (!PyUnicode_Check(key)) { + return 0; + } + + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + + htkeysiter_t iter; + htkeysiter_init(&iter, md->keys, hash); + entry_t *entries = htkeys_entries(md->keys); + + for (; iter.index != DKIX_EMPTY; htkeysiter_next(&iter)) { + if (iter.index < 0) { + continue; + } + entry_t *entry = entries + iter.index; + if (hash != entry->hash) { + continue; + } + int tmp = _str_cmp(identity, entry->identity); + if (tmp > 0) { + Py_DECREF(identity); + if (pret != NULL) { + *pret = _md_ensure_key(md, entry); + if (*pret == NULL) { + goto fail; + } + } + return 1; + } else if (tmp < 0) { + goto fail; + } + } + + Py_DECREF(identity); + if (pret != NULL) { + *pret = NULL; + } + return 0; +fail: + Py_XDECREF(identity); + if (pret != NULL) { + *pret = NULL; + } + return -1; +} + +static inline int +md_get_one(MultiDictObject *md, PyObject *key, PyObject **ret) +{ + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + + htkeysiter_t iter; + htkeysiter_init(&iter, md->keys, hash); + entry_t *entries = htkeys_entries(md->keys); + + for (; iter.index != DKIX_EMPTY; htkeysiter_next(&iter)) { + if (iter.index < 0) { + continue; + } + entry_t *entry = entries + iter.index; + if (hash != entry->hash) { + continue; + } + int tmp = _str_cmp(identity, entry->identity); + if (tmp > 0) { + Py_DECREF(identity); + *ret = Py_NewRef(entry->value); + return 1; + } else if (tmp < 0) { + goto fail; + } + } + + Py_DECREF(identity); + return 0; +fail: + Py_XDECREF(identity); + return -1; +} + +static inline int +md_get_all(MultiDictObject *md, PyObject *key, PyObject **ret) +{ + *ret = NULL; + + md_finder_t finder = {0}; + + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + if (md_init_finder(md, identity, &finder) < 0) { + assert(PyErr_Occurred()); + goto fail; + } + + int tmp; + PyObject *value = NULL; + + while ((tmp = md_find_next(&finder, NULL, &value)) > 0) { + if (*ret == NULL) { + *ret = PyList_New(1); + if (*ret == NULL) { + goto fail; + } + PyList_SET_ITEM(*ret, 0, value); + value = NULL; // stealed by PyList_SET_ITEM + } else { + if (PyList_Append(*ret, value) < 0) { + goto fail; + } + Py_CLEAR(value); + } + } + if (tmp < 0) { + goto fail; + } + + md_finder_cleanup(&finder); + Py_DECREF(identity); + return *ret != NULL; +fail: + md_finder_cleanup(&finder); + Py_XDECREF(identity); + Py_XDECREF(value); + Py_CLEAR(*ret); + return -1; +} + +static inline int +md_set_default(MultiDictObject *md, PyObject *key, PyObject *value, + PyObject **result) +{ + *result = NULL; + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + + htkeysiter_t iter; + htkeysiter_init(&iter, md->keys, hash); + entry_t *entries = htkeys_entries(md->keys); + + for (; iter.index != DKIX_EMPTY; htkeysiter_next(&iter)) { + if (iter.index < 0) { + continue; + } + entry_t *entry = entries + iter.index; + + if (hash != entry->hash) { + continue; + } + int tmp = _str_cmp(identity, entry->identity); + if (tmp > 0) { + Py_DECREF(identity); + ASSERT_CONSISTENT(md, false); + *result = Py_NewRef(entry->value); + return 1; + } else if (tmp < 0) { + goto fail; + } + } + + if (_md_add_with_hash(md, hash, identity, key, value) < 0) { + goto fail; + } + + Py_DECREF(identity); + ASSERT_CONSISTENT(md, false); + *result = Py_NewRef(value); + return 0; +fail: + Py_XDECREF(identity); + return -1; +} + +static inline int +md_pop_one(MultiDictObject *md, PyObject *key, PyObject **ret) +{ + PyObject *value = NULL; + + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + + htkeysiter_t iter; + htkeysiter_init(&iter, md->keys, hash); + entry_t *entries = htkeys_entries(md->keys); + + for (; iter.index != DKIX_EMPTY; htkeysiter_next(&iter)) { + if (iter.index < 0) { + continue; + } + entry_t *entry = entries + iter.index; + + if (hash != entry->hash) { + continue; + } + int tmp = _str_cmp(identity, entry->identity); + if (tmp > 0) { + value = Py_NewRef(entry->value); + if (_md_del_at(md, iter.slot, entry) < 0) { + goto fail; + } + Py_DECREF(identity); + *ret = value; + md->version = NEXT_VERSION(md->state); + ASSERT_CONSISTENT(md, false); + return 1; + } else if (tmp < 0) { + goto fail; + } + } + + ASSERT_CONSISTENT(md, false); + return 0; +fail: + Py_XDECREF(value); + Py_XDECREF(identity); + return -1; +} + +static inline int +md_pop_all(MultiDictObject *md, PyObject *key, PyObject **ret) +{ + PyObject *lst = NULL; + + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + + if (md_len(md) == 0) { + Py_DECREF(identity); + return 0; + } + + htkeysiter_t iter; + htkeysiter_init(&iter, md->keys, hash); + entry_t *entries = htkeys_entries(md->keys); + + for (; iter.index != DKIX_EMPTY; htkeysiter_next(&iter)) { + if (iter.index < 0) { + continue; + } + entry_t *entry = entries + iter.index; + + if (hash != entry->hash) { + continue; + } + int tmp = _str_cmp(identity, entry->identity); + if (tmp > 0) { + if (lst == NULL) { + lst = PyList_New(1); + if (lst == NULL) { + goto fail; + } + if (PyList_SetItem(lst, 0, Py_NewRef(entry->value)) < 0) { + goto fail; + } + } else if (PyList_Append(lst, entry->value) < 0) { + goto fail; + } + if (_md_del_at(md, iter.slot, entry) < 0) { + goto fail; + } + md->version = NEXT_VERSION(md->state); + } else if (tmp < 0) { + goto fail; + } + } + + *ret = lst; + Py_DECREF(identity); + ASSERT_CONSISTENT(md, false); + return lst != NULL; +fail: + Py_XDECREF(identity); + Py_XDECREF(lst); + return -1; +} + +static inline PyObject * +md_pop_item(MultiDictObject *md) +{ + if (md->used == 0) { + PyErr_SetString(PyExc_KeyError, "empty multidict"); + return NULL; + } + + entry_t *entries = htkeys_entries(md->keys); + + Py_ssize_t pos = md->keys->nentries - 1; + entry_t *entry = entries + pos; + while (pos >= 0 && entry->identity == NULL) { + pos--; + entry--; + } + assert(pos >= 0); + + PyObject *key = md_calc_key(md, entry->key, entry->identity); + if (key == NULL) { + return NULL; + } + PyObject *ret = PyTuple_Pack(2, key, entry->value); + Py_CLEAR(key); + if (ret == NULL) { + return NULL; + } + + htkeysiter_t iter; + htkeysiter_init(&iter, md->keys, entry->hash); + + for (; iter.index != pos; htkeysiter_next(&iter)) { + } + if (_md_del_at(md, iter.slot, entry) < 0) { + return NULL; + } + md->version = NEXT_VERSION(md->state); + ASSERT_CONSISTENT(md, false); + return ret; +} + +static inline int +_md_replace(MultiDictObject *md, PyObject *key, PyObject *value, + PyObject *identity, Py_hash_t hash) +{ + int found = 0; + md_finder_t finder = {0}; + if (md_init_finder(md, identity, &finder) < 0) { + assert(PyErr_Occurred()); + goto fail; + } + entry_t *entries = htkeys_entries(md->keys); + + int tmp; + + // don't grab neither key nor value but use the calculated index + while ((tmp = md_find_next(&finder, NULL, NULL)) > 0) { + entry_t *entry = entries + md_finder_index(&finder); + if (!found) { + found = 1; + Py_SETREF(entry->key, Py_NewRef(key)); + Py_SETREF(entry->value, Py_NewRef(value)); + entry->hash = -1; + } else { + if (_md_del_at(md, md_finder_slot(&finder), entry) < 0) { + goto fail; + } + } + } + if (tmp < 0) { + goto fail; + } + + md_finder_cleanup(&finder); + if (!found) { + if (_md_add_with_hash(md, hash, identity, key, value) < 0) { + goto fail; + } + return 0; + } else { + md->version = NEXT_VERSION(md->state); + return 0; + } +fail: + md_finder_cleanup(&finder); + return -1; +} + +static inline int +md_replace(MultiDictObject *md, PyObject *key, PyObject *value) +{ + PyObject *identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + + int ret = _md_replace(md, key, value, identity, hash); + Py_DECREF(identity); + ASSERT_CONSISTENT(md, false); + return ret; +fail: + Py_XDECREF(identity); + return -1; +} + +static inline int +_md_update(MultiDictObject *md, Py_hash_t hash, PyObject *identity, + PyObject *key, PyObject *value) +{ + htkeysiter_t iter; + htkeysiter_init(&iter, md->keys, hash); + entry_t *entries = htkeys_entries(md->keys); + bool found = false; + + for (; iter.index != DKIX_EMPTY; htkeysiter_next(&iter)) { + if (iter.index == DKIX_DUMMY) { + continue; + } + entry_t *entry = entries + iter.index; + if (hash != entry->hash) { + continue; + } + int tmp = _str_cmp(identity, entry->identity); + if (tmp > 0) { + if (!found) { + found = true; + if (entry->key == NULL) { + /* entry->key could be NULL if it was deleted + by the previous _md_update call during the iteration + in md_update_from* functions. */ + assert(entry->value == NULL); + entry->key = Py_NewRef(key); + entry->value = Py_NewRef(value); + } else { + Py_SETREF(entry->key, Py_NewRef(key)); + Py_SETREF(entry->value, Py_NewRef(value)); + } + entry->hash = -1; + } else { + if (_md_del_at_for_upd(md, iter.slot, entry) < 0) { + goto fail; + } + } + } else if (tmp < 0) { + goto fail; + } + } + + if (!found) { + if (_md_add_for_upd(md, hash, identity, key, value) < 0) { + goto fail; + } + } + return 0; +fail: + return -1; +} + +static inline int +md_post_update(MultiDictObject *md) +{ + htkeys_t *keys = md->keys; + size_t num_slots = htkeys_nslots(keys); + entry_t *entries = htkeys_entries(keys); + for (size_t slot = 0; slot < num_slots; slot++) { + Py_ssize_t index = htkeys_get_index(keys, slot); + if (index >= 0) { + entry_t *entry = entries + index; + if (entry->key == NULL) { + /* the entry is marked for deletion during .update() call + and not replaced with a new value */ + Py_CLEAR(entry->identity); + htkeys_set_index(keys, slot, DKIX_DUMMY); + md->used -= 1; + } + if (entry->hash == -1) { + entry->hash = _unicode_hash(entry->identity); + if (entry->hash == -1) { + // hash of string always exists but still + return -1; + } + } + } + } + return 0; +} + +static inline int +md_update_from_ht(MultiDictObject *md, MultiDictObject *other, bool update) +{ + Py_ssize_t pos; + Py_hash_t hash; + PyObject *identity = NULL; + PyObject *key = NULL; + bool recalc_identity = md->is_ci != other->is_ci; + + if (other->used == 0) { + return 0; + } + + entry_t *entries = htkeys_entries(other->keys); + + for (pos = 0; pos < other->keys->nentries; pos++) { + entry_t *entry = entries + pos; + if (entry->identity == NULL) { + continue; + } + if (recalc_identity) { + identity = md_calc_identity(md, entry->key); + if (identity == NULL) { + goto fail; + } + hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + /* materialize key */ + key = md_calc_key(other, entry->key, identity); + if (key == NULL) { + goto fail; + } + } else { + identity = entry->identity; + hash = entry->hash; + key = entry->key; + } + if (update) { + if (_md_update(md, hash, identity, key, entry->value) < 0) { + goto fail; + } + } else { + if (_md_add_with_hash(md, hash, identity, key, entry->value) < 0) { + goto fail; + } + } + if (recalc_identity) { + Py_CLEAR(identity); + Py_CLEAR(key); + } + } + return 0; +fail: + if (recalc_identity) { + Py_CLEAR(identity); + Py_CLEAR(key); + } + return -1; +} + +static inline int +md_update_from_dict(MultiDictObject *md, PyObject *kwds, bool update) +{ + Py_ssize_t pos = 0; + PyObject *identity = NULL; + PyObject *key = NULL; + PyObject *value = NULL; + + assert(PyDict_CheckExact(kwds)); + + // PyDict_Next returns borrowed refs + while (PyDict_Next(kwds, &pos, &key, &value)) { + Py_INCREF(key); + identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + if (update) { + if (_md_update(md, hash, identity, key, value) < 0) { + goto fail; + } + Py_CLEAR(identity); + Py_CLEAR(key); + } else { + int tmp = _md_add_with_hash_steal_refs( + md, hash, identity, key, Py_NewRef(value)); + if (tmp < 0) { + Py_DECREF(value); + goto fail; + } + identity = NULL; + key = NULL; + value = NULL; + } + } + return 0; +fail: + Py_CLEAR(identity); + Py_CLEAR(key); + return -1; +} + +static inline void +_err_not_sequence(Py_ssize_t i) +{ + PyErr_Format(PyExc_TypeError, + "multidict cannot convert sequence element #%zd" + " to a sequence", + i); +} + +static inline void +_err_bad_length(Py_ssize_t i, Py_ssize_t n) +{ + PyErr_Format(PyExc_ValueError, + "multidict update sequence element #%zd " + "has length %zd; 2 is required", + i, + n); +} + +static inline void +_err_cannot_fetch(Py_ssize_t i, const char *name) +{ + PyErr_Format(PyExc_ValueError, + "multidict update sequence element #%zd's " + "%s could not be fetched", + name, + i); +} + +static int +_md_parse_item(Py_ssize_t i, PyObject *item, PyObject **pkey, + PyObject **pvalue) +{ + Py_ssize_t n; + + if (PyTuple_CheckExact(item)) { + n = PyTuple_GET_SIZE(item); + if (n != 2) { + _err_bad_length(i, n); + goto fail; + } + *pkey = Py_NewRef(PyTuple_GET_ITEM(item, 0)); + *pvalue = Py_NewRef(PyTuple_GET_ITEM(item, 1)); + } else if (PyList_CheckExact(item)) { + n = PyList_GET_SIZE(item); + if (n != 2) { + _err_bad_length(i, n); + goto fail; + } + *pkey = Py_NewRef(PyList_GET_ITEM(item, 0)); + *pvalue = Py_NewRef(PyList_GET_ITEM(item, 1)); + } else { + if (!PySequence_Check(item)) { + _err_not_sequence(i); + goto fail; + } + n = PySequence_Size(item); + if (n != 2) { + _err_bad_length(i, n); + goto fail; + } + *pkey = PySequence_ITEM(item, 0); + *pvalue = PySequence_ITEM(item, 1); + if (*pkey == NULL) { + _err_cannot_fetch(i, "key"); + goto fail; + } + if (*pvalue == NULL) { + _err_cannot_fetch(i, "value"); + goto fail; + } + } + return 0; +fail: + Py_CLEAR(*pkey); + Py_CLEAR(*pvalue); + return -1; +} + +static inline int +md_update_from_seq(MultiDictObject *md, PyObject *seq, bool update) +{ + PyObject *it = NULL; + PyObject *item = NULL; // seq[i] + + PyObject *key = NULL; + PyObject *value = NULL; + PyObject *identity = NULL; + + Py_ssize_t i; + Py_ssize_t size = -1; + + enum { LIST, TUPLE, ITER } kind; + + if (PyList_CheckExact(seq)) { + kind = LIST; + size = PyList_GET_SIZE(seq); + if (size == 0) { + return 0; + } + } else if (PyTuple_CheckExact(seq)) { + kind = TUPLE; + size = PyTuple_GET_SIZE(seq); + if (size == 0) { + return 0; + } + } else { + kind = ITER; + it = PyObject_GetIter(seq); + if (it == NULL) { + goto fail; + } + } + + for (i = 0;; ++i) { // i - index into seq of current element + switch (kind) { + case LIST: + if (i >= size) { + goto exit; + } + item = PyList_GET_ITEM(seq, i); + if (item == NULL) { + goto fail; + } + Py_INCREF(item); + break; + case TUPLE: + if (i >= size) { + goto exit; + } + item = PyTuple_GET_ITEM(seq, i); + if (item == NULL) { + goto fail; + } + Py_INCREF(item); + break; + case ITER: + item = PyIter_Next(it); + if (item == NULL) { + if (PyErr_Occurred()) { + goto fail; + } + goto exit; + } + } + + if (_md_parse_item(i, item, &key, &value) < 0) { + goto fail; + } + + identity = md_calc_identity(md, key); + if (identity == NULL) { + goto fail; + } + + Py_hash_t hash = _unicode_hash(identity); + if (hash == -1) { + goto fail; + } + + if (update) { + if (_md_update(md, hash, identity, key, value) < 0) { + goto fail; + } + Py_CLEAR(identity); + Py_CLEAR(key); + Py_CLEAR(value); + } else { + if (_md_add_with_hash_steal_refs(md, hash, identity, key, value) < + 0) { + goto fail; + } + identity = NULL; + key = NULL; + value = NULL; + } + Py_CLEAR(item); + } + +exit: + Py_CLEAR(it); + return 0; + +fail: + Py_CLEAR(identity); + Py_CLEAR(it); + Py_CLEAR(item); + Py_CLEAR(key); + Py_CLEAR(value); + return -1; +} + +static inline int +md_eq(MultiDictObject *md, MultiDictObject *other) +{ + if (md == other) { + return 1; + } + + if (md_len(md) != md_len(other)) { + return 0; + } + + Py_ssize_t pos1 = 0; + Py_ssize_t pos2 = 0; + + entry_t *lft_entries = htkeys_entries(md->keys); + entry_t *rht_entries = htkeys_entries(other->keys); + for (;;) { + if (pos1 >= md->keys->nentries || pos2 >= other->keys->nentries) { + return 1; + } + entry_t *entry1 = lft_entries + pos1; + if (entry1->identity == NULL) { + pos1++; + continue; + } + entry_t *entry2 = rht_entries + pos2; + if (entry2->identity == NULL) { + pos2++; + continue; + } + + if (entry1->hash != entry2->hash) { + return 0; + } + + int cmp = _str_cmp(entry1->identity, entry2->identity); + if (cmp < 0) { + return -1; + }; + if (cmp == 0) { + return 0; + } + + cmp = PyObject_RichCompareBool(entry1->value, entry2->value, Py_EQ); + if (cmp < 0) { + return -1; + }; + if (cmp == 0) { + return 0; + } + pos1++; + pos2++; + } + return 1; +} + +static inline int +md_eq_to_mapping(MultiDictObject *md, PyObject *other) +{ + PyObject *key = NULL; + PyObject *avalue = NULL; + PyObject *bvalue; + + Py_ssize_t other_len; + + if (!PyMapping_Check(other)) { + PyErr_Format(PyExc_TypeError, + "other argument must be a mapping, not %s", + Py_TYPE(other)->tp_name); + return -1; + } + + other_len = PyMapping_Size(other); + if (other_len < 0) { + return -1; + } + if (md_len(md) != other_len) { + return 0; + } + + md_pos_t pos; + md_init_pos(md, &pos); + + for (;;) { + int ret = md_next(md, &pos, NULL, &key, &avalue); + if (ret < 0) { + return -1; + } + if (ret == 0) { + break; + } + ret = PyMapping_GetOptionalItem(other, key, &bvalue); + Py_CLEAR(key); + if (ret < 0) { + Py_CLEAR(avalue); + return -1; + } + + if (bvalue == NULL) { + Py_CLEAR(avalue); + return 0; + } + + int eq = PyObject_RichCompareBool(avalue, bvalue, Py_EQ); + Py_CLEAR(bvalue); + Py_CLEAR(avalue); + + if (eq <= 0) { + return eq; + } + } + + return 1; +} + +static inline PyObject * +md_repr(MultiDictObject *md, PyObject *name, bool show_keys, bool show_values) +{ + PyObject *key = NULL; + PyObject *value = NULL; + + bool comma = false; + uint64_t version = md->version; + + PyUnicodeWriter *writer = PyUnicodeWriter_Create(1024); + if (writer == NULL) return NULL; + + if (PyUnicodeWriter_WriteChar(writer, '<') < 0) { + goto fail; + } + if (PyUnicodeWriter_WriteStr(writer, name) < 0) { + goto fail; + } + if (PyUnicodeWriter_WriteChar(writer, '(') < 0) { + goto fail; + } + + entry_t *entries = htkeys_entries(md->keys); + + for (Py_ssize_t pos = 0; pos < md->keys->nentries; ++pos) { + if (version != md->version) { + PyErr_SetString(PyExc_RuntimeError, + "MultiDict changed during iteration"); + return NULL; + } + entry_t *entry = entries + pos; + if (entry->identity == NULL) { + continue; + } + key = Py_NewRef(entry->key); + value = Py_NewRef(entry->value); + + if (comma) { + if (PyUnicodeWriter_WriteChar(writer, ',') < 0) { + goto fail; + } + if (PyUnicodeWriter_WriteChar(writer, ' ') < 0) { + goto fail; + } + } + if (show_keys) { + if (PyUnicodeWriter_WriteChar(writer, '\'') < 0) { + goto fail; + } + /* Don't need to convert key to istr, the text is the same*/ + if (PyUnicodeWriter_WriteStr(writer, key) < 0) { + goto fail; + } + if (PyUnicodeWriter_WriteChar(writer, '\'') < 0) { + goto fail; + } + } + if (show_keys && show_values) { + if (PyUnicodeWriter_WriteChar(writer, ':') < 0) { + goto fail; + } + if (PyUnicodeWriter_WriteChar(writer, ' ') < 0) { + goto fail; + } + } + if (show_values) { + if (PyUnicodeWriter_WriteRepr(writer, value) < 0) { + goto fail; + } + } + + comma = true; + Py_CLEAR(key); + Py_CLEAR(value); + } + + if (PyUnicodeWriter_WriteChar(writer, ')') < 0) { + goto fail; + } + if (PyUnicodeWriter_WriteChar(writer, '>') < 0) { + goto fail; + } + return PyUnicodeWriter_Finish(writer); +fail: + Py_CLEAR(key); + Py_CLEAR(value); + PyUnicodeWriter_Discard(writer); + return NULL; +} + +/***********************************************************************/ + +static inline int +md_traverse(MultiDictObject *md, visitproc visit, void *arg) +{ + if (md->used == 0) { + return 0; + } + + entry_t *entries = htkeys_entries(md->keys); + for (Py_ssize_t pos = 0; pos < md->keys->nentries; pos++) { + entry_t *entry = entries + pos; + if (entry->identity != NULL) { + Py_VISIT(entry->key); + Py_VISIT(entry->value); + } + } + + return 0; +} + +static inline int +md_clear(MultiDictObject *md) +{ + if (md->used == 0) { + return 0; + } + md->version = NEXT_VERSION(md->state); + + entry_t *entries = htkeys_entries(md->keys); + for (Py_ssize_t pos = 0; pos < md->keys->nentries; pos++) { + entry_t *entry = entries + pos; + if (entry->identity != NULL) { + Py_CLEAR(entry->identity); + Py_CLEAR(entry->key); + Py_CLEAR(entry->value); + } + } + + md->used = 0; + if (md->keys != &empty_htkeys) { + htkeys_free(md->keys); + md->keys = &empty_htkeys; + } + ASSERT_CONSISTENT(md, false); + return 0; +} + +static inline int +_md_check_consistency(MultiDictObject *md, bool update) +{ + // ASSERT_WORLD_STOPPED_OR_DICT_LOCKED(op); + +#define CHECK(expr) assert(expr) + // do { if (!(expr)) { assert(0 && Py_STRINGIFY(expr)); } } while (0) + + htkeys_t *keys = md->keys; + CHECK(keys != NULL); + Py_ssize_t calc_usable = USABLE_FRACTION(htkeys_nslots(keys)); + + // In the free-threaded build, shared keys may be concurrently modified, + // so use atomic loads. + Py_ssize_t usable = keys->usable; + Py_ssize_t nentries = keys->nentries; + + CHECK(0 <= md->used && md->used <= calc_usable); + CHECK(0 <= usable && usable <= calc_usable); + CHECK(0 <= nentries && nentries <= calc_usable); + CHECK(usable + nentries <= calc_usable); + + for (Py_ssize_t i = 0; i < htkeys_nslots(keys); i++) { + Py_ssize_t ix = htkeys_get_index(keys, i); + CHECK(DKIX_DUMMY <= ix && ix <= calc_usable); + } + + entry_t *entries = htkeys_entries(keys); + for (Py_ssize_t i = 0; i < calc_usable; i++) { + entry_t *entry = &entries[i]; + PyObject *identity = entry->identity; + + if (identity != NULL) { + if (!update) { + CHECK(entry->hash != -1); + CHECK(entry->key != NULL); + CHECK(entry->value != NULL); + } else { + if (entry->key == NULL) { + CHECK(entry->value == NULL); + } else { + CHECK(entry->value != NULL); + } + } + + CHECK(PyUnicode_CheckExact(identity)); + if (entry->hash != -1) { + Py_hash_t hash = _unicode_hash(identity); + CHECK(entry->hash == hash); + } + } + } + return 1; + +#undef CHECK +} + +static inline int +_md_dump(MultiDictObject *md) +{ + htkeys_t *keys = md->keys; + printf("Dump %p [%ld from %ld usable %ld nentries %ld]\n", + (void *)md, + md->used, + htkeys_nslots(keys), + keys->usable, + keys->nentries); + for (Py_ssize_t i = 0; i < htkeys_nslots(keys); i++) { + Py_ssize_t ix = htkeys_get_index(keys, i); + printf(" %ld -> %ld\n", i, ix); + } + printf(" --------\n"); + entry_t *entries = htkeys_entries(keys); + for (Py_ssize_t i = 0; i < keys->nentries; i++) { + entry_t *entry = &entries[i]; + PyObject *identity = entry->identity; + + if (identity == NULL) { + printf(" %ld [deleted]\n", i); + } else { + printf(" %ld h=%20ld, i=\'", i, entry->hash); + PyObject_Print(entry->identity, stdout, Py_PRINT_RAW); + printf("\', k=\'"); + PyObject_Print(entry->key, stdout, Py_PRINT_RAW); + printf("\', v=\'"); + PyObject_Print(entry->value, stdout, Py_PRINT_RAW); + printf("\'\n"); + } + } + printf("\n"); + return 1; +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/contrib/python/multidict/multidict/_multilib/htkeys.h b/contrib/python/multidict/multidict/_multilib/htkeys.h new file mode 100644 index 00000000000..a13d5fb41b0 --- /dev/null +++ b/contrib/python/multidict/multidict/_multilib/htkeys.h @@ -0,0 +1,414 @@ +#include "pythoncapi_compat.h" + +#ifndef _MULTIDICT_HTKEYS_H +#define _MULTIDICT_HTKEYS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <Python.h> +#include <stdbool.h> + +/* Implementation note. +identity always has exact PyUnicode_Type type, not a subclass. +It guarantees that identity hashing and comparison never calls +Python code back, and these operations has no weird side effects, +e.g. deletion the key from multidict. + +Taking into account the fact that all multidict operations except +repr(md), repr(md_proxy), or repr(view) never access to the key +itself but identity instead, borrowed references during iteration +over pair_list for, e.g., md.get() or md.pop() is safe. +*/ + +typedef struct entry { + Py_hash_t hash; + PyObject *identity; + PyObject *key; + PyObject *value; +} entry_t; + +#define DKIX_EMPTY (-1) /* empty (never used) slot */ +#define DKIX_DUMMY (-2) /* deleted slot */ + +#define HT_LOG_MINSIZE 3 +#define HT_MINSIZE 8 +#define HT_PERTURB_SHIFT 5 + +typedef struct _htkeys { + /* Size of the hash table (indices). It must be a power of 2. */ + uint8_t log2_size; + + /* Size of the hash table (indices) by bytes. */ + uint8_t log2_index_bytes; + + /* Number of usable entries in dk_entries. */ + Py_ssize_t usable; + + /* Number of used entries in dk_entries. */ + Py_ssize_t nentries; + + /* Actual hash table of dk_size entries. It holds indices in dk_entries, + or DKIX_EMPTY(-1) or DKIX_DUMMY(-2). + + Indices must be: 0 <= indice < USABLE_FRACTION(dk_size). + + The size in bytes of an indice depends on dk_size: + + - 1 byte if htkeys_nslots() <= 0xff (char*) + - 2 bytes if htkeys_nslots() <= 0xffff (int16_t*) + - 4 bytes if htkeys_nslots() <= 0xffffffff (int32_t*) + - 8 bytes otherwise (int64_t*) + + Dynamically sized, SIZEOF_VOID_P is minimum. */ + char indices[]; /* char is required to avoid strict aliasing. */ + +} htkeys_t; + +#if SIZEOF_VOID_P > 4 +static inline Py_ssize_t +htkeys_nslots(const htkeys_t *keys) +{ + return ((int64_t)1) << keys->log2_size; +} +#else +static inline Py_ssize_t +htkeys_nslots(const htkeys_t *keys) +{ + return 1 << keys->log2_size; +} +#endif + +static inline Py_ssize_t +htkeys_mask(const htkeys_t *keys) +{ + return htkeys_nslots(keys) - 1; +} + +static inline entry_t * +htkeys_entries(const htkeys_t *dk) +{ + int8_t *indices = (int8_t *)(dk->indices); + size_t index = (size_t)1 << dk->log2_index_bytes; + return (entry_t *)(&indices[index]); +} + +#define LOAD_INDEX(keys, size, idx) \ + ((const int##size##_t *)(keys->indices))[idx] +#define STORE_INDEX(keys, size, idx, value) \ + ((int##size##_t *)(keys->indices))[idx] = (int##size##_t)value + +/* lookup indices. returns DKIX_EMPTY, DKIX_DUMMY, or ix >=0 */ +static inline Py_ssize_t +htkeys_get_index(const htkeys_t *keys, Py_ssize_t i) +{ + uint8_t log2size = keys->log2_size; + Py_ssize_t ix; + + if (log2size < 8) { + ix = LOAD_INDEX(keys, 8, i); + } else if (log2size < 16) { + ix = LOAD_INDEX(keys, 16, i); + } +#if SIZEOF_VOID_P > 4 + else if (log2size >= 32) { + ix = LOAD_INDEX(keys, 64, i); + } +#endif + else { + ix = LOAD_INDEX(keys, 32, i); + } + assert(ix >= DKIX_DUMMY); + return ix; +} + +/* write to indices. */ +static inline void +htkeys_set_index(htkeys_t *keys, Py_ssize_t i, Py_ssize_t ix) +{ + uint8_t log2size = keys->log2_size; + + assert(ix >= DKIX_DUMMY); + + if (log2size < 8) { + assert(ix <= 0x7f); + STORE_INDEX(keys, 8, i, ix); + } else if (log2size < 16) { + assert(ix <= 0x7fff); + STORE_INDEX(keys, 16, i, ix); + } +#if SIZEOF_VOID_P > 4 + else if (log2size >= 32) { + STORE_INDEX(keys, 64, i, ix); + } +#endif + else { + assert(ix <= 0x7fffffff); + STORE_INDEX(keys, 32, i, ix); + } +} + +/* USABLE_FRACTION is the maximum dictionary load. + * Increasing this ratio makes dictionaries more dense resulting in more + * collisions. Decreasing it improves sparseness at the expense of spreading + * indices over more cache lines and at the cost of total memory consumed. + * + * USABLE_FRACTION must obey the following: + * (0 < USABLE_FRACTION(n) < n) for all n >= 2 + * + * USABLE_FRACTION should be quick to calculate. + * Fractions around 1/2 to 2/3 seem to work well in practice. + */ +static inline Py_ssize_t +USABLE_FRACTION(Py_ssize_t n) +{ + return (n << 1) / 3; +} + +// Return the index of the most significant 1 bit in 'x'. This is the smallest +// integer k such that x < 2**k. Equivalent to floor(log2(x)) + 1 for x != 0. +static inline int +_ht_bit_length(unsigned long x) +{ +#if (defined(__clang__) || defined(__GNUC__)) + if (x != 0) { + // __builtin_clzl() is available since GCC 3.4. + // Undefined behavior for x == 0. + return (int)sizeof(unsigned long) * 8 - __builtin_clzl(x); + } else { + return 0; + } +#elif defined(_MSC_VER) + // _BitScanReverse() is documented to search 32 bits. + Py_BUILD_ASSERT(sizeof(unsigned long) <= 4); + unsigned long msb; + if (_BitScanReverse(&msb, x)) { + return (int)msb + 1; + } else { + return 0; + } +#else + const int BIT_LENGTH_TABLE[32] = {0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, + 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}; + int msb = 0; + while (x >= 32) { + msb += 6; + x >>= 6; + } + msb += BIT_LENGTH_TABLE[x]; + return msb; +#endif +} + +/* Find the smallest dk_size >= minsize. */ +static inline uint8_t +calculate_log2_keysize(Py_ssize_t minsize) +{ +#if SIZEOF_LONG == SIZEOF_SIZE_T + minsize = (minsize | HT_MINSIZE) - 1; + return _ht_bit_length(minsize | (HT_MINSIZE - 1)); +#elif defined(_MSC_VER) + // On 64bit Windows, sizeof(long) == 4. + minsize = (minsize | HT_MINSIZE) - 1; + unsigned long msb; + _BitScanReverse64(&msb, (uint64_t)minsize); + return (uint8_t)(msb + 1); +#else + uint8_t log2_size; + for (log2_size = HT_LOG_MINSIZE; (((Py_ssize_t)1) << log2_size) < minsize; + log2_size++) + ; + return log2_size; +#endif +} + +/* estimate_keysize is reverse function of USABLE_FRACTION. + * + * This can be used to reserve enough size to insert n entries without + * resizing. + */ +static inline uint8_t +estimate_log2_keysize(Py_ssize_t n) +{ + return calculate_log2_keysize((n * 3 + 1) / 2); +} + +/* This immutable, empty PyDictKeysObject is used for PyDict_Clear() + * (which cannot fail and thus can do no allocation). + * + * See https://github.com/python/cpython/pull/127568#discussion_r1868070614 + * for the rationale of using log2_index_bytes=3 instead of 0. + */ +static htkeys_t empty_htkeys = { + 0, /* log2_size */ + 3, /* log2_index_bytes */ + 0, /* usable (immutable) */ + 0, /* nentries */ + {DKIX_EMPTY, + DKIX_EMPTY, + DKIX_EMPTY, + DKIX_EMPTY, + DKIX_EMPTY, + DKIX_EMPTY, + DKIX_EMPTY, + DKIX_EMPTY}, /* indices */ +}; + +static inline Py_ssize_t +htkeys_sizeof(htkeys_t *keys) +{ + Py_ssize_t usable = USABLE_FRACTION((size_t)1 << keys->log2_size); + return (sizeof(htkeys_t) + ((size_t)1 << keys->log2_index_bytes) + + sizeof(entry_t) * usable); +} + +static inline htkeys_t * +htkeys_new(uint8_t log2_size) +{ + assert(log2_size >= HT_LOG_MINSIZE); + + Py_ssize_t usable = USABLE_FRACTION(((size_t)1) << log2_size); + uint8_t log2_bytes; + + if (log2_size < 8) { + log2_bytes = log2_size; + } else if (log2_size < 16) { + log2_bytes = log2_size + 1; + } +#if SIZEOF_VOID_P > 4 + else if (log2_size >= 32) { + log2_bytes = log2_size + 3; + } +#endif + else { + log2_bytes = log2_size + 2; + } + + htkeys_t *keys = NULL; + /* TODO: CPython uses freelist of key objects with unicode type + and log2_size == PyDict_LOG_MINSIZE */ + keys = PyMem_Malloc(sizeof(htkeys_t) + ((size_t)1 << log2_bytes) + + sizeof(entry_t) * usable); + if (keys == NULL) { + PyErr_NoMemory(); + return NULL; + } + + keys->log2_size = log2_size; + keys->log2_index_bytes = log2_bytes; + keys->nentries = 0; + keys->usable = usable; + memset(&keys->indices[0], 0xff, ((size_t)1 << log2_bytes)); + memset( + &keys->indices[(size_t)1 << log2_bytes], 0, sizeof(entry_t) * usable); + return keys; +} + +static inline void +htkeys_free(htkeys_t *dk) +{ + /* TODO: CPython uses freelist of key objects with unicode type + and log2_size == PyDict_LOG_MINSIZE */ + PyMem_Free(dk); +} + +static inline Py_hash_t +_unicode_hash(PyObject *o) +{ + assert(PyUnicode_CheckExact(o)); + PyASCIIObject *ascii = (PyASCIIObject *)o; + if (ascii->hash != -1) { + return ascii->hash; + } + return PyUnicode_Type.tp_hash(o); +} + +/* +Internal routine used by ht_resize() to build a hashtable of entries. +*/ +static inline int +htkeys_build_indices(htkeys_t *keys, entry_t *ep, Py_ssize_t n, bool update) +{ + size_t mask = htkeys_mask(keys); + for (Py_ssize_t ix = 0; ix != n; ix++, ep++) { + Py_hash_t hash = ep->hash; + if (update) { + if (hash == -1) { + hash = _unicode_hash(ep->identity); + if (hash == -1) { + return -1; + } + } + } else { + assert(hash != -1); + } + size_t i = hash & mask; + for (size_t perturb = hash; htkeys_get_index(keys, i) != DKIX_EMPTY;) { + perturb >>= HT_PERTURB_SHIFT; + i = mask & (i * 5 + perturb + 1); + } + htkeys_set_index(keys, i, ix); + } + return 0; +} + +/* Internal function to find slot for an item from its hash + when it is known that the key is not present in the dict. + */ +static inline Py_ssize_t +htkeys_find_empty_slot(htkeys_t *keys, Py_hash_t hash) +{ + const size_t mask = htkeys_mask(keys); + size_t i = hash & mask; + Py_ssize_t ix = htkeys_get_index(keys, i); + for (size_t perturb = hash; ix >= 0 || ix == DKIX_DUMMY;) { + perturb >>= HT_PERTURB_SHIFT; + i = (i * 5 + perturb + 1) & mask; + ix = htkeys_get_index(keys, i); + } + return i; +} + +/* Iterator over slots/indexes for given hash. + N.B. The iterator MIGHT return the same slot + multiple times, eiter consequently (1, 2, 2, 3) + or with different slots in the middle (1, 2, 3, 1). + + The caller is responsible to mark visited slots + and cleanup the mark after the iteration finish. + + See ht_finder_t for an object designed for such operations. +*/ + +typedef struct _htkeysiter { + htkeys_t *keys; + size_t mask; // htkeys_mask(keys) + size_t slot; // masked hash, Py_hash_t h & mask; + size_t perturb; + Py_ssize_t index; +} htkeysiter_t; + +static inline void +htkeysiter_init(htkeysiter_t *iter, htkeys_t *keys, Py_hash_t hash) +{ + iter->keys = keys; + iter->mask = htkeys_mask(keys); + iter->perturb = (size_t)hash; + iter->slot = hash & iter->mask; + iter->index = htkeys_get_index(iter->keys, iter->slot); +} + +static inline void +htkeysiter_next(htkeysiter_t *iter) +{ + iter->perturb >>= HT_PERTURB_SHIFT; + iter->slot = (iter->slot * 5 + iter->perturb + 1) & iter->mask; + iter->index = htkeys_get_index(iter->keys, iter->slot); +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/contrib/python/multidict/multidict/_multilib/istr.h b/contrib/python/multidict/multidict/_multilib/istr.h index 156b0dc04a3..4805849f3fe 100644 --- a/contrib/python/multidict/multidict/_multilib/istr.h +++ b/contrib/python/multidict/multidict/_multilib/istr.h @@ -9,7 +9,7 @@ extern "C" { typedef struct { PyUnicodeObject str; - PyObject * canonical; + PyObject *canonical; mod_state *state; } istrobject; @@ -23,7 +23,7 @@ static inline void istr_dealloc(istrobject *self) { Py_XDECREF(self->canonical); - PyUnicode_Type.tp_dealloc((PyObject*)self); + PyUnicode_Type.tp_dealloc((PyObject *)self); } static inline PyObject * @@ -42,8 +42,8 @@ istr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) PyObject *canonical = NULL; PyObject *ret = NULL; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO:str", - kwlist, &x, &encoding, &errors)) { + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "|OOO:str", kwlist, &x, &encoding, &errors)) { return NULL; } if (x != NULL && IStr_Check(state, x)) { @@ -58,8 +58,8 @@ istr_new(PyTypeObject *type, PyObject *args, PyObject *kwds) if (!canonical) { goto fail; } - ((istrobject*)ret)->canonical = canonical; - ((istrobject*)ret)->state = state; + ((istrobject *)ret)->canonical = canonical; + ((istrobject *)ret)->state = state; return ret; fail: Py_XDECREF(ret); @@ -88,10 +88,9 @@ ret: return result; } - static PyMethodDef istr_methods[] = { {"__reduce__", (PyCFunction)istr_reduce, METH_NOARGS, NULL}, - {NULL, NULL} /* sentinel */ + {NULL, NULL} /* sentinel */ }; static PyType_Slot istr_slots[] = { @@ -105,8 +104,7 @@ static PyType_Slot istr_slots[] = { static PyType_Spec istr_spec = { .name = "multidict._multidict.istr", .basicsize = sizeof(istrobject), - .flags = (Py_TPFLAGS_DEFAULT - | Py_TPFLAGS_BASETYPE + .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE #if PY_VERSION_HEX >= 0x030a00f0 | Py_TPFLAGS_IMMUTABLETYPE #endif @@ -114,7 +112,6 @@ static PyType_Spec istr_spec = { .slots = istr_slots, }; - static inline PyObject * IStr_New(mod_state *state, PyObject *str, PyObject *canonical) { @@ -129,8 +126,8 @@ IStr_New(mod_state *state, PyObject *str, PyObject *canonical) goto ret; } Py_INCREF(canonical); - ((istrobject*)res)->canonical = canonical; - ((istrobject*)res)->state = state; + ((istrobject *)res)->canonical = canonical; + ((istrobject *)res)->state = state; ret: Py_CLEAR(args); return res; diff --git a/contrib/python/multidict/multidict/_multilib/iter.h b/contrib/python/multidict/multidict/_multilib/iter.h index 2f3ca9de84c..10460bf2756 100644 --- a/contrib/python/multidict/multidict/_multilib/iter.h +++ b/contrib/python/multidict/multidict/_multilib/iter.h @@ -6,13 +6,13 @@ extern "C" { #endif #include "dict.h" -#include "pair_list.h" +#include "hashtable.h" #include "state.h" typedef struct multidict_iter { PyObject_HEAD MultiDictObject *md; // MultiDict or CIMultiDict - pair_list_pos_t current; + md_pos_t current; } MultidictIter; static inline void @@ -21,14 +21,14 @@ _init_iter(MultidictIter *it, MultiDictObject *md) Py_INCREF(md); it->md = md; - pair_list_init_pos(&md->pairs, &it->current); + md_init_pos(md, &it->current); } static inline PyObject * multidict_items_iter_new(MultiDictObject *md) { - MultidictIter *it = PyObject_GC_New( - MultidictIter, md->pairs.state->ItemsIterType); + MultidictIter *it = + PyObject_GC_New(MultidictIter, md->state->ItemsIterType); if (it == NULL) { return NULL; } @@ -42,8 +42,8 @@ multidict_items_iter_new(MultiDictObject *md) static inline PyObject * multidict_keys_iter_new(MultiDictObject *md) { - MultidictIter *it = PyObject_GC_New( - MultidictIter, md->pairs.state->KeysIterType); + MultidictIter *it = + PyObject_GC_New(MultidictIter, md->state->KeysIterType); if (it == NULL) { return NULL; } @@ -57,8 +57,8 @@ multidict_keys_iter_new(MultiDictObject *md) static inline PyObject * multidict_values_iter_new(MultiDictObject *md) { - MultidictIter *it = PyObject_GC_New( - MultidictIter, md->pairs.state->ValuesIterType); + MultidictIter *it = + PyObject_GC_New(MultidictIter, md->state->ValuesIterType); if (it == NULL) { return NULL; } @@ -76,8 +76,7 @@ multidict_items_iter_iternext(MultidictIter *self) PyObject *value = NULL; PyObject *ret = NULL; - int res = pair_list_next(&self->md->pairs, &self->current, - NULL, &key, &value); + int res = md_next(self->md, &self->current, NULL, &key, &value); if (res < 0) { return NULL; } @@ -103,8 +102,7 @@ multidict_values_iter_iternext(MultidictIter *self) { PyObject *value = NULL; - int res = pair_list_next(&self->md->pairs, &self->current, - NULL, NULL, &value); + int res = md_next(self->md, &self->current, NULL, NULL, &value); if (res < 0) { return NULL; } @@ -121,8 +119,7 @@ multidict_keys_iter_iternext(MultidictIter *self) { PyObject *key = NULL; - int res = pair_list_next(&self->md->pairs, &self->current, - NULL, &key, NULL); + int res = md_next(self->md, &self->current, NULL, &key, NULL); if (res < 0) { return NULL; } @@ -159,23 +156,18 @@ multidict_iter_clear(MultidictIter *self) static inline PyObject * multidict_iter_len(MultidictIter *self) { - return PyLong_FromLong(pair_list_len(&self->md->pairs)); + return PyLong_FromLong(md_len(self->md)); } PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it))."); static PyMethodDef multidict_iter_methods[] = { - { - "__length_hint__", - (PyCFunction)(void(*)(void))multidict_iter_len, - METH_NOARGS, - length_hint_doc - }, - { - NULL, - NULL - } /* sentinel */ + {"__length_hint__", + (PyCFunction)(void (*)(void))multidict_iter_len, + METH_NOARGS, + length_hint_doc}, + {NULL, NULL} /* sentinel */ }; /***********************************************************************/ @@ -222,7 +214,6 @@ static PyType_Spec multidict_values_iter_spec = { .slots = multidict_values_iter_slots, }; - static PyType_Slot multidict_keys_iter_slots[] = { {Py_tp_dealloc, multidict_iter_dealloc}, {Py_tp_methods, multidict_iter_methods}, @@ -247,7 +238,7 @@ static PyType_Spec multidict_keys_iter_spec = { static inline int multidict_iter_init(PyObject *module, mod_state *state) { - PyObject * tmp; + PyObject *tmp; tmp = PyType_FromModuleAndSpec(module, &multidict_items_iter_spec, NULL); if (tmp == NULL) { return -1; diff --git a/contrib/python/multidict/multidict/_multilib/pair_list.h b/contrib/python/multidict/multidict/_multilib/pair_list.h deleted file mode 100644 index 6c45673b73f..00000000000 --- a/contrib/python/multidict/multidict/_multilib/pair_list.h +++ /dev/null @@ -1,1633 +0,0 @@ -#include "pythoncapi_compat.h" - -#ifndef _MULTIDICT_PAIR_LIST_H -#define _MULTIDICT_PAIR_LIST_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include <string.h> -#include <stddef.h> -#include <stdint.h> -#include <stdbool.h> - -#include "istr.h" -#include "state.h" - -/* Implementation note. -identity always has exact PyUnicode_Type type, not a subclass. -It guarantees that identity hashing and comparison never calls -Python code back, and these operations has no weird side effects, -e.g. deletion the key from multidict. - -Taking into account the fact that all multidict operations except -repr(md), repr(md_proxy), or repr(view) never access to the key -itself but identity instead, borrowed references during iteration -over pair_list for, e.g., md.get() or md.pop() is safe. -*/ - -typedef struct pair { - PyObject *identity; // 8 - PyObject *key; // 8 - PyObject *value; // 8 - Py_hash_t hash; // 8 -} pair_t; - -/* Note about the structure size -With 28 pairs the MultiDict object size is slightly less than 1KiB - -To fit into 512 bytes, the structure can contain only 13 pairs -which is too small, e.g. https://www.python.org returns 16 headers -(9 of them are caching proxy information though). - -The embedded buffer intention is to fit the vast majority of possible -HTTP headers into the buffer without allocating an extra memory block. -*/ - -#define EMBEDDED_CAPACITY 28 - -typedef struct pair_list { - mod_state *state; - Py_ssize_t capacity; - Py_ssize_t size; - uint64_t version; - bool calc_ci_indentity; - pair_t *pairs; - pair_t buffer[EMBEDDED_CAPACITY]; -} pair_list_t; - -#define MIN_CAPACITY 64 -#define CAPACITY_STEP MIN_CAPACITY - -/* Global counter used to set ma_version_tag field of dictionary. - * It is incremented each time that a dictionary is created and each - * time that a dictionary is modified. */ -static uint64_t pair_list_global_version = 0; - -#define NEXT_VERSION() (++pair_list_global_version) - - -typedef struct pair_list_pos { - Py_ssize_t pos; - uint64_t version; -} pair_list_pos_t; - - -static inline int -str_cmp(PyObject *s1, PyObject *s2) -{ - PyObject *ret = PyUnicode_RichCompare(s1, s2, Py_EQ); - if (Py_IsTrue(ret)) { - Py_DECREF(ret); - return 1; - } - else if (ret == NULL) { - return -1; - } - else { - Py_DECREF(ret); - return 0; - } -} - - -static inline PyObject * -_key_to_ident(mod_state *state, PyObject *key) -{ - if (IStr_Check(state, key)) { - return Py_NewRef(((istrobject*)key)->canonical); - } - if (PyUnicode_CheckExact(key)) { - return Py_NewRef(key); - } - if (PyUnicode_Check(key)) { - return PyUnicode_FromObject(key); - } - PyErr_SetString(PyExc_TypeError, - "MultiDict keys should be either str " - "or subclasses of str"); - return NULL; -} - - -static inline PyObject * -_ci_key_to_ident(mod_state *state, PyObject *key) -{ - if (IStr_Check(state, key)) { - return Py_NewRef(((istrobject*)key)->canonical); - } - if (PyUnicode_Check(key)) { - PyObject *ret = PyObject_CallMethodNoArgs(key, state->str_lower); - if (!PyUnicode_CheckExact(ret)) { - PyObject *tmp = PyUnicode_FromObject(ret); - Py_CLEAR(ret); - if (tmp == NULL) { - return NULL; - } - ret = tmp; - } - return ret; - } - PyErr_SetString(PyExc_TypeError, - "CIMultiDict keys should be either str " - "or subclasses of str"); - return NULL; -} - - -static inline PyObject * -_arg_to_key(mod_state *state, PyObject *key, PyObject *ident) -{ - if (PyUnicode_Check(key)) { - return Py_NewRef(key); - } - PyErr_SetString(PyExc_TypeError, - "MultiDict keys should be either str " - "or subclasses of str"); - return NULL; -} - - -static inline PyObject * -_ci_arg_to_key(mod_state *state, PyObject *key, PyObject *ident) -{ - if (IStr_Check(state, key)) { - return Py_NewRef(key); - } - if (PyUnicode_Check(key)) { - return IStr_New(state, key, ident); - } - PyErr_SetString(PyExc_TypeError, - "CIMultiDict keys should be either str " - "or subclasses of str"); - return NULL; -} - - -static inline int -pair_list_grow(pair_list_t *list, Py_ssize_t amount) -{ - // Grow by one element if needed - Py_ssize_t capacity = ((Py_ssize_t)((list->size + amount) - / CAPACITY_STEP) + 1) * CAPACITY_STEP; - - pair_t *new_pairs; - - if (list->size + amount -1 < list->capacity) { - return 0; - } - - if (list->pairs == list->buffer) { - new_pairs = PyMem_New(pair_t, (size_t)capacity); - memcpy(new_pairs, list->buffer, (size_t)list->capacity * sizeof(pair_t)); - - list->pairs = new_pairs; - list->capacity = capacity; - return 0; - } else { - new_pairs = PyMem_Resize(list->pairs, pair_t, (size_t)capacity); - - if (NULL == new_pairs) { - // Resizing error - return -1; - } - - list->pairs = new_pairs; - list->capacity = capacity; - return 0; - } -} - - -static inline int -pair_list_shrink(pair_list_t *list) -{ - // Shrink by one element if needed. - // Optimization is applied to prevent jitter - // (grow-shrink-grow-shrink on adding-removing the single element - // when the buffer is full). - // To prevent this, the buffer is resized if the size is less than the capacity - // by 2*CAPACITY_STEP factor. - // The switch back to embedded buffer is never performed for both reasons: - // the code simplicity and the jitter prevention. - - pair_t *new_pairs; - Py_ssize_t new_capacity; - - if (list->capacity - list->size < 2 * CAPACITY_STEP) { - return 0; - } - new_capacity = list->capacity - CAPACITY_STEP; - if (new_capacity < MIN_CAPACITY) { - return 0; - } - - new_pairs = PyMem_Resize(list->pairs, pair_t, (size_t)new_capacity); - - if (NULL == new_pairs) { - // Resizing error - return -1; - } - - list->pairs = new_pairs; - list->capacity = new_capacity; - - return 0; -} - - -static inline int -_pair_list_init(pair_list_t *list, mod_state *state, - bool calc_ci_identity, Py_ssize_t preallocate) -{ - list->state = state; - list->calc_ci_indentity = calc_ci_identity; - Py_ssize_t capacity = EMBEDDED_CAPACITY; - if (preallocate >= capacity) { - capacity = ((Py_ssize_t)(preallocate / CAPACITY_STEP) + 1) * CAPACITY_STEP; - list->pairs = PyMem_New(pair_t, (size_t)capacity); - } else { - list->pairs = list->buffer; - } - list->capacity = capacity; - list->size = 0; - list->version = NEXT_VERSION(); - return 0; -} - -static inline int -pair_list_init(pair_list_t *list, mod_state *state, Py_ssize_t size) -{ - return _pair_list_init(list, state, /* calc_ci_identity = */ false, size); -} - - -static inline int -ci_pair_list_init(pair_list_t *list, mod_state *state, Py_ssize_t size) -{ - return _pair_list_init(list, state, /* calc_ci_identity = */ true, size); -} - - -static inline PyObject * -pair_list_calc_identity(pair_list_t *list, PyObject *key) -{ - if (list->calc_ci_indentity) - return _ci_key_to_ident(list->state, key); - return _key_to_ident(list->state, key); -} - -static inline PyObject * -pair_list_calc_key(pair_list_t *list, PyObject *key, PyObject *ident) -{ - if (list->calc_ci_indentity) - return _ci_arg_to_key(list->state, key, ident); - return _arg_to_key(list->state, key, ident); -} - -static inline void -pair_list_dealloc(pair_list_t *list) -{ - Py_ssize_t pos; - - for (pos = 0; pos < list->size; pos++) { - pair_t *pair = list->pairs + pos; - - Py_CLEAR(pair->identity); - Py_CLEAR(pair->key); - Py_CLEAR(pair->value); - } - - /* - Strictly speaking, resetting size and capacity and - assigning pairs to buffer is not necessary. - Do it to consistency and idemotency. - The cleanup doesn't hurt performance. - !!! - !!! The buffer deletion is crucial though. - !!! - */ - list->size = 0; - if (list->pairs != list->buffer) { - PyMem_Free(list->pairs); - list->pairs = list->buffer; - list->capacity = EMBEDDED_CAPACITY; - } -} - - -static inline Py_ssize_t -pair_list_len(pair_list_t *list) -{ - return list->size; -} - - -static inline int -_pair_list_add_with_hash_steal_refs(pair_list_t *list, - PyObject *identity, - PyObject *key, - PyObject *value, - Py_hash_t hash) -{ - if (pair_list_grow(list, 1) < 0) { - return -1; - } - - pair_t *pair = list->pairs + list->size; - - pair->identity = identity; - pair->key = key; - pair->value = value; - pair->hash = hash; - - list->version = NEXT_VERSION(); - list->size += 1; - - return 0; -} - -static inline int -_pair_list_add_with_hash(pair_list_t *list, - PyObject *identity, - PyObject *key, - PyObject *value, - Py_hash_t hash) -{ - Py_INCREF(identity); - Py_INCREF(key); - Py_INCREF(value); - return _pair_list_add_with_hash_steal_refs(list, identity, key, value, hash); -} - - -static inline int -pair_list_add(pair_list_t *list, PyObject *key, PyObject *value) -{ - PyObject *identity = pair_list_calc_identity(list, key); - if (identity == NULL) { - goto fail; - } - Py_hash_t hash = PyObject_Hash(identity); - if (hash == -1) { - goto fail; - } - int ret = _pair_list_add_with_hash(list, identity, key, value, hash); - Py_DECREF(identity); - return ret; -fail: - Py_XDECREF(identity); - return -1; -} - - -static inline int -pair_list_del_at(pair_list_t *list, Py_ssize_t pos) -{ - // return 1 on success, -1 on failure - pair_t *pair = list->pairs + pos; - Py_DECREF(pair->identity); - Py_DECREF(pair->key); - Py_DECREF(pair->value); - - list->size -= 1; - list->version = NEXT_VERSION(); - - if (list->size == pos) { - // remove from tail, no need to shift body - return 0; - } - - Py_ssize_t tail = list->size - pos; - // TODO: raise an error if tail < 0 - memmove((void *)(list->pairs + pos), - (void *)(list->pairs + pos + 1), - sizeof(pair_t) * (size_t)tail); - - return pair_list_shrink(list); -} - - -static inline int -_pair_list_drop_tail(pair_list_t *list, PyObject *identity, Py_hash_t hash, - Py_ssize_t pos) -{ - // return 1 if deleted, 0 if not found - int found = 0; - - if (pos >= list->size) { - return 0; - } - - for (; pos < list->size; pos++) { - pair_t *pair = list->pairs + pos; - if (pair->hash != hash) { - continue; - } - int ret = str_cmp(pair->identity, identity); - if (ret > 0) { - if (pair_list_del_at(list, pos) < 0) { - return -1; - } - found = 1; - pos--; - } - else if (ret == -1) { - return -1; - } - } - - return found; -} - - -static inline int -pair_list_del(pair_list_t *list, PyObject *key) -{ - PyObject *identity = pair_list_calc_identity(list, key); - if (identity == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(identity); - if (hash == -1) { - goto fail; - } - - int ret = _pair_list_drop_tail(list, identity, hash, 0); - - if (ret < 0) { - goto fail; - } - else if (ret == 0) { - PyErr_SetObject(PyExc_KeyError, key); - goto fail; - } - else { - list->version = NEXT_VERSION(); - } - Py_DECREF(identity); - return 0; -fail: - Py_XDECREF(identity); - return -1; -} - - -static inline uint64_t -pair_list_version(pair_list_t *list) -{ - return list->version; -} - - -static inline void -pair_list_init_pos(pair_list_t *list, pair_list_pos_t *pos) -{ - pos->pos = 0; - pos->version = list->version; -} - -static inline int -pair_list_next(pair_list_t *list, pair_list_pos_t *pos, - PyObject **pidentity, - PyObject **pkey, PyObject **pvalue) -{ - if (pos->pos >= list->size) { - if (pidentity) { - *pidentity = NULL; - } - if (pkey) { - *pkey = NULL; - } - if (pvalue) { - *pvalue = NULL; - } - return 0; - } - - if (pos->version != list->version) { - if (pidentity) { - *pidentity = NULL; - } - if (pkey) { - *pkey = NULL; - } - if (pvalue) { - *pvalue = NULL; - } - PyErr_SetString(PyExc_RuntimeError, "MultiDict changed during iteration"); - return -1; - } - - - pair_t *pair = list->pairs + pos->pos; - - if (pidentity) { - *pidentity = Py_NewRef(pair->identity);; - } - - if (pkey) { - PyObject *key = pair_list_calc_key(list, pair->key, pair->identity); - if (key == NULL) { - return -1; - } - if (key != pair->key) { - Py_SETREF(pair->key, key); - } else { - Py_CLEAR(key); - } - *pkey = Py_NewRef(pair->key); - } - if (pvalue) { - *pvalue = Py_NewRef(pair->value); - } - - ++pos->pos; - return 1; -} - - -static inline int -pair_list_next_by_identity(pair_list_t *list, pair_list_pos_t *pos, - PyObject *identity, - PyObject **pkey, PyObject **pvalue) -{ - if (pos->pos >= list->size) { - if (pkey) { - *pkey = NULL; - } - if (pvalue) { - *pvalue = NULL; - } - return 0; - } - - if (pos->version != list->version) { - if (pkey) { - *pkey = NULL; - } - if (pvalue) { - *pvalue = NULL; - } - PyErr_SetString(PyExc_RuntimeError, "MultiDict changed during iteration"); - return -1; - } - - - for (; pos->pos < list->size; ++pos->pos) { - pair_t *pair = list->pairs + pos->pos; - PyObject *ret = PyUnicode_RichCompare(identity, pair->identity, Py_EQ); - if (Py_IsFalse(ret)) { - Py_DECREF(ret); - continue; - } else if (ret == NULL) { - return -1; - } else { - // equals - Py_DECREF(ret); - } - - if (pkey) { - PyObject *key = pair_list_calc_key(list, pair->key, pair->identity); - if (key == NULL) { - return -1; - } - if (key != pair->key) { - Py_SETREF(pair->key, key); - } else { - Py_CLEAR(key); - } - *pkey = Py_NewRef(pair->key); - } - if (pvalue) { - *pvalue = Py_NewRef(pair->value); - } - ++pos->pos; - return 1; - } - if (pkey) { - *pkey = NULL; - } - if (pvalue) { - *pvalue = NULL; - } - return 0; -} - - -static inline int -pair_list_contains(pair_list_t *list, PyObject *key, PyObject **pret) -{ - Py_ssize_t pos; - - if (!PyUnicode_Check(key)) { - return 0; - } - - PyObject *ident = pair_list_calc_identity(list, key); - if (ident == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(ident); - if (hash == -1) { - goto fail; - } - - Py_ssize_t size = pair_list_len(list); - - for(pos = 0; pos < size; ++pos) { - pair_t * pair = list->pairs + pos; - if (hash != pair->hash) { - continue; - } - int tmp = str_cmp(ident, pair->identity); - if (tmp > 0) { - Py_DECREF(ident); - if (pret != NULL) { - *pret = Py_NewRef(pair->key); - } - return 1; - } - else if (tmp < 0) { - goto fail; - } - } - - Py_DECREF(ident); - if (pret != NULL) { - *pret = NULL; - } - return 0; -fail: - Py_XDECREF(ident); - if (pret != NULL) { - *pret = NULL; - } - return -1; -} - - -static inline int -pair_list_get_one(pair_list_t *list, PyObject *key, PyObject **ret) -{ - Py_ssize_t pos; - - PyObject *ident = pair_list_calc_identity(list, key); - if (ident == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(ident); - if (hash == -1) { - goto fail; - } - - Py_ssize_t size = pair_list_len(list); - - for(pos = 0; pos < size; ++pos) { - pair_t *pair = list->pairs + pos; - if (hash != pair->hash) { - continue; - } - int tmp = str_cmp(ident, pair->identity); - if (tmp > 0) { - Py_DECREF(ident); - *ret = Py_NewRef(pair->value); - return 0; - } - else if (tmp < 0) { - goto fail; - } - } - - Py_DECREF(ident); - return 0; -fail: - Py_XDECREF(ident); - return -1; -} - - -static inline int -pair_list_get_all(pair_list_t *list, PyObject *key, PyObject **ret) -{ - Py_ssize_t pos; - PyObject *res = NULL; - - PyObject *ident = pair_list_calc_identity(list, key); - if (ident == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(ident); - if (hash == -1) { - goto fail; - } - - Py_ssize_t size = pair_list_len(list); - for(pos = 0; pos < size; ++pos) { - pair_t *pair = list->pairs + pos; - - if (hash != pair->hash) { - continue; - } - int tmp = str_cmp(ident, pair->identity); - if (tmp > 0) { - if (res == NULL) { - res = PyList_New(1); - if (res == NULL) { - goto fail; - } - if (PyList_SetItem(res, 0, Py_NewRef(pair->value)) < 0) { - goto fail; - } - } - else if (PyList_Append(res, pair->value) < 0) { - goto fail; - } - } - else if (tmp < 0) { - goto fail; - } - } - - if (res != NULL) { - *ret = res; - } - Py_DECREF(ident); - return 0; - -fail: - Py_XDECREF(ident); - Py_XDECREF(res); - return -1; -} - - -static inline PyObject * -pair_list_set_default(pair_list_t *list, PyObject *key, PyObject *value) -{ - Py_ssize_t pos; - - PyObject *ident = pair_list_calc_identity(list, key); - if (ident == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(ident); - if (hash == -1) { - goto fail; - } - Py_ssize_t size = pair_list_len(list); - - for(pos = 0; pos < size; ++pos) { - pair_t * pair = list->pairs + pos; - - if (hash != pair->hash) { - continue; - } - int tmp = str_cmp(ident, pair->identity); - if (tmp > 0) { - Py_DECREF(ident); - return Py_NewRef(pair->value); - } - else if (tmp < 0) { - goto fail; - } - } - - if (_pair_list_add_with_hash(list, ident, key, value, hash) < 0) { - goto fail; - } - - Py_DECREF(ident); - return Py_NewRef(value); -fail: - Py_XDECREF(ident); - return NULL; -} - - -static inline int -pair_list_pop_one(pair_list_t *list, PyObject *key, PyObject **ret) -{ - Py_ssize_t pos; - PyObject *value = NULL; - - PyObject *ident = pair_list_calc_identity(list, key); - if (ident == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(ident); - if (hash == -1) { - goto fail; - } - - for (pos=0; pos < list->size; pos++) { - pair_t *pair = list->pairs + pos; - if (pair->hash != hash) { - continue; - } - int tmp = str_cmp(ident, pair->identity); - if (tmp > 0) { - value = Py_NewRef(pair->value); - if (pair_list_del_at(list, pos) < 0) { - goto fail; - } - Py_DECREF(ident); - *ret = value; - return 0; - } - else if (tmp < 0) { - goto fail; - } - } - - return 0; -fail: - Py_XDECREF(value); - Py_XDECREF(ident); - return -1; -} - - -static inline int -pair_list_pop_all(pair_list_t *list, PyObject *key, PyObject ** ret) -{ - Py_ssize_t pos; - PyObject *lst = NULL; - - PyObject *ident = pair_list_calc_identity(list, key); - if (ident == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(ident); - if (hash == -1) { - goto fail; - } - - if (list->size == 0) { - Py_DECREF(ident); - return 0; - } - - for (pos = list->size - 1; pos >= 0; pos--) { - pair_t *pair = list->pairs + pos; - if (hash != pair->hash) { - continue; - } - int tmp = str_cmp(ident, pair->identity); - if (tmp > 0) { - if (lst == NULL) { - lst = PyList_New(1); - if (lst == NULL) { - goto fail; - } - if (PyList_SetItem(lst, 0, Py_NewRef(pair->value)) < 0) { - goto fail; - } - } else if (PyList_Append(lst, pair->value) < 0) { - goto fail; - } - if (pair_list_del_at(list, pos) < 0) { - goto fail; - } - } - else if (tmp < 0) { - goto fail; - } - } - - if (lst != NULL) { - if (PyList_Reverse(lst) < 0) { - goto fail; - } - } - *ret = lst; - Py_DECREF(ident); - return 0; -fail: - Py_XDECREF(ident); - Py_XDECREF(lst); - return -1; -} - - -static inline PyObject * -pair_list_pop_item(pair_list_t *list) -{ - if (list->size == 0) { - PyErr_SetString(PyExc_KeyError, "empty multidict"); - return NULL; - } - - Py_ssize_t pos = list->size - 1; - pair_t *pair = list->pairs + pos; - PyObject *key = pair_list_calc_key(list, pair->key, pair->identity); - if (key == NULL) { - return NULL; - } - PyObject *ret = PyTuple_Pack(2, key, pair->value); - Py_CLEAR(key); - if (ret == NULL) { - return NULL; - } - - if (pair_list_del_at(list, pos) < 0) { - Py_DECREF(ret); - return NULL; - } - - return ret; -} - - -static inline int -pair_list_replace(pair_list_t *list, PyObject * key, PyObject *value) -{ - Py_ssize_t pos; - int found = 0; - - PyObject *identity = pair_list_calc_identity(list, key); - if (identity == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(identity); - if (hash == -1) { - goto fail; - } - - - for (pos = 0; pos < list->size; pos++) { - pair_t *pair = list->pairs + pos; - if (hash != pair->hash) { - continue; - } - int tmp = str_cmp(identity, pair->identity); - if (tmp > 0) { - found = 1; - Py_SETREF(pair->key, Py_NewRef(key)); - Py_SETREF(pair->value, Py_NewRef(value)); - break; - } - else if (tmp < 0) { - goto fail; - } - } - - if (!found) { - if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) { - goto fail; - } - Py_DECREF(identity); - return 0; - } - else { - list->version = NEXT_VERSION(); - if (_pair_list_drop_tail(list, identity, hash, pos+1) < 0) { - goto fail; - } - Py_DECREF(identity); - return 0; - } -fail: - Py_XDECREF(identity); - return -1; -} - - -static inline int -_dict_set_number(PyObject *dict, PyObject *key, Py_ssize_t num) -{ - PyObject *tmp = PyLong_FromSsize_t(num); - if (tmp == NULL) { - return -1; - } - - if (PyDict_SetItem(dict, key, tmp) < 0) { - Py_DECREF(tmp); - return -1; - } - - Py_DECREF(tmp); - return 0; -} - - -static inline int -pair_list_post_update(pair_list_t *list, PyObject* used) -{ - PyObject *tmp = NULL; - Py_ssize_t pos; - - for (pos = 0; pos < list->size; pos++) { - pair_t *pair = list->pairs + pos; - int status = PyDict_GetItemRef(used, pair->identity, &tmp); - if (status == -1) { - // exception set - return -1; - } - else if (status == 0) { - // not found - continue; - } - - Py_ssize_t num = PyLong_AsSsize_t(tmp); - Py_DECREF(tmp); - if (num == -1) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "invalid internal state"); - } - return -1; - } - - if (pos >= num) { - // del self[pos] - if (pair_list_del_at(list, pos) < 0) { - return -1; - } - pos--; - } - } - - list->version = NEXT_VERSION(); - return 0; -} - -// TODO: need refactoring function name -static inline int -_pair_list_update(pair_list_t *list, PyObject *key, - PyObject *value, PyObject *used, - PyObject *identity, Py_hash_t hash) -{ - PyObject *item = NULL; - Py_ssize_t pos; - int found; - - int status = PyDict_GetItemRef(used, identity, &item); - if (status == -1) { - // exception set - return -1; - } - else if (status == 0) { - // not found - pos = 0; - } - else { - pos = PyLong_AsSsize_t(item); - Py_DECREF(item); - if (pos == -1) { - if (!PyErr_Occurred()) { - PyErr_SetString(PyExc_RuntimeError, "invalid internal state"); - } - return -1; - } - } - - found = 0; - for (; pos < list->size; pos++) { - pair_t *pair = list->pairs + pos; - if (pair->hash != hash) { - continue; - } - - int ident_cmp_res = str_cmp(pair->identity, identity); - if (ident_cmp_res > 0) { - Py_SETREF(pair->key, Py_NewRef(key)); - Py_SETREF(pair->value, Py_NewRef(value)); - - if (_dict_set_number(used, pair->identity, pos + 1) < 0) { - return -1; - } - - found = 1; - break; - } - else if (ident_cmp_res < 0) { - return -1; - } - } - - if (!found) { - if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) { - return -1; - } - if (_dict_set_number(used, identity, list->size) < 0) { - return -1; - } - } - - return 0; -} - - -static inline int -pair_list_update_from_pair_list(pair_list_t *list, - PyObject* used, pair_list_t *other) -{ - Py_ssize_t pos; - Py_hash_t hash; - PyObject *identity = NULL; - PyObject *key = NULL; - bool recalc_identity = list->calc_ci_indentity != other->calc_ci_indentity; - - for (pos = 0; pos < other->size; pos++) { - pair_t *pair = other->pairs + pos; - if (recalc_identity) { - identity = pair_list_calc_identity(list, pair->key); - if (identity == NULL) { - goto fail; - } - hash = PyObject_Hash(identity); - if (hash == -1) { - goto fail; - } - /* materialize key */ - key = pair_list_calc_key(other, pair->key, identity); - if (key == NULL) { - goto fail; - } - } else { - identity = pair->identity; - hash = pair->hash; - key = pair->key; - } - if (used != NULL) { - if (_pair_list_update(list, key, pair->value, used, - identity, hash) < 0) { - goto fail; - } - } else { - if (_pair_list_add_with_hash(list, identity, key, - pair->value, hash) < 0) { - goto fail; - } - } - if (recalc_identity) { - Py_CLEAR(identity); - Py_CLEAR(key); - } - } - return 0; -fail: - if (recalc_identity) { - Py_CLEAR(identity); - Py_CLEAR(key); - } - return -1; -} - -static inline int -pair_list_update_from_dict(pair_list_t *list, PyObject* used, PyObject *kwds) -{ - Py_ssize_t pos = 0; - PyObject *identity = NULL; - PyObject *key = NULL; - PyObject *value = NULL; - - while(PyDict_Next(kwds, &pos, &key, &value)) { - Py_INCREF(key); - identity = pair_list_calc_identity(list, key); - if (identity == NULL) { - goto fail; - } - Py_hash_t hash = PyObject_Hash(identity); - if (hash == -1) { - goto fail; - } - if (used != NULL) { - if (_pair_list_update(list, key, value, used, identity, hash) < 0) { - goto fail; - } - } else { - if (_pair_list_add_with_hash(list, identity, key, value, hash) < 0) { - goto fail; - } - } - Py_CLEAR(identity); - Py_CLEAR(key); - } - return 0; -fail: - Py_CLEAR(identity); - Py_CLEAR(key); - return -1; -} - -static inline void _err_not_sequence(Py_ssize_t i) -{ - PyErr_Format(PyExc_TypeError, - "multidict cannot convert sequence element #%zd" - " to a sequence", - i); -} - -static inline void _err_bad_length(Py_ssize_t i, Py_ssize_t n) -{ - PyErr_Format(PyExc_ValueError, - "multidict update sequence element #%zd " - "has length %zd; 2 is required", - i, n); -} - -static inline void _err_cannot_fetch(Py_ssize_t i, const char * name) -{ - PyErr_Format(PyExc_ValueError, - "multidict update sequence element #%zd's " - "%s could not be fetched", name, i); -} - - -static int _pair_list_parse_item(Py_ssize_t i, PyObject *item, - PyObject **pkey, PyObject **pvalue) -{ - Py_ssize_t n; - - if (PyList_CheckExact(item)) { - n = PyList_GET_SIZE(item); - if (n != 2) { - _err_bad_length(i, n); - goto fail; - } - *pkey = Py_NewRef(PyList_GET_ITEM(item, 0)); - *pvalue = Py_NewRef(PyList_GET_ITEM(item, 1)); - } else if (PyTuple_CheckExact(item)) { - n = PyTuple_GET_SIZE(item); - if (n != 2) { - _err_bad_length(i, n); - goto fail; - } - *pkey = Py_NewRef(PyTuple_GET_ITEM(item, 0)); - *pvalue = Py_NewRef(PyTuple_GET_ITEM(item, 1)); - } else { - if (!PySequence_Check(item)) { - _err_not_sequence(i); - goto fail; - } - n = PySequence_Size(item); - if (n != 2) { - _err_bad_length(i, n); - goto fail; - } - *pkey = PySequence_ITEM(item, 0); - *pvalue = PySequence_ITEM(item, 1); - if (*pkey == NULL) { - _err_cannot_fetch(i, "key"); - goto fail; - } - if (*pvalue == NULL) { - _err_cannot_fetch(i, "value"); - goto fail; - } - } - return 0; -fail: - Py_CLEAR(*pkey); - Py_CLEAR(*pvalue); - return -1; -} - - -static inline int -pair_list_update_from_seq(pair_list_t *list, PyObject *used, PyObject *seq) -{ - PyObject *it = NULL; - PyObject *item = NULL; // seq[i] - - PyObject *key = NULL; - PyObject *value = NULL; - PyObject *identity = NULL; - - Py_ssize_t i; - Py_ssize_t size = -1; - - enum {LIST, TUPLE, ITER} kind; - - if (PyList_CheckExact(seq)) { - kind = LIST; - size = PyList_GET_SIZE(seq); - } else if (PyTuple_CheckExact(seq)) { - kind = TUPLE; - size = PyTuple_GET_SIZE(seq); - } else { - kind = ITER; - it = PyObject_GetIter(seq); - if (it == NULL) { - goto fail; - } - } - - for (i = 0; ; ++i) { // i - index into seq of current element - switch (kind) { - case LIST: - if (i >= size) { - goto exit; - } - item = PyList_GET_ITEM(seq, i); - if (item == NULL) { - goto fail; - } - Py_INCREF(item); - break; - case TUPLE: - if (i >= size) { - goto exit; - } - item = PyTuple_GET_ITEM(seq, i); - if (item == NULL) { - goto fail; - } - Py_INCREF(item); - break; - case ITER: - item = PyIter_Next(it); - if (item == NULL) { - if (PyErr_Occurred()) { - goto fail; - } - goto exit; - } - } - - if (_pair_list_parse_item(i, item, &key, &value) < 0) { - goto fail; - } - - identity = pair_list_calc_identity(list, key); - if (identity == NULL) { - goto fail; - } - - Py_hash_t hash = PyObject_Hash(identity); - if (hash == -1) { - goto fail; - } - - if (used) { - if (_pair_list_update(list, key, value, used, identity, hash) < 0) { - goto fail; - } - Py_CLEAR(identity); - Py_CLEAR(key); - Py_CLEAR(value); - } else { - if (_pair_list_add_with_hash_steal_refs(list, identity, - key, value, hash) < 0) { - goto fail; - } - identity = NULL; - key = NULL; - value = NULL; - } - Py_CLEAR(item); - } - -exit: - Py_CLEAR(it); - return 0; - -fail: - Py_CLEAR(identity); - Py_CLEAR(it); - Py_CLEAR(item); - Py_CLEAR(key); - Py_CLEAR(value); - return -1; -} - - -static inline int -pair_list_eq(pair_list_t *list, pair_list_t *other) -{ - Py_ssize_t pos; - - if (list == other) { - return 1; - } - - Py_ssize_t size = pair_list_len(list); - - if (size != pair_list_len(other)) { - return 0; - } - - for(pos = 0; pos < size; ++pos) { - pair_t *pair1 = list->pairs + pos; - pair_t *pair2 = other->pairs +pos; - - if (pair1->hash != pair2->hash) { - return 0; - } - - int cmp = PyObject_RichCompareBool(pair1->identity, pair2->identity, Py_EQ); - if (cmp < 0) { - return -1; - }; - if (cmp == 0) { - return 0; - } - - cmp = PyObject_RichCompareBool(pair1->value, pair2->value, Py_EQ); - if (cmp < 0) { - return -1; - }; - if (cmp == 0) { - return 0; - } - } - - return 1; -} - -static inline int -pair_list_eq_to_mapping(pair_list_t *list, PyObject *other) -{ - PyObject *key = NULL; - PyObject *avalue = NULL; - PyObject *bvalue; - - Py_ssize_t other_len; - - if (!PyMapping_Check(other)) { - PyErr_Format(PyExc_TypeError, - "other argument must be a mapping, not %s", - Py_TYPE(other)->tp_name); - return -1; - } - - other_len = PyMapping_Size(other); - if (other_len < 0) { - return -1; - } - if (pair_list_len(list) != other_len) { - return 0; - } - - pair_list_pos_t pos; - pair_list_init_pos(list, &pos); - - for(;;) { - int ret = pair_list_next(list, &pos, NULL, &key, &avalue); - if (ret < 0) { - return -1; - } - if (ret == 0) { - break; - } - ret = PyMapping_GetOptionalItem(other, key, &bvalue); - Py_CLEAR(key); - if (ret < 0) { - Py_CLEAR(avalue); - return -1; - } - - if (bvalue == NULL) { - Py_CLEAR(avalue); - return 0; - } - - int eq = PyObject_RichCompareBool(avalue, bvalue, Py_EQ); - Py_CLEAR(bvalue); - Py_CLEAR(avalue); - - if (eq <= 0) { - return eq; - } - } - - return 1; -} - - -static inline PyObject * -pair_list_repr(pair_list_t *list, PyObject *name, - bool show_keys, bool show_values) -{ - PyObject *key = NULL; - PyObject *value = NULL; - - bool comma = false; - Py_ssize_t pos; - uint64_t version = list->version; - - PyUnicodeWriter *writer = PyUnicodeWriter_Create(1024); - if (writer == NULL) - return NULL; - - if (PyUnicodeWriter_WriteChar(writer, '<') <0) - goto fail; - if (PyUnicodeWriter_WriteStr(writer, name) <0) - goto fail; - if (PyUnicodeWriter_WriteChar(writer, '(') <0) - goto fail; - - for (pos = 0; pos < list->size; ++pos) { - if (version != list->version) { - PyErr_SetString(PyExc_RuntimeError, "MultiDict changed during iteration"); - return NULL; - } - pair_t *pair = list->pairs + pos; - key = Py_NewRef(pair->key); - value = Py_NewRef(pair->value); - - if (comma) { - if (PyUnicodeWriter_WriteChar(writer, ',') <0) - goto fail; - if (PyUnicodeWriter_WriteChar(writer, ' ') <0) - goto fail; - } - if (show_keys) { - if (PyUnicodeWriter_WriteChar(writer, '\'') <0) - goto fail; - /* Don't need to convert key to istr, the text is the same*/ - if (PyUnicodeWriter_WriteStr(writer, key) <0) - goto fail; - if (PyUnicodeWriter_WriteChar(writer, '\'') <0) - goto fail; - } - if (show_keys && show_values) { - if (PyUnicodeWriter_WriteChar(writer, ':') <0) - goto fail; - if (PyUnicodeWriter_WriteChar(writer, ' ') <0) - goto fail; - } - if (show_values) { - if (PyUnicodeWriter_WriteRepr(writer, value) <0) - goto fail; - } - - comma = true; - Py_CLEAR(key); - Py_CLEAR(value); - } - - if (PyUnicodeWriter_WriteChar(writer, ')') <0) - goto fail; - if (PyUnicodeWriter_WriteChar(writer, '>') <0) - goto fail; - return PyUnicodeWriter_Finish(writer); -fail: - Py_CLEAR(key); - Py_CLEAR(value); - PyUnicodeWriter_Discard(writer); - return NULL; -} - - - -/***********************************************************************/ - -static inline int -pair_list_traverse(pair_list_t *list, visitproc visit, void *arg) -{ - pair_t *pair = NULL; - Py_ssize_t pos; - - for (pos = 0; pos < list->size; pos++) { - pair = list->pairs + pos; - // Don't need traverse the identity: it is a terminal - Py_VISIT(pair->key); - Py_VISIT(pair->value); - } - - return 0; -} - - -static inline int -pair_list_clear(pair_list_t *list) -{ - pair_t *pair = NULL; - Py_ssize_t pos; - - if (list->size == 0) { - return 0; - } - - list->version = NEXT_VERSION(); - for (pos = 0; pos < list->size; pos++) { - pair = list->pairs + pos; - Py_CLEAR(pair->key); - Py_CLEAR(pair->identity); - Py_CLEAR(pair->value); - } - list->size = 0; - if (list->pairs != list->buffer) { - PyMem_Free(list->pairs); - list->pairs = list->buffer; - } - - return 0; -} - - -#ifdef __cplusplus -} -#endif -#endif diff --git a/contrib/python/multidict/multidict/_multilib/parser.h b/contrib/python/multidict/multidict/_multilib/parser.h index 074f6fa7d9e..1ff3b33d37e 100644 --- a/contrib/python/multidict/multidict/_multilib/parser.h +++ b/contrib/python/multidict/multidict/_multilib/parser.h @@ -5,23 +5,26 @@ extern "C" { #endif -static int raise_unexpected_kwarg(const char *fname, PyObject* argname) +static inline int +raise_unexpected_kwarg(const char *fname, PyObject *argname) { PyErr_Format(PyExc_TypeError, "%.150s() got an unexpected keyword argument '%.150U'", - fname, argname); + fname, + argname); return -1; } -static int raise_missing_posarg(const char *fname, const char* argname) +static inline int +raise_missing_posarg(const char *fname, const char *argname) { PyErr_Format(PyExc_TypeError, "%.150s() missing 1 required positional argument: '%.150s'", - fname, argname); + fname, + argname); return -1; } - /* Parse FASTCALL|METH_KEYWORDS arguments as two args, the first arg is mandatory and the second one is optional. If the second arg is not passed it remains NULL pointer. @@ -32,19 +35,13 @@ The parser accepts three forms: 3. all named keyword args. */ -static int parse2(const char* fname, - PyObject*const *args, - Py_ssize_t nargs, - PyObject *kwnames, - Py_ssize_t minargs, - const char* arg1name, - PyObject **arg1, - const char* arg2name, - PyObject **arg2 -) +static inline int +parse2(const char *fname, PyObject *const *args, Py_ssize_t nargs, + PyObject *kwnames, Py_ssize_t minargs, const char *arg1name, + PyObject **arg1, const char *arg2name, PyObject **arg2) { - assert(minargs>=1); - assert(minargs<=2); + assert(minargs >= 1); + assert(minargs <= 2); if (kwnames != NULL) { Py_ssize_t kwsize = PyTuple_Size(kwnames); @@ -71,7 +68,8 @@ static int parse2(const char* fname, } else { return raise_unexpected_kwarg(fname, argname); } - } else if (PyUnicode_CompareWithASCIIString(argname, arg2name) == 0) { + } else if (PyUnicode_CompareWithASCIIString(argname, arg2name) == + 0) { argname = PyTuple_GetItem(kwnames, 1); if (argname == NULL) { return -1; @@ -113,13 +111,15 @@ static int parse2(const char* fname, } } else { if (nargs < 1) { - PyErr_Format(PyExc_TypeError, - "%.150s() missing 1 required positional argument: '%s'", - fname, arg1name); + PyErr_Format( + PyExc_TypeError, + "%.150s() missing 1 required positional argument: '%s'", + fname, + arg1name); return -1; } if (nargs < minargs || nargs > 2) { - const char* txt; + const char *txt; if (minargs == 2) { txt = "from 1 to 2 positional arguments"; } else { @@ -127,7 +127,9 @@ static int parse2(const char* fname, } PyErr_Format(PyExc_TypeError, "%.150s() takes %s but %zd were given", - fname, txt, nargs); + fname, + txt, + nargs); return -1; } *arg1 = args[0]; diff --git a/contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h b/contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h index 4b179e49319..1ab42c8fa57 100644 --- a/contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h +++ b/contrib/python/multidict/multidict/_multilib/pythoncapi_compat.h @@ -19,49 +19,48 @@ extern "C" { #endif #include <Python.h> -#include <stddef.h> // offsetof() +#include <stddef.h> // offsetof() // Python 3.11.0b4 added PyFrame_Back() to Python.h #if PY_VERSION_HEX < 0x030b00B4 && !defined(PYPY_VERSION) -# include "frameobject.h" // PyFrameObject, PyFrame_GetBack() +#include "frameobject.h" // PyFrameObject, PyFrame_GetBack() #endif #if PY_VERSION_HEX < 0x030C00A3 -# include <structmember.h> // T_SHORT, READONLY +#include <structmember.h> // T_SHORT, READONLY #endif - #ifndef _Py_CAST -# define _Py_CAST(type, expr) ((type)(expr)) +#define _Py_CAST(type, expr) ((type)(expr)) #endif // Static inline functions should use _Py_NULL rather than using directly NULL // to prevent C++ compiler warnings. On C23 and newer and on C++11 and newer, // _Py_NULL is defined as nullptr. #ifndef _Py_NULL -# if (defined (__STDC_VERSION__) && __STDC_VERSION__ > 201710L) \ - || (defined(__cplusplus) && __cplusplus >= 201103) -# define _Py_NULL nullptr -# else -# define _Py_NULL NULL -# endif +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ > 201710L) || \ + (defined(__cplusplus) && __cplusplus >= 201103) +#define _Py_NULL nullptr +#else +#define _Py_NULL NULL +#endif #endif // Cast argument to PyObject* type. #ifndef _PyObject_CAST -# define _PyObject_CAST(op) _Py_CAST(PyObject*, op) +#define _PyObject_CAST(op) _Py_CAST(PyObject *, op) #endif #ifndef Py_BUILD_ASSERT -# define Py_BUILD_ASSERT(cond) \ - do { \ - (void)sizeof(char [1 - 2 * !(cond)]); \ - } while(0) +#define Py_BUILD_ASSERT(cond) \ + do { \ + (void)sizeof(char[1 - 2 * !(cond)]); \ + } while (0) #endif - // bpo-42262 added Py_NewRef() to Python 3.10.0a3 #if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef) -static inline PyObject* _Py_NewRef(PyObject *obj) +static inline PyObject * +_Py_NewRef(PyObject *obj) { Py_INCREF(obj); return obj; @@ -69,10 +68,10 @@ static inline PyObject* _Py_NewRef(PyObject *obj) #define Py_NewRef(obj) _Py_NewRef(_PyObject_CAST(obj)) #endif - // bpo-42262 added Py_XNewRef() to Python 3.10.0a3 #if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_XNewRef) -static inline PyObject* _Py_XNewRef(PyObject *obj) +static inline PyObject * +_Py_XNewRef(PyObject *obj) { Py_XINCREF(obj); return obj; @@ -80,103 +79,106 @@ static inline PyObject* _Py_XNewRef(PyObject *obj) #define Py_XNewRef(obj) _Py_XNewRef(_PyObject_CAST(obj)) #endif - // bpo-39573 added Py_SET_REFCNT() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_REFCNT) -static inline void _Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) +static inline void +_Py_SET_REFCNT(PyObject *ob, Py_ssize_t refcnt) { ob->ob_refcnt = refcnt; } #define Py_SET_REFCNT(ob, refcnt) _Py_SET_REFCNT(_PyObject_CAST(ob), refcnt) #endif - // Py_SETREF() and Py_XSETREF() were added to Python 3.5.2. // It is excluded from the limited C API. -#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && !defined(Py_LIMITED_API) -#define Py_SETREF(dst, src) \ - do { \ - PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ - PyObject *_tmp_dst = (*_tmp_dst_ptr); \ - *_tmp_dst_ptr = _PyObject_CAST(src); \ - Py_DECREF(_tmp_dst); \ +#if (PY_VERSION_HEX < 0x03050200 && !defined(Py_SETREF)) && \ + !defined(Py_LIMITED_API) +#define Py_SETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject **, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_DECREF(_tmp_dst); \ } while (0) -#define Py_XSETREF(dst, src) \ - do { \ - PyObject **_tmp_dst_ptr = _Py_CAST(PyObject**, &(dst)); \ - PyObject *_tmp_dst = (*_tmp_dst_ptr); \ - *_tmp_dst_ptr = _PyObject_CAST(src); \ - Py_XDECREF(_tmp_dst); \ +#define Py_XSETREF(dst, src) \ + do { \ + PyObject **_tmp_dst_ptr = _Py_CAST(PyObject **, &(dst)); \ + PyObject *_tmp_dst = (*_tmp_dst_ptr); \ + *_tmp_dst_ptr = _PyObject_CAST(src); \ + Py_XDECREF(_tmp_dst); \ } while (0) #endif - // bpo-43753 added Py_Is(), Py_IsNone(), Py_IsTrue() and Py_IsFalse() // to Python 3.10.0b1. #if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_Is) -# define Py_Is(x, y) ((x) == (y)) +#define Py_Is(x, y) ((x) == (y)) #endif #if PY_VERSION_HEX < 0x030A00B1 && !defined(Py_IsNone) -# define Py_IsNone(x) Py_Is(x, Py_None) +#define Py_IsNone(x) Py_Is(x, Py_None) #endif -#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsTrue) -# define Py_IsTrue(x) Py_Is(x, Py_True) +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && \ + !defined(Py_IsTrue) +#define Py_IsTrue(x) Py_Is(x, Py_True) #endif -#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && !defined(Py_IsFalse) -# define Py_IsFalse(x) Py_Is(x, Py_False) +#if (PY_VERSION_HEX < 0x030A00B1 || defined(PYPY_VERSION)) && \ + !defined(Py_IsFalse) +#define Py_IsFalse(x) Py_Is(x, Py_False) #endif - // bpo-39573 added Py_SET_TYPE() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_TYPE) -static inline void _Py_SET_TYPE(PyObject *ob, PyTypeObject *type) +static inline void +_Py_SET_TYPE(PyObject *ob, PyTypeObject *type) { ob->ob_type = type; } #define Py_SET_TYPE(ob, type) _Py_SET_TYPE(_PyObject_CAST(ob), type) #endif - // bpo-39573 added Py_SET_SIZE() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_SET_SIZE) -static inline void _Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) +static inline void +_Py_SET_SIZE(PyVarObject *ob, Py_ssize_t size) { ob->ob_size = size; } -#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject*)(ob), size) +#define Py_SET_SIZE(ob, size) _Py_SET_SIZE((PyVarObject *)(ob), size) #endif - // bpo-40421 added PyFrame_GetCode() to Python 3.9.0b1 #if PY_VERSION_HEX < 0x030900B1 || defined(PYPY_VERSION) -static inline PyCodeObject* PyFrame_GetCode(PyFrameObject *frame) +static inline PyCodeObject * +PyFrame_GetCode(PyFrameObject *frame) { assert(frame != _Py_NULL); assert(frame->f_code != _Py_NULL); - return _Py_CAST(PyCodeObject*, Py_NewRef(frame->f_code)); + return _Py_CAST(PyCodeObject *, Py_NewRef(frame->f_code)); } #endif -static inline PyCodeObject* _PyFrame_GetCodeBorrow(PyFrameObject *frame) +static inline PyCodeObject * +_PyFrame_GetCodeBorrow(PyFrameObject *frame) { PyCodeObject *code = PyFrame_GetCode(frame); Py_DECREF(code); return code; } - // bpo-40421 added PyFrame_GetBack() to Python 3.9.0b1 #if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) -static inline PyFrameObject* PyFrame_GetBack(PyFrameObject *frame) +static inline PyFrameObject * +PyFrame_GetBack(PyFrameObject *frame) { assert(frame != _Py_NULL); - return _Py_CAST(PyFrameObject*, Py_XNewRef(frame->f_back)); + return _Py_CAST(PyFrameObject *, Py_XNewRef(frame->f_back)); } #endif #if !defined(PYPY_VERSION) -static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) +static inline PyFrameObject * +_PyFrame_GetBackBorrow(PyFrameObject *frame) { PyFrameObject *back = PyFrame_GetBack(frame); Py_XDECREF(back); @@ -184,10 +186,10 @@ static inline PyFrameObject* _PyFrame_GetBackBorrow(PyFrameObject *frame) } #endif - // bpo-40421 added PyFrame_GetLocals() to Python 3.11.0a7 #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) -static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) +static inline PyObject * +PyFrame_GetLocals(PyFrameObject *frame) { #if PY_VERSION_HEX >= 0x030400B1 if (PyFrame_FastToLocalsWithError(frame) < 0) { @@ -200,28 +202,28 @@ static inline PyObject* PyFrame_GetLocals(PyFrameObject *frame) } #endif - // bpo-40421 added PyFrame_GetGlobals() to Python 3.11.0a7 #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) -static inline PyObject* PyFrame_GetGlobals(PyFrameObject *frame) +static inline PyObject * +PyFrame_GetGlobals(PyFrameObject *frame) { return Py_NewRef(frame->f_globals); } #endif - // bpo-40421 added PyFrame_GetBuiltins() to Python 3.11.0a7 #if PY_VERSION_HEX < 0x030B00A7 && !defined(PYPY_VERSION) -static inline PyObject* PyFrame_GetBuiltins(PyFrameObject *frame) +static inline PyObject * +PyFrame_GetBuiltins(PyFrameObject *frame) { return Py_NewRef(frame->f_builtins); } #endif - // bpo-40421 added PyFrame_GetLasti() to Python 3.11.0b1 #if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) -static inline int PyFrame_GetLasti(PyFrameObject *frame) +static inline int +PyFrame_GetLasti(PyFrameObject *frame) { #if PY_VERSION_HEX >= 0x030A00A7 // bpo-27129: Since Python 3.10.0a7, f_lasti is an instruction offset, @@ -237,10 +239,10 @@ static inline int PyFrame_GetLasti(PyFrameObject *frame) } #endif - // gh-91248 added PyFrame_GetVar() to Python 3.12.0a2 #if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) -static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) +static inline PyObject * +PyFrame_GetVar(PyFrameObject *frame, PyObject *name) { PyObject *locals, *value; @@ -270,10 +272,9 @@ static inline PyObject* PyFrame_GetVar(PyFrameObject *frame, PyObject *name) } #endif - // gh-91248 added PyFrame_GetVarString() to Python 3.12.0a2 #if PY_VERSION_HEX < 0x030C00A2 && !defined(PYPY_VERSION) -static inline PyObject* +static inline PyObject * PyFrame_GetVarString(PyFrameObject *frame, const char *name) { PyObject *name_obj, *value; @@ -291,9 +292,9 @@ PyFrame_GetVarString(PyFrameObject *frame, const char *name) } #endif - // bpo-39947 added PyThreadState_GetInterpreter() to Python 3.9.0a5 -#if PY_VERSION_HEX < 0x030900A5 || (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000) +#if PY_VERSION_HEX < 0x030900A5 || \ + (defined(PYPY_VERSION) && PY_VERSION_HEX < 0x030B0000) static inline PyInterpreterState * PyThreadState_GetInterpreter(PyThreadState *tstate) { @@ -302,10 +303,10 @@ PyThreadState_GetInterpreter(PyThreadState *tstate) } #endif - // bpo-40429 added PyThreadState_GetFrame() to Python 3.9.0b1 #if PY_VERSION_HEX < 0x030900B1 && !defined(PYPY_VERSION) -static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) +static inline PyFrameObject * +PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != _Py_NULL); return _Py_CAST(PyFrameObject *, Py_XNewRef(tstate->frame)); @@ -313,7 +314,7 @@ static inline PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) #endif #if !defined(PYPY_VERSION) -static inline PyFrameObject* +static inline PyFrameObject * _PyThreadState_GetFrameBorrow(PyThreadState *tstate) { PyFrameObject *frame = PyThreadState_GetFrame(tstate); @@ -322,10 +323,10 @@ _PyThreadState_GetFrameBorrow(PyThreadState *tstate) } #endif - // bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a5 #if PY_VERSION_HEX < 0x030900A5 || defined(PYPY_VERSION) -static inline PyInterpreterState* PyInterpreterState_Get(void) +static inline PyInterpreterState * +PyInterpreterState_Get(void) { PyThreadState *tstate; PyInterpreterState *interp; @@ -342,10 +343,11 @@ static inline PyInterpreterState* PyInterpreterState_Get(void) } #endif - // bpo-39947 added PyInterpreterState_Get() to Python 3.9.0a6 -#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) -static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) +#if 0x030700A1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030900A6 && \ + !defined(PYPY_VERSION) +static inline uint64_t +PyThreadState_GetID(PyThreadState *tstate) { assert(tstate != _Py_NULL); return tstate->id; @@ -354,7 +356,8 @@ static inline uint64_t PyThreadState_GetID(PyThreadState *tstate) // bpo-43760 added PyThreadState_EnterTracing() to Python 3.11.0a2 #if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) -static inline void PyThreadState_EnterTracing(PyThreadState *tstate) +static inline void +PyThreadState_EnterTracing(PyThreadState *tstate) { tstate->tracing++; #if PY_VERSION_HEX >= 0x030A00A1 @@ -367,10 +370,11 @@ static inline void PyThreadState_EnterTracing(PyThreadState *tstate) // bpo-43760 added PyThreadState_LeaveTracing() to Python 3.11.0a2 #if PY_VERSION_HEX < 0x030B00A2 && !defined(PYPY_VERSION) -static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) +static inline void +PyThreadState_LeaveTracing(PyThreadState *tstate) { - int use_tracing = (tstate->c_tracefunc != _Py_NULL - || tstate->c_profilefunc != _Py_NULL); + int use_tracing = + (tstate->c_tracefunc != _Py_NULL || tstate->c_profilefunc != _Py_NULL); tstate->tracing--; #if PY_VERSION_HEX >= 0x030A00A1 tstate->cframe->use_tracing = use_tracing; @@ -380,28 +384,27 @@ static inline void PyThreadState_LeaveTracing(PyThreadState *tstate) } #endif - // bpo-37194 added PyObject_CallNoArgs() to Python 3.9.0a1 // PyObject_CallNoArgs() added to PyPy 3.9.16-v7.3.11 #if !defined(PyObject_CallNoArgs) && PY_VERSION_HEX < 0x030900A1 -static inline PyObject* PyObject_CallNoArgs(PyObject *func) +static inline PyObject * +PyObject_CallNoArgs(PyObject *func) { return PyObject_CallFunctionObjArgs(func, NULL); } #endif - // bpo-39245 made PyObject_CallOneArg() public (previously called // _PyObject_CallOneArg) in Python 3.9.0a4 // PyObject_CallOneArg() added to PyPy 3.9.16-v7.3.11 #if !defined(PyObject_CallOneArg) && PY_VERSION_HEX < 0x030900A4 -static inline PyObject* PyObject_CallOneArg(PyObject *func, PyObject *arg) +static inline PyObject * +PyObject_CallOneArg(PyObject *func, PyObject *arg) { return PyObject_CallFunctionObjArgs(func, arg, NULL); } #endif - // bpo-1635741 added PyModule_AddObjectRef() to Python 3.10.0a3 #if PY_VERSION_HEX < 0x030A00A3 static inline int @@ -426,10 +429,10 @@ PyModule_AddObjectRef(PyObject *module, const char *name, PyObject *value) } #endif - // bpo-40024 added PyModule_AddType() to Python 3.9.0a5 #if PY_VERSION_HEX < 0x030900A5 -static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) +static inline int +PyModule_AddType(PyObject *module, PyTypeObject *type) { const char *name, *dot; @@ -449,11 +452,11 @@ static inline int PyModule_AddType(PyObject *module, PyTypeObject *type) } #endif - // bpo-40241 added PyObject_GC_IsTracked() to Python 3.9.0a6. // bpo-4688 added _PyObject_GC_IS_TRACKED() to Python 2.7.0a2. #if PY_VERSION_HEX < 0x030900A6 && !defined(PYPY_VERSION) -static inline int PyObject_GC_IsTracked(PyObject* obj) +static inline int +PyObject_GC_IsTracked(PyObject *obj) { return (PyObject_IS_GC(obj) && _PyObject_GC_IS_TRACKED(obj)); } @@ -461,69 +464,89 @@ static inline int PyObject_GC_IsTracked(PyObject* obj) // bpo-40241 added PyObject_GC_IsFinalized() to Python 3.9.0a6. // bpo-18112 added _PyGCHead_FINALIZED() to Python 3.4.0 final. -#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && !defined(PYPY_VERSION) -static inline int PyObject_GC_IsFinalized(PyObject *obj) +#if PY_VERSION_HEX < 0x030900A6 && PY_VERSION_HEX >= 0x030400F0 && \ + !defined(PYPY_VERSION) +static inline int +PyObject_GC_IsFinalized(PyObject *obj) { - PyGC_Head *gc = _Py_CAST(PyGC_Head*, obj) - 1; + PyGC_Head *gc = _Py_CAST(PyGC_Head *, obj) - 1; return (PyObject_IS_GC(obj) && _PyGCHead_FINALIZED(gc)); } #endif - // bpo-39573 added Py_IS_TYPE() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 && !defined(Py_IS_TYPE) -static inline int _Py_IS_TYPE(PyObject *ob, PyTypeObject *type) { +static inline int +_Py_IS_TYPE(PyObject *ob, PyTypeObject *type) +{ return Py_TYPE(ob) == type; } #define Py_IS_TYPE(ob, type) _Py_IS_TYPE(_PyObject_CAST(ob), type) #endif - // bpo-46906 added PyFloat_Pack2() and PyFloat_Unpack2() to Python 3.11a7. // bpo-11734 added _PyFloat_Pack2() and _PyFloat_Unpack2() to Python 3.6.0b1. // Python 3.11a2 moved _PyFloat_Pack2() and _PyFloat_Unpack2() to the internal // C API: Python 3.11a2-3.11a6 versions are not supported. -#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) -static inline int PyFloat_Pack2(double x, char *p, int le) -{ return _PyFloat_Pack2(x, (unsigned char*)p, le); } +#if 0x030600B1 <= PY_VERSION_HEX && PY_VERSION_HEX <= 0x030B00A1 && \ + !defined(PYPY_VERSION) +static inline int +PyFloat_Pack2(double x, char *p, int le) +{ + return _PyFloat_Pack2(x, (unsigned char *)p, le); +} -static inline double PyFloat_Unpack2(const char *p, int le) -{ return _PyFloat_Unpack2((const unsigned char *)p, le); } +static inline double +PyFloat_Unpack2(const char *p, int le) +{ + return _PyFloat_Unpack2((const unsigned char *)p, le); +} #endif - // bpo-46906 added PyFloat_Pack4(), PyFloat_Pack8(), PyFloat_Unpack4() and // PyFloat_Unpack8() to Python 3.11a7. // Python 3.11a2 moved _PyFloat_Pack4(), _PyFloat_Pack8(), _PyFloat_Unpack4() // and _PyFloat_Unpack8() to the internal C API: Python 3.11a2-3.11a6 versions // are not supported. #if PY_VERSION_HEX <= 0x030B00A1 && !defined(PYPY_VERSION) -static inline int PyFloat_Pack4(double x, char *p, int le) -{ return _PyFloat_Pack4(x, (unsigned char*)p, le); } +static inline int +PyFloat_Pack4(double x, char *p, int le) +{ + return _PyFloat_Pack4(x, (unsigned char *)p, le); +} -static inline int PyFloat_Pack8(double x, char *p, int le) -{ return _PyFloat_Pack8(x, (unsigned char*)p, le); } +static inline int +PyFloat_Pack8(double x, char *p, int le) +{ + return _PyFloat_Pack8(x, (unsigned char *)p, le); +} -static inline double PyFloat_Unpack4(const char *p, int le) -{ return _PyFloat_Unpack4((const unsigned char *)p, le); } +static inline double +PyFloat_Unpack4(const char *p, int le) +{ + return _PyFloat_Unpack4((const unsigned char *)p, le); +} -static inline double PyFloat_Unpack8(const char *p, int le) -{ return _PyFloat_Unpack8((const unsigned char *)p, le); } +static inline double +PyFloat_Unpack8(const char *p, int le) +{ + return _PyFloat_Unpack8((const unsigned char *)p, le); +} #endif - // gh-92154 added PyCode_GetCode() to Python 3.11.0b1 #if PY_VERSION_HEX < 0x030B00B1 && !defined(PYPY_VERSION) -static inline PyObject* PyCode_GetCode(PyCodeObject *code) +static inline PyObject * +PyCode_GetCode(PyCodeObject *code) { return Py_NewRef(code->co_code); } #endif - // gh-95008 added PyCode_GetVarnames() to Python 3.11.0rc1 #if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) -static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) +static inline PyObject * +PyCode_GetVarnames(PyCodeObject *code) { return Py_NewRef(code->co_varnames); } @@ -531,7 +554,8 @@ static inline PyObject* PyCode_GetVarnames(PyCodeObject *code) // gh-95008 added PyCode_GetFreevars() to Python 3.11.0rc1 #if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) -static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) +static inline PyObject * +PyCode_GetFreevars(PyCodeObject *code) { return Py_NewRef(code->co_freevars); } @@ -539,35 +563,35 @@ static inline PyObject* PyCode_GetFreevars(PyCodeObject *code) // gh-95008 added PyCode_GetCellvars() to Python 3.11.0rc1 #if PY_VERSION_HEX < 0x030B00C1 && !defined(PYPY_VERSION) -static inline PyObject* PyCode_GetCellvars(PyCodeObject *code) +static inline PyObject * +PyCode_GetCellvars(PyCodeObject *code) { return Py_NewRef(code->co_cellvars); } #endif - // Py_UNUSED() was added to Python 3.4.0b2. #if PY_VERSION_HEX < 0x030400B2 && !defined(Py_UNUSED) -# if defined(__GNUC__) || defined(__clang__) -# define Py_UNUSED(name) _unused_ ## name __attribute__((unused)) -# else -# define Py_UNUSED(name) _unused_ ## name -# endif +#if defined(__GNUC__) || defined(__clang__) +#define Py_UNUSED(name) _unused_##name __attribute__((unused)) +#else +#define Py_UNUSED(name) _unused_##name +#endif #endif - // gh-105922 added PyImport_AddModuleRef() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A0 -static inline PyObject* PyImport_AddModuleRef(const char *name) +static inline PyObject * +PyImport_AddModuleRef(const char *name) { return Py_XNewRef(PyImport_AddModule(name)); } #endif - // gh-105927 added PyWeakref_GetRef() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D0000 -static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) +static inline int +PyWeakref_GetRef(PyObject *ref, PyObject **pobj) { PyObject *obj; if (ref != NULL && !PyWeakref_Check(ref)) { @@ -590,26 +614,26 @@ static inline int PyWeakref_GetRef(PyObject *ref, PyObject **pobj) } #endif - // bpo-36974 added PY_VECTORCALL_ARGUMENTS_OFFSET to Python 3.8b1 #ifndef PY_VECTORCALL_ARGUMENTS_OFFSET -# define PY_VECTORCALL_ARGUMENTS_OFFSET (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1)) +#define PY_VECTORCALL_ARGUMENTS_OFFSET \ + (_Py_CAST(size_t, 1) << (8 * sizeof(size_t) - 1)) #endif // bpo-36974 added PyVectorcall_NARGS() to Python 3.8b1 #if PY_VERSION_HEX < 0x030800B1 -static inline Py_ssize_t PyVectorcall_NARGS(size_t n) +static inline Py_ssize_t +PyVectorcall_NARGS(size_t n) { return n & ~PY_VECTORCALL_ARGUMENTS_OFFSET; } #endif - // gh-105922 added PyObject_Vectorcall() to Python 3.9.0a4 #if PY_VERSION_HEX < 0x030900A4 -static inline PyObject* -PyObject_Vectorcall(PyObject *callable, PyObject *const *args, - size_t nargsf, PyObject *kwnames) +static inline PyObject * +PyObject_Vectorcall(PyObject *callable, PyObject *const *args, size_t nargsf, + PyObject *kwnames) { #if PY_VERSION_HEX >= 0x030800B1 && !defined(PYPY_VERSION) // bpo-36974 added _PyObject_Vectorcall() to Python 3.8.0b1 @@ -631,8 +655,7 @@ PyObject_Vectorcall(PyObject *callable, PyObject *const *args, nposargs = (Py_ssize_t)PyVectorcall_NARGS(nargsf); if (kwnames) { nkwargs = PyTuple_GET_SIZE(kwnames); - } - else { + } else { nkwargs = 0; } @@ -641,7 +664,7 @@ PyObject_Vectorcall(PyObject *callable, PyObject *const *args, goto error; } if (nposargs) { - for (i=0; i < nposargs; i++) { + for (i = 0; i < nposargs; i++) { PyTuple_SET_ITEM(posargs, i, Py_NewRef(*args)); args++; } @@ -661,8 +684,7 @@ PyObject_Vectorcall(PyObject *callable, PyObject *const *args, goto error; } } - } - else { + } else { kwargs = NULL; } @@ -679,7 +701,6 @@ error: } #endif - // gh-106521 added PyObject_GetOptionalAttr() and // PyObject_GetOptionalAttrString() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 @@ -706,7 +727,8 @@ PyObject_GetOptionalAttr(PyObject *obj, PyObject *attr_name, PyObject **result) } static inline int -PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject **result) +PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, + PyObject **result) { PyObject *name_obj; int rc; @@ -725,7 +747,6 @@ PyObject_GetOptionalAttrString(PyObject *obj, const char *attr_name, PyObject ** } #endif - // gh-106307 added PyObject_GetOptionalAttr() and // PyMapping_GetOptionalItemString() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 @@ -744,7 +765,8 @@ PyMapping_GetOptionalItem(PyObject *obj, PyObject *key, PyObject **result) } static inline int -PyMapping_GetOptionalItemString(PyObject *obj, const char *key, PyObject **result) +PyMapping_GetOptionalItemString(PyObject *obj, const char *key, + PyObject **result) { PyObject *key_obj; int rc; @@ -785,7 +807,6 @@ PyMapping_HasKeyStringWithError(PyObject *obj, const char *key) } #endif - // gh-108511 added PyObject_HasAttrWithError() and // PyObject_HasAttrStringWithError() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 @@ -808,7 +829,6 @@ PyObject_HasAttrStringWithError(PyObject *obj, const char *attr) } #endif - // gh-106004 added PyDict_GetItemRef() and PyDict_GetItemStringRef() // to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 @@ -851,7 +871,6 @@ PyDict_GetItemStringRef(PyObject *mp, const char *key, PyObject **result) } #endif - // gh-106307 added PyModule_Add() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 static inline int @@ -863,13 +882,13 @@ PyModule_Add(PyObject *mod, const char *name, PyObject *value) } #endif - // gh-108014 added Py_IsFinalizing() to Python 3.13.0a1 // bpo-1856 added _Py_Finalizing to Python 3.2.1b1. // _Py_IsFinalizing() was added to PyPy 7.3.0. -#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) \ - && (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) -static inline int Py_IsFinalizing(void) +#if (0x030201B1 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030D00A1) && \ + (!defined(PYPY_VERSION_NUM) || PYPY_VERSION_NUM >= 0x7030000) +static inline int +Py_IsFinalizing(void) { #if PY_VERSION_HEX >= 0x030700A1 // _Py_IsFinalizing() was added to Python 3.7.0a1. @@ -880,10 +899,10 @@ static inline int Py_IsFinalizing(void) } #endif - // gh-108323 added PyDict_ContainsString() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 -static inline int PyDict_ContainsString(PyObject *op, const char *key) +static inline int +PyDict_ContainsString(PyObject *op, const char *key) { PyObject *key_obj = PyUnicode_FromString(key); if (key_obj == NULL) { @@ -895,10 +914,10 @@ static inline int PyDict_ContainsString(PyObject *op, const char *key) } #endif - // gh-108445 added PyLong_AsInt() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 -static inline int PyLong_AsInt(PyObject *obj) +static inline int +PyLong_AsInt(PyObject *obj) { #ifdef PYPY_VERSION long value = PyLong_AsLong(obj); @@ -917,7 +936,6 @@ static inline int PyLong_AsInt(PyObject *obj) } #endif - // gh-107073 added PyObject_VisitManagedDict() to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 static inline int @@ -945,7 +963,7 @@ PyObject_ClearManagedDict(PyObject *obj) // gh-108867 added PyThreadState_GetUnchecked() to Python 3.13.0a1 // Python 3.5.2 added _PyThreadState_UncheckedGet(). #if PY_VERSION_HEX >= 0x03050200 && PY_VERSION_HEX < 0x030D00A1 -static inline PyThreadState* +static inline PyThreadState * PyThreadState_GetUnchecked(void) { return _PyThreadState_UncheckedGet(); @@ -956,7 +974,8 @@ PyThreadState_GetUnchecked(void) // to Python 3.13.0a1 #if PY_VERSION_HEX < 0x030D00A1 static inline int -PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_len) +PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, + Py_ssize_t str_len) { Py_ssize_t len; const void *utf8; @@ -971,8 +990,7 @@ PyUnicode_EqualToUTF8AndSize(PyObject *unicode, const char *str, Py_ssize_t str_ if (PyUnicode_IS_ASCII(unicode)) { utf8 = PyUnicode_DATA(unicode); len = PyUnicode_GET_LENGTH(unicode); - } - else { + } else { utf8 = PyUnicode_AsUTF8AndSize(unicode, &len); if (utf8 == NULL) { // Memory allocation failure. The API cannot report error, @@ -1025,7 +1043,6 @@ PyUnicode_EqualToUTF8(PyObject *unicode, const char *str) } #endif - // gh-111138 added PyList_Extend() and PyList_Clear() to Python 3.13.0a2 #if PY_VERSION_HEX < 0x030D00A2 static inline int @@ -1059,10 +1076,11 @@ PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) // bpo-16991 added _PyDict_Pop() to Python 3.5.0b2. // Python 3.6.0b3 changed _PyDict_Pop() first argument type to PyObject*. // Python 3.13.0a1 removed _PyDict_Pop(). -#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || PY_VERSION_HEX >= 0x030D0000 +#if defined(PYPY_VERSION) || PY_VERSION_HEX < 0x030500b2 || \ + PY_VERSION_HEX >= 0x030D0000 value = PyObject_CallMethod(dict, "pop", "O", key); #elif PY_VERSION_HEX < 0x030600b3 - value = _PyDict_Pop(_Py_CAST(PyDictObject*, dict), key, NULL); + value = _PyDict_Pop(_Py_CAST(PyDictObject *, dict), key, NULL); #else value = _PyDict_Pop(dict, key, NULL); #endif @@ -1078,8 +1096,7 @@ PyDict_Pop(PyObject *dict, PyObject *key, PyObject **result) } if (result) { *result = value; - } - else { + } else { Py_DECREF(value); } return 1; @@ -1102,43 +1119,51 @@ PyDict_PopString(PyObject *dict, const char *key, PyObject **result) } #endif - #if PY_VERSION_HEX < 0x030200A4 // Python 3.2.0a4 added Py_hash_t type typedef Py_ssize_t Py_hash_t; #endif - // gh-111545 added Py_HashPointer() to Python 3.13.0a3 #if PY_VERSION_HEX < 0x030D00A3 -static inline Py_hash_t Py_HashPointer(const void *ptr) +static inline Py_hash_t +Py_HashPointer(const void *ptr) { #if PY_VERSION_HEX >= 0x030900A4 && !defined(PYPY_VERSION) return _Py_HashPointer(ptr); #else - return _Py_HashPointer(_Py_CAST(void*, ptr)); + return _Py_HashPointer(_Py_CAST(void *, ptr)); #endif } #endif - // Python 3.13a4 added a PyTime API. // Use the private API added to Python 3.5. -#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000 +#if PY_VERSION_HEX < 0x030D00A4 && PY_VERSION_HEX >= 0x03050000 typedef _PyTime_t PyTime_t; #define PyTime_MIN _PyTime_MIN #define PyTime_MAX _PyTime_MAX -static inline double PyTime_AsSecondsDouble(PyTime_t t) -{ return _PyTime_AsSecondsDouble(t); } +static inline double +PyTime_AsSecondsDouble(PyTime_t t) +{ + return _PyTime_AsSecondsDouble(t); +} -static inline int PyTime_Monotonic(PyTime_t *result) -{ return _PyTime_GetMonotonicClockWithInfo(result, NULL); } +static inline int +PyTime_Monotonic(PyTime_t *result) +{ + return _PyTime_GetMonotonicClockWithInfo(result, NULL); +} -static inline int PyTime_Time(PyTime_t *result) -{ return _PyTime_GetSystemClockWithInfo(result, NULL); } +static inline int +PyTime_Time(PyTime_t *result) +{ + return _PyTime_GetSystemClockWithInfo(result, NULL); +} -static inline int PyTime_PerfCounter(PyTime_t *result) +static inline int +PyTime_PerfCounter(PyTime_t *result) { #if PY_VERSION_HEX >= 0x03070000 && !defined(PYPY_VERSION) return _PyTime_GetPerfCounterWithInfo(result, NULL); @@ -1211,17 +1236,16 @@ static inline int PyTime_PerfCounter(PyTime_t *result) // gh-111389 added hash constants to Python 3.13.0a5. These constants were // added first as private macros to Python 3.4.0b1 and PyPy 7.3.8. -#if (!defined(PyHASH_BITS) \ - && ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) \ - || (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 \ - && PYPY_VERSION_NUM >= 0x07030800))) -# define PyHASH_BITS _PyHASH_BITS -# define PyHASH_MODULUS _PyHASH_MODULUS -# define PyHASH_INF _PyHASH_INF -# define PyHASH_IMAG _PyHASH_IMAG +#if (!defined(PyHASH_BITS) && \ + ((!defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x030400B1) || \ + (defined(PYPY_VERSION) && PY_VERSION_HEX >= 0x03070000 && \ + PYPY_VERSION_NUM >= 0x07030800))) +#define PyHASH_BITS _PyHASH_BITS +#define PyHASH_MODULUS _PyHASH_MODULUS +#define PyHASH_INF _PyHASH_INF +#define PyHASH_IMAG _PyHASH_IMAG #endif - // gh-111545 added Py_GetConstant() and Py_GetConstantBorrowed() // to Python 3.13.0a6 #if PY_VERSION_HEX < 0x030D00A6 && !defined(Py_CONSTANT_NONE) @@ -1237,9 +1261,10 @@ static inline int PyTime_PerfCounter(PyTime_t *result) #define Py_CONSTANT_EMPTY_BYTES 8 #define Py_CONSTANT_EMPTY_TUPLE 9 -static inline PyObject* Py_GetConstant(unsigned int constant_id) +static inline PyObject * +Py_GetConstant(unsigned int constant_id) { - static PyObject* constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL}; + static PyObject *constants[Py_CONSTANT_EMPTY_TUPLE + 1] = {NULL}; if (constants[Py_CONSTANT_NONE] == NULL) { constants[Py_CONSTANT_NONE] = Py_None; @@ -1275,7 +1300,7 @@ static inline PyObject* Py_GetConstant(unsigned int constant_id) // goto dance to avoid compiler warnings about Py_FatalError() goto init_done; -fatal_error: + fatal_error: // This case should never happen Py_FatalError("Py_GetConstant() failed to get constants"); } @@ -1283,14 +1308,14 @@ fatal_error: init_done: if (constant_id <= Py_CONSTANT_EMPTY_TUPLE) { return Py_NewRef(constants[constant_id]); - } - else { + } else { PyErr_BadInternalCall(); return NULL; } } -static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id) +static inline PyObject * +Py_GetConstantBorrowed(unsigned int constant_id) { PyObject *obj = Py_GetConstant(constant_id); Py_XDECREF(obj); @@ -1298,7 +1323,6 @@ static inline PyObject* Py_GetConstantBorrowed(unsigned int constant_id) } #endif - // gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 #if PY_VERSION_HEX < 0x030D00A4 static inline PyObject * @@ -1310,7 +1334,6 @@ PyList_GetItemRef(PyObject *op, Py_ssize_t index) } #endif - // gh-114329 added PyList_GetItemRef() to Python 3.13.0a4 #if PY_VERSION_HEX < 0x030D00A4 static inline int @@ -1329,8 +1352,7 @@ PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, // present if (result) { *result = value; - } - else { + } else { Py_DECREF(value); } return 1; @@ -1352,26 +1374,28 @@ PyDict_SetDefaultRef(PyObject *d, PyObject *key, PyObject *default_value, #endif #if PY_VERSION_HEX < 0x030D00B3 -# define Py_BEGIN_CRITICAL_SECTION(op) { -# define Py_END_CRITICAL_SECTION() } -# define Py_BEGIN_CRITICAL_SECTION2(a, b) { -# define Py_END_CRITICAL_SECTION2() } +#define Py_BEGIN_CRITICAL_SECTION(op) { +#define Py_END_CRITICAL_SECTION() } +#define Py_BEGIN_CRITICAL_SECTION2(a, b) { +#define Py_END_CRITICAL_SECTION2() } #endif -#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) +#if PY_VERSION_HEX < 0x030E0000 && PY_VERSION_HEX >= 0x03060000 && \ + !defined(PYPY_VERSION) typedef struct PyUnicodeWriter PyUnicodeWriter; -static inline void PyUnicodeWriter_Discard(PyUnicodeWriter *writer) +static inline void +PyUnicodeWriter_Discard(PyUnicodeWriter *writer) { - _PyUnicodeWriter_Dealloc((_PyUnicodeWriter*)writer); + _PyUnicodeWriter_Dealloc((_PyUnicodeWriter *)writer); PyMem_Free(writer); } -static inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) +static inline PyUnicodeWriter * +PyUnicodeWriter_Create(Py_ssize_t length) { if (length < 0) { - PyErr_SetString(PyExc_ValueError, - "length must be positive"); + PyErr_SetString(PyExc_ValueError, "length must be positive"); return NULL; } @@ -1392,10 +1416,11 @@ static inline PyUnicodeWriter* PyUnicodeWriter_Create(Py_ssize_t length) return pub_writer; } -static inline PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer) +static inline PyObject * +PyUnicodeWriter_Finish(PyUnicodeWriter *writer) { - PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter*)writer); - assert(((_PyUnicodeWriter*)writer)->buffer == NULL); + PyObject *str = _PyUnicodeWriter_Finish((_PyUnicodeWriter *)writer); + assert(((_PyUnicodeWriter *)writer)->buffer == NULL); PyMem_Free(writer); return str; } @@ -1409,7 +1434,7 @@ PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch) return -1; } - return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter*)writer, ch); + return _PyUnicodeWriter_WriteChar((_PyUnicodeWriter *)writer, ch); } static inline int @@ -1420,7 +1445,7 @@ PyUnicodeWriter_WriteStr(PyUnicodeWriter *writer, PyObject *obj) return -1; } - int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str); Py_DECREF(str); return res; } @@ -1433,14 +1458,14 @@ PyUnicodeWriter_WriteRepr(PyUnicodeWriter *writer, PyObject *obj) return -1; } - int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str); Py_DECREF(str); return res; } static inline int -PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, - const char *str, Py_ssize_t size) +PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, const char *str, + Py_ssize_t size) { if (size < 0) { size = (Py_ssize_t)strlen(str); @@ -1451,14 +1476,14 @@ PyUnicodeWriter_WriteUTF8(PyUnicodeWriter *writer, return -1; } - int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str_obj); Py_DECREF(str_obj); return res; } static inline int -PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, - const wchar_t *str, Py_ssize_t size) +PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, const wchar_t *str, + Py_ssize_t size) { if (size < 0) { size = (Py_ssize_t)wcslen(str); @@ -1469,7 +1494,7 @@ PyUnicodeWriter_WriteWideChar(PyUnicodeWriter *writer, return -1; } - int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str_obj); + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str_obj); Py_DECREF(str_obj); return res; } @@ -1491,8 +1516,8 @@ PyUnicodeWriter_WriteSubstring(PyUnicodeWriter *writer, PyObject *str, return -1; } - return _PyUnicodeWriter_WriteSubstring((_PyUnicodeWriter*)writer, str, - start, end); + return _PyUnicodeWriter_WriteSubstring( + (_PyUnicodeWriter *)writer, str, start, end); } static inline int @@ -1506,7 +1531,7 @@ PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) return -1; } - int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter*)writer, str); + int res = _PyUnicodeWriter_WriteStr((_PyUnicodeWriter *)writer, str); Py_DECREF(str); return res; } @@ -1514,10 +1539,12 @@ PyUnicodeWriter_Format(PyUnicodeWriter *writer, const char *format, ...) // gh-116560 added PyLong_GetSign() to Python 3.14.0a0 #if PY_VERSION_HEX < 0x030E00A0 -static inline int PyLong_GetSign(PyObject *obj, int *sign) +static inline int +PyLong_GetSign(PyObject *obj, int *sign) { if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expect int, got %s", Py_TYPE(obj)->tp_name); + PyErr_Format( + PyExc_TypeError, "expect int, got %s", Py_TYPE(obj)->tp_name); return -1; } @@ -1528,52 +1555,60 @@ static inline int PyLong_GetSign(PyObject *obj, int *sign) // gh-126061 added PyLong_IsPositive/Negative/Zero() to Python in 3.14.0a2 #if PY_VERSION_HEX < 0x030E00A2 -static inline int PyLong_IsPositive(PyObject *obj) +static inline int +PyLong_IsPositive(PyObject *obj) { if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + PyErr_Format( + PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); return -1; } return _PyLong_Sign(obj) == 1; } -static inline int PyLong_IsNegative(PyObject *obj) +static inline int +PyLong_IsNegative(PyObject *obj) { if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + PyErr_Format( + PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); return -1; } return _PyLong_Sign(obj) == -1; } -static inline int PyLong_IsZero(PyObject *obj) +static inline int +PyLong_IsZero(PyObject *obj) { if (!PyLong_Check(obj)) { - PyErr_Format(PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); + PyErr_Format( + PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); return -1; } return _PyLong_Sign(obj) == 0; } #endif - // gh-124502 added PyUnicode_Equal() to Python 3.14.0a0 #if PY_VERSION_HEX < 0x030E00A0 -static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) +static inline int +PyUnicode_Equal(PyObject *str1, PyObject *str2) { if (!PyUnicode_Check(str1)) { - PyErr_Format(PyExc_TypeError, "first argument must be str, not %s", + PyErr_Format(PyExc_TypeError, + "first argument must be str, not %s", Py_TYPE(str1)->tp_name); return -1; } if (!PyUnicode_Check(str2)) { - PyErr_Format(PyExc_TypeError, "second argument must be str, not %s", + PyErr_Format(PyExc_TypeError, + "second argument must be str, not %s", Py_TYPE(str2)->tp_name); return -1; } #if PY_VERSION_HEX >= 0x030d0000 && !defined(PYPY_VERSION) - PyAPI_FUNC(int) _PyUnicode_Equal(PyObject *str1, PyObject *str2); + PyAPI_FUNC(int) _PyUnicode_Equal(PyObject * str1, PyObject * str2); return _PyUnicode_Equal(str1, str2); #elif PY_VERSION_HEX >= 0x03060000 && !defined(PYPY_VERSION) @@ -1586,18 +1621,18 @@ static inline int PyUnicode_Equal(PyObject *str1, PyObject *str2) } #endif - // gh-121645 added PyBytes_Join() to Python 3.14.0a0 #if PY_VERSION_HEX < 0x030E00A0 -static inline PyObject* PyBytes_Join(PyObject *sep, PyObject *iterable) +static inline PyObject * +PyBytes_Join(PyObject *sep, PyObject *iterable) { return _PyBytes_Join(sep, iterable); } #endif - #if PY_VERSION_HEX < 0x030E00A0 -static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) +static inline Py_hash_t +Py_HashBuffer(const void *ptr, Py_ssize_t len) { #if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) PyAPI_FUNC(Py_hash_t) _Py_HashBytes(const void *src, Py_ssize_t len); @@ -1605,7 +1640,7 @@ static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) return _Py_HashBytes(ptr, len); #else Py_hash_t hash; - PyObject *bytes = PyBytes_FromStringAndSize((const char*)ptr, len); + PyObject *bytes = PyBytes_FromStringAndSize((const char *)ptr, len); if (bytes == NULL) { return -1; } @@ -1616,9 +1651,9 @@ static inline Py_hash_t Py_HashBuffer(const void *ptr, Py_ssize_t len) } #endif - #if PY_VERSION_HEX < 0x030E00A0 -static inline int PyIter_NextItem(PyObject *iter, PyObject **item) +static inline int +PyIter_NextItem(PyObject *iter, PyObject **item) { iternextfunc tp_iternext; @@ -1628,7 +1663,8 @@ static inline int PyIter_NextItem(PyObject *iter, PyObject **item) tp_iternext = Py_TYPE(iter)->tp_iternext; if (tp_iternext == NULL) { *item = NULL; - PyErr_Format(PyExc_TypeError, "expected an iterator, got '%s'", + PyErr_Format(PyExc_TypeError, + "expected an iterator, got '%s'", Py_TYPE(iter)->tp_name); return -1; } @@ -1647,33 +1683,37 @@ static inline int PyIter_NextItem(PyObject *iter, PyObject **item) } #endif - #if PY_VERSION_HEX < 0x030E00A0 -static inline PyObject* PyLong_FromInt32(int32_t value) +static inline PyObject * +PyLong_FromInt32(int32_t value) { Py_BUILD_ASSERT(sizeof(long) >= 4); return PyLong_FromLong(value); } -static inline PyObject* PyLong_FromInt64(int64_t value) +static inline PyObject * +PyLong_FromInt64(int64_t value) { Py_BUILD_ASSERT(sizeof(long long) >= 8); return PyLong_FromLongLong(value); } -static inline PyObject* PyLong_FromUInt32(uint32_t value) +static inline PyObject * +PyLong_FromUInt32(uint32_t value) { Py_BUILD_ASSERT(sizeof(unsigned long) >= 4); return PyLong_FromUnsignedLong(value); } -static inline PyObject* PyLong_FromUInt64(uint64_t value) +static inline PyObject * +PyLong_FromUInt64(uint64_t value) { Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8); return PyLong_FromUnsignedLongLong(value); } -static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) +static inline int +PyLong_AsInt32(PyObject *obj, int32_t *pvalue) { Py_BUILD_ASSERT(sizeof(int) == 4); int value = PyLong_AsInt(obj); @@ -1684,7 +1724,8 @@ static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue) return 0; } -static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) +static inline int +PyLong_AsInt64(PyObject *obj, int64_t *pvalue) { Py_BUILD_ASSERT(sizeof(long long) == 8); long long value = PyLong_AsLongLong(obj); @@ -1695,7 +1736,8 @@ static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue) return 0; } -static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) +static inline int +PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) { Py_BUILD_ASSERT(sizeof(long) >= 4); unsigned long value = PyLong_AsUnsignedLong(obj); @@ -1713,7 +1755,8 @@ static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue) return 0; } -static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) +static inline int +PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) { Py_BUILD_ASSERT(sizeof(long long) == 8); unsigned long long value = PyLong_AsUnsignedLongLong(obj); @@ -1725,9 +1768,9 @@ static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue) } #endif - // gh-102471 added import and export API for integers to 3.14.0a2. -#if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) +#if PY_VERSION_HEX < 0x030E00A2 && PY_VERSION_HEX >= 0x03000000 && \ + !defined(PYPY_VERSION) // Helpers to access PyLongObject internals. static inline void _PyLong_SetSignAndDigitCount(PyLongObject *op, int sign, Py_ssize_t size) @@ -1747,17 +1790,17 @@ _PyLong_DigitCount(const PyLongObject *op) #if PY_VERSION_HEX >= 0x030C0000 return (Py_ssize_t)(op->long_value.lv_tag >> 3); #else - return _PyLong_Sign((PyObject*)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op); + return _PyLong_Sign((PyObject *)op) < 0 ? -Py_SIZE(op) : Py_SIZE(op); #endif } -static inline digit* +static inline digit * _PyLong_GetDigits(const PyLongObject *op) { #if PY_VERSION_HEX >= 0x030C0000 - return (digit*)(op->long_value.ob_digit); + return (digit *)(op->long_value.ob_digit); #else - return (digit*)(op->ob_digit); + return (digit *)(op->ob_digit); #endif } @@ -1778,7 +1821,7 @@ typedef struct PyLongExport { typedef struct PyLongWriter PyLongWriter; -static inline const PyLongLayout* +static inline const PyLongLayout * PyLong_GetNativeLayout(void) { static const PyLongLayout PyLong_LAYOUT = { @@ -1796,13 +1839,13 @@ PyLong_Export(PyObject *obj, PyLongExport *export_long) { if (!PyLong_Check(obj)) { memset(export_long, 0, sizeof(*export_long)); - PyErr_Format(PyExc_TypeError, "expected int, got %s", - Py_TYPE(obj)->tp_name); + PyErr_Format( + PyExc_TypeError, "expected int, got %s", Py_TYPE(obj)->tp_name); return -1; } // Fast-path: try to convert to a int64_t - PyLongObject *self = (PyLongObject*)obj; + PyLongObject *self = (PyLongObject *)obj; int overflow; #if SIZEOF_LONG == 8 long value = PyLong_AsLongAndOverflow(obj, &overflow); @@ -1820,8 +1863,7 @@ PyLong_Export(PyObject *obj, PyLongExport *export_long) export_long->ndigits = 0; export_long->digits = 0; export_long->_reserved = 0; - } - else { + } else { export_long->value = 0; export_long->negative = _PyLong_Sign(obj) < 0; export_long->ndigits = _PyLong_DigitCount(self); @@ -1837,7 +1879,7 @@ PyLong_Export(PyObject *obj, PyLongExport *export_long) static inline void PyLong_FreeExport(PyLongExport *export_long) { - PyObject *obj = (PyObject*)export_long->_reserved; + PyObject *obj = (PyObject *)export_long->_reserved; if (obj) { export_long->_reserved = 0; @@ -1845,7 +1887,7 @@ PyLong_FreeExport(PyLongExport *export_long) } } -static inline PyLongWriter* +static inline PyLongWriter * PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) { if (ndigits <= 0) { @@ -1858,10 +1900,10 @@ PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits) if (obj == NULL) { return NULL; } - _PyLong_SetSignAndDigitCount(obj, negative?-1:1, ndigits); + _PyLong_SetSignAndDigitCount(obj, negative ? -1 : 1, ndigits); *digits = _PyLong_GetDigits(obj); - return (PyLongWriter*)obj; + return (PyLongWriter *)obj; } static inline void @@ -1873,11 +1915,11 @@ PyLongWriter_Discard(PyLongWriter *writer) Py_DECREF(obj); } -static inline PyObject* +static inline PyObject * PyLongWriter_Finish(PyLongWriter *writer) { PyObject *obj = (PyObject *)writer; - PyLongObject *self = (PyLongObject*)obj; + PyLongObject *self = (PyLongObject *)obj; Py_ssize_t j = _PyLong_DigitCount(self); Py_ssize_t i = j; int sign = _PyLong_Sign(obj); @@ -1885,7 +1927,7 @@ PyLongWriter_Finish(PyLongWriter *writer) assert(Py_REFCNT(obj) == 1); // Normalize and get singleton if possible - while (i > 0 && _PyLong_GetDigits(self)[i-1] == 0) { + while (i > 0 && _PyLong_GetDigits(self)[i - 1] == 0) { --i; } if (i != j) { @@ -1904,44 +1946,43 @@ PyLongWriter_Finish(PyLongWriter *writer) } #endif - #if PY_VERSION_HEX < 0x030C00A3 -# define Py_T_SHORT T_SHORT -# define Py_T_INT T_INT -# define Py_T_LONG T_LONG -# define Py_T_FLOAT T_FLOAT -# define Py_T_DOUBLE T_DOUBLE -# define Py_T_STRING T_STRING -# define _Py_T_OBJECT T_OBJECT -# define Py_T_CHAR T_CHAR -# define Py_T_BYTE T_BYTE -# define Py_T_UBYTE T_UBYTE -# define Py_T_USHORT T_USHORT -# define Py_T_UINT T_UINT -# define Py_T_ULONG T_ULONG -# define Py_T_STRING_INPLACE T_STRING_INPLACE -# define Py_T_BOOL T_BOOL -# define Py_T_OBJECT_EX T_OBJECT_EX -# define Py_T_LONGLONG T_LONGLONG -# define Py_T_ULONGLONG T_ULONGLONG -# define Py_T_PYSSIZET T_PYSSIZET - -# if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) -# define _Py_T_NONE T_NONE -# endif - -# define Py_READONLY READONLY -# define Py_AUDIT_READ READ_RESTRICTED -# define _Py_WRITE_RESTRICTED PY_WRITE_RESTRICTED +#define Py_T_SHORT T_SHORT +#define Py_T_INT T_INT +#define Py_T_LONG T_LONG +#define Py_T_FLOAT T_FLOAT +#define Py_T_DOUBLE T_DOUBLE +#define Py_T_STRING T_STRING +#define _Py_T_OBJECT T_OBJECT +#define Py_T_CHAR T_CHAR +#define Py_T_BYTE T_BYTE +#define Py_T_UBYTE T_UBYTE +#define Py_T_USHORT T_USHORT +#define Py_T_UINT T_UINT +#define Py_T_ULONG T_ULONG +#define Py_T_STRING_INPLACE T_STRING_INPLACE +#define Py_T_BOOL T_BOOL +#define Py_T_OBJECT_EX T_OBJECT_EX +#define Py_T_LONGLONG T_LONGLONG +#define Py_T_ULONGLONG T_ULONGLONG +#define Py_T_PYSSIZET T_PYSSIZET + +#if PY_VERSION_HEX >= 0x03000000 && !defined(PYPY_VERSION) +#define _Py_T_NONE T_NONE #endif +#define Py_READONLY READONLY +#define Py_AUDIT_READ READ_RESTRICTED +#define _Py_WRITE_RESTRICTED PY_WRITE_RESTRICTED +#endif // gh-127350 added Py_fopen() and Py_fclose() to Python 3.14a4 #if PY_VERSION_HEX < 0x030E00A4 -static inline FILE* Py_fopen(PyObject *path, const char *mode) +static inline FILE * +Py_fopen(PyObject *path, const char *mode) { #if 0x030400A2 <= PY_VERSION_HEX && !defined(PYPY_VERSION) - PyAPI_FUNC(FILE*) _Py_fopen_obj(PyObject *path, const char *mode); + PyAPI_FUNC(FILE *) _Py_fopen_obj(PyObject * path, const char *mode); return _Py_fopen_obj(path, mode); #else @@ -1971,15 +2012,16 @@ static inline FILE* Py_fopen(PyObject *path, const char *mode) #endif } -static inline int Py_fclose(FILE *file) +static inline int +Py_fclose(FILE *file) { return fclose(file); } #endif - -#if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && !defined(PYPY_VERSION) -static inline PyObject* +#if 0x03090000 <= PY_VERSION_HEX && PY_VERSION_HEX < 0x030E0000 && \ + !defined(PYPY_VERSION) +static inline PyObject * PyConfig_Get(const char *name) { typedef enum { @@ -1999,9 +2041,11 @@ PyConfig_Get(const char *name) const char *sys_attr; } PyConfigSpec; -#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \ - {#MEMBER, offsetof(PyConfig, MEMBER), \ - _PyConfig_MEMBER_##TYPE, sys_attr} +#define PYTHONCAPI_COMPAT_SPEC(MEMBER, TYPE, sys_attr) \ + { \ + #MEMBER, offsetof(PyConfig, MEMBER), _PyConfig_MEMBER_##TYPE, \ + sys_attr \ + } static const PyConfigSpec config_spec[] = { PYTHONCAPI_COMPAT_SPEC(argv, WSTR_LIST, "argv"), @@ -2095,7 +2139,7 @@ PyConfig_Get(const char *name) const PyConfigSpec *spec; int found = 0; - for (size_t i=0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) { + for (size_t i = 0; i < sizeof(config_spec) / sizeof(config_spec[0]); i++) { spec = &config_spec[i]; if (strcmp(spec->name, name) == 0) { found = 1; @@ -2106,64 +2150,61 @@ PyConfig_Get(const char *name) if (spec->sys_attr != NULL) { PyObject *value = PySys_GetObject(spec->sys_attr); if (value == NULL) { - PyErr_Format(PyExc_RuntimeError, "lost sys.%s", spec->sys_attr); + PyErr_Format( + PyExc_RuntimeError, "lost sys.%s", spec->sys_attr); return NULL; } return Py_NewRef(value); } - PyAPI_FUNC(const PyConfig*) _Py_GetConfig(void); + PyAPI_FUNC(const PyConfig *) _Py_GetConfig(void); const PyConfig *config = _Py_GetConfig(); void *member = (char *)config + spec->offset; switch (spec->type) { - case _PyConfig_MEMBER_INT: - case _PyConfig_MEMBER_UINT: - { - int value = *(int *)member; - return PyLong_FromLong(value); - } - case _PyConfig_MEMBER_BOOL: - { - int value = *(int *)member; - return PyBool_FromLong(value != 0); - } - case _PyConfig_MEMBER_ULONG: - { - unsigned long value = *(unsigned long *)member; - return PyLong_FromUnsignedLong(value); - } - case _PyConfig_MEMBER_WSTR: - case _PyConfig_MEMBER_WSTR_OPT: - { - wchar_t *wstr = *(wchar_t **)member; - if (wstr != NULL) { - return PyUnicode_FromWideChar(wstr, -1); + case _PyConfig_MEMBER_INT: + case _PyConfig_MEMBER_UINT: { + int value = *(int *)member; + return PyLong_FromLong(value); } - else { - return Py_NewRef(Py_None); + case _PyConfig_MEMBER_BOOL: { + int value = *(int *)member; + return PyBool_FromLong(value != 0); } - } - case _PyConfig_MEMBER_WSTR_LIST: - { - const PyWideStringList *list = (const PyWideStringList *)member; - PyObject *tuple = PyTuple_New(list->length); - if (tuple == NULL) { - return NULL; + case _PyConfig_MEMBER_ULONG: { + unsigned long value = *(unsigned long *)member; + return PyLong_FromUnsignedLong(value); } - - for (Py_ssize_t i = 0; i < list->length; i++) { - PyObject *item = PyUnicode_FromWideChar(list->items[i], -1); - if (item == NULL) { - Py_DECREF(tuple); + case _PyConfig_MEMBER_WSTR: + case _PyConfig_MEMBER_WSTR_OPT: { + wchar_t *wstr = *(wchar_t **)member; + if (wstr != NULL) { + return PyUnicode_FromWideChar(wstr, -1); + } else { + return Py_NewRef(Py_None); + } + } + case _PyConfig_MEMBER_WSTR_LIST: { + const PyWideStringList *list = + (const PyWideStringList *)member; + PyObject *tuple = PyTuple_New(list->length); + if (tuple == NULL) { return NULL; } - PyTuple_SET_ITEM(tuple, i, item); + + for (Py_ssize_t i = 0; i < list->length; i++) { + PyObject *item = + PyUnicode_FromWideChar(list->items[i], -1); + if (item == NULL) { + Py_DECREF(tuple); + return NULL; + } + PyTuple_SET_ITEM(tuple, i, item); + } + return tuple; } - return tuple; - } - default: - Py_UNREACHABLE(); + default: + Py_UNREACHABLE(); } } @@ -2189,7 +2230,8 @@ PyConfig_GetInt(const char *name, int *value) Py_DECREF(obj); if (as_int == -1 && PyErr_Occurred()) { PyErr_Format(PyExc_OverflowError, - "config option %s value does not fit into a C int", name); + "config option %s value does not fit into a C int", + name); return -1; } @@ -2198,7 +2240,6 @@ PyConfig_GetInt(const char *name, int *value) } #endif // PY_VERSION_HEX > 0x03090000 && !defined(PYPY_VERSION) - #ifdef __cplusplus } #endif diff --git a/contrib/python/multidict/multidict/_multilib/state.h b/contrib/python/multidict/multidict/_multilib/state.h index 58110d973a7..4e2610b6c1d 100644 --- a/contrib/python/multidict/multidict/_multilib/state.h +++ b/contrib/python/multidict/multidict/_multilib/state.h @@ -22,8 +22,11 @@ typedef struct { PyTypeObject *ItemsIterType; PyTypeObject *ValuesIterType; - PyObject *str_lower; PyObject *str_canonical; + PyObject *str_lower; + PyObject *str_name; + + uint64_t global_version; } mod_state; static inline mod_state * @@ -42,12 +45,11 @@ get_mod_state_by_cls(PyTypeObject *cls) return state; } - #if PY_VERSION_HEX < 0x030b0000 PyObject * PyType_GetModuleByDef(PyTypeObject *tp, PyModuleDef *def) { - PyModuleDef * mod_def; + PyModuleDef *mod_def; if (!PyType_HasFeature(tp, Py_TPFLAGS_HEAPTYPE)) { goto err; } @@ -75,7 +77,7 @@ PyType_GetModuleByDef(PyTypeObject *tp, PyModuleDef *def) if (!PyType_HasFeature((PyTypeObject *)super, Py_TPFLAGS_HEAPTYPE)) { continue; } - mod = PyType_GetModule((PyTypeObject*)super); + mod = PyType_GetModule((PyTypeObject *)super); if (mod == NULL) { PyErr_Clear(); } else { @@ -92,7 +94,6 @@ err: "PyType_GetModuleByDef: No superclass of '%s' has the given module", tp->tp_name); return NULL; - } #endif @@ -115,7 +116,6 @@ get_mod_state_by_def_checked(PyObject *self, mod_state **ret) return 1; } - static inline mod_state * get_mod_state_by_def(PyObject *self) { @@ -125,6 +125,11 @@ get_mod_state_by_def(PyObject *self) return get_mod_state(mod); } +static inline uint64_t +NEXT_VERSION(mod_state *state) +{ + return ++state->global_version; +} #ifdef __cplusplus } diff --git a/contrib/python/multidict/multidict/_multilib/views.h b/contrib/python/multidict/multidict/_multilib/views.h index e734ce57849..a15641d5b0b 100644 --- a/contrib/python/multidict/multidict/_multilib/views.h +++ b/contrib/python/multidict/multidict/_multilib/views.h @@ -6,7 +6,7 @@ extern "C" { #endif #include "dict.h" -#include "pair_list.h" +#include "hashtable.h" #include "state.h" typedef struct { @@ -14,12 +14,10 @@ typedef struct { MultiDictObject *md; } _Multidict_ViewObject; - #define Items_CheckExact(state, obj) Py_IS_TYPE(obj, state->ItemsViewType) #define Keys_CheckExact(state, obj) Py_IS_TYPE(obj, state->KeysViewType) #define Values_CheckExact(state, obj) Py_IS_TYPE(obj, state->ValuesViewType) - /********** Base **********/ static inline void @@ -38,7 +36,8 @@ multidict_view_dealloc(_Multidict_ViewObject *self) } static inline int -multidict_view_traverse(_Multidict_ViewObject *self, visitproc visit, void *arg) +multidict_view_traverse(_Multidict_ViewObject *self, visitproc visit, + void *arg) { Py_VISIT(self->md); return 0; @@ -54,17 +53,15 @@ multidict_view_clear(_Multidict_ViewObject *self) static inline Py_ssize_t multidict_view_len(_Multidict_ViewObject *self) { - return pair_list_len(&self->md->pairs); + return md_len(self->md); } static inline PyObject * -multidict_view_richcompare(PyObject *self, PyObject *other, int op) +multidict_view_richcompare(_Multidict_ViewObject *self, PyObject *other, + int op) { int tmp; - Py_ssize_t self_size = PyObject_Length(self); - if (self_size < 0) { - return NULL; - } + Py_ssize_t self_size = md_len(self->md); Py_ssize_t size = PyObject_Length(other); if (size < 0) { PyErr_Clear(); @@ -72,16 +69,15 @@ multidict_view_richcompare(PyObject *self, PyObject *other, int op) } PyObject *iter = NULL; PyObject *item = NULL; - switch(op) { + switch (op) { case Py_LT: - if (self_size >= size) - Py_RETURN_FALSE; - return PyObject_RichCompare(self, other, Py_LE); + if (self_size >= size) Py_RETURN_FALSE; + return multidict_view_richcompare(self, other, Py_LE); case Py_LE: if (self_size > size) { Py_RETURN_FALSE; } - iter = PyObject_GetIter(self); + iter = PyObject_GetIter((PyObject *)self); if (iter == NULL) { goto fail; } @@ -102,18 +98,23 @@ multidict_view_richcompare(PyObject *self, PyObject *other, int op) } Py_RETURN_TRUE; case Py_EQ: - if (self_size != size) - Py_RETURN_FALSE; - return PyObject_RichCompare(self, other, Py_LE); + if (self_size != size) Py_RETURN_FALSE; + return multidict_view_richcompare(self, other, Py_LE); case Py_NE: - tmp = PyObject_RichCompareBool(self, other, Py_EQ); - if (tmp < 0) + item = multidict_view_richcompare(self, other, Py_EQ); + if (item == NULL) { goto fail; - return PyBool_FromLong(!tmp); - case Py_GT: - if (self_size <= size) + } + if (item == Py_True) { + Py_DECREF(item); Py_RETURN_FALSE; - return PyObject_RichCompare(self, other, Py_GE); + } else { + Py_DECREF(item); + Py_RETURN_TRUE; + } + case Py_GT: + if (self_size <= size) Py_RETURN_FALSE; + return multidict_view_richcompare(self, other, Py_GE); case Py_GE: if (self_size < size) { Py_RETURN_FALSE; @@ -123,7 +124,7 @@ multidict_view_richcompare(PyObject *self, PyObject *other, int op) goto fail; } while ((item = PyIter_Next(iter))) { - tmp = PySequence_Contains(self, item); + tmp = PySequence_Contains((PyObject *)self, item); if (tmp < 0) { goto fail; } @@ -145,14 +146,13 @@ fail: return NULL; } - /********** Items **********/ static inline PyObject * multidict_itemsview_new(MultiDictObject *md) { - _Multidict_ViewObject *mv = PyObject_GC_New( - _Multidict_ViewObject, md->pairs.state->ItemsViewType); + _Multidict_ViewObject *mv = + PyObject_GC_New(_Multidict_ViewObject, md->state->ItemsViewType); if (mv == NULL) { return NULL; } @@ -179,12 +179,13 @@ multidict_itemsview_repr(_Multidict_ViewObject *self) if (tmp > 0) { return PyUnicode_FromString("..."); } - PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__"); + PyObject *name = + PyObject_GetAttrString((PyObject *)Py_TYPE(self), "__name__"); if (name == NULL) { Py_ReprLeave((PyObject *)self); return NULL; } - PyObject *ret = pair_list_repr(&self->md->pairs, name, true, true); + PyObject *ret = md_repr(self->md, name, true, true); Py_ReprLeave((PyObject *)self); Py_CLEAR(name); return ret; @@ -214,7 +215,7 @@ _multidict_itemsview_parse_item(_Multidict_ViewObject *self, PyObject *arg, *pvalue = Py_NewRef(PyTuple_GET_ITEM(arg, 1)); } - *pidentity = pair_list_calc_identity(&self->md->pairs, key); + *pidentity = md_calc_identity(self->md, key); Py_DECREF(key); if (*pidentity == NULL) { if (pkey != NULL) { @@ -233,8 +234,8 @@ _multidict_itemsview_parse_item(_Multidict_ViewObject *self, PyObject *arg, return 1; } -static int -_set_add(PyObject *set, PyObject *key, PyObject * value) +static inline int +_set_add(PyObject *set, PyObject *key, PyObject *value) { PyObject *tpl = PyTuple_Pack(2, key, value); if (tpl == NULL) { @@ -255,8 +256,7 @@ multidict_itemsview_and1(_Multidict_ViewObject *self, PyObject *other) PyObject *value2 = NULL; PyObject *arg = NULL; PyObject *ret = NULL; - - pair_list_pos_t pos; + md_finder_t finder = {0}; PyObject *iter = PyObject_GetIter(other); if (iter == NULL) { @@ -271,8 +271,8 @@ multidict_itemsview_and1(_Multidict_ViewObject *self, PyObject *other) goto fail; } while ((arg = PyIter_Next(iter))) { - int tmp = _multidict_itemsview_parse_item(self, arg, - &identity, &key, &value); + int tmp = _multidict_itemsview_parse_item( + self, arg, &identity, &key, &value); if (tmp < 0) { goto fail; } else if (tmp == 0) { @@ -280,29 +280,28 @@ multidict_itemsview_and1(_Multidict_ViewObject *self, PyObject *other) continue; } - pair_list_init_pos(&self->md->pairs, &pos); + if (md_init_finder(self->md, identity, &finder) < 0) { + assert(PyErr_Occurred()); + goto fail; + } - while (true) { - tmp = pair_list_next_by_identity(&self->md->pairs, &pos, - identity, &key2, &value2); + while ((tmp = md_find_next(&finder, &key2, &value2)) > 0) { + tmp = PyObject_RichCompareBool(value, value2, Py_EQ); if (tmp < 0) { goto fail; - } else if (tmp == 0) { - break; - } else { - tmp = PyObject_RichCompareBool(value, value2, Py_EQ); - if (tmp < 0) { + } + if (tmp > 0) { + if (_set_add(ret, key2, value2) < 0) { goto fail; } - if (tmp > 0) { - if (_set_add(ret, key2, value2) < 0) { - goto fail; - } - } } Py_CLEAR(key2); Py_CLEAR(value2); } + if (tmp < 0) { + goto fail; + } + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(key); @@ -314,6 +313,7 @@ multidict_itemsview_and1(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(iter); return ret; fail: + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(key); @@ -334,8 +334,7 @@ multidict_itemsview_and2(_Multidict_ViewObject *self, PyObject *other) PyObject *value2 = NULL; PyObject *arg = NULL; PyObject *ret = NULL; - - pair_list_pos_t pos; + md_finder_t finder = {0}; PyObject *iter = PyObject_GetIter(other); if (iter == NULL) { @@ -350,8 +349,8 @@ multidict_itemsview_and2(_Multidict_ViewObject *self, PyObject *other) goto fail; } while ((arg = PyIter_Next(iter))) { - int tmp = _multidict_itemsview_parse_item(self, arg, - &identity, &key, &value); + int tmp = _multidict_itemsview_parse_item( + self, arg, &identity, &key, &value); if (tmp < 0) { goto fail; } else if (tmp == 0) { @@ -359,28 +358,27 @@ multidict_itemsview_and2(_Multidict_ViewObject *self, PyObject *other) continue; } - pair_list_init_pos(&self->md->pairs, &pos); + if (md_init_finder(self->md, identity, &finder) < 0) { + assert(PyErr_Occurred()); + goto fail; + } - while (true) { - tmp = pair_list_next_by_identity(&self->md->pairs, &pos, - identity, NULL, &value2); + while ((tmp = md_find_next(&finder, NULL, &value2)) > 0) { + tmp = PyObject_RichCompareBool(value, value2, Py_EQ); if (tmp < 0) { goto fail; - } else if (tmp == 0) { - break; - } else { - tmp = PyObject_RichCompareBool(value, value2, Py_EQ); - if (tmp < 0) { + } + if (tmp > 0) { + if (_set_add(ret, key, value2) < 0) { goto fail; } - if (tmp > 0) { - if (_set_add(ret, key, value2) < 0) { - goto fail; - } - } } Py_CLEAR(value2); } + if (tmp < 0) { + goto fail; + } + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(key); @@ -392,6 +390,7 @@ multidict_itemsview_and2(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(iter); return ret; fail: + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(key); @@ -405,7 +404,7 @@ fail: static inline PyObject * multidict_itemsview_and(PyObject *lft, PyObject *rht) { - mod_state * state; + mod_state *state; int tmp = get_mod_state_by_def_checked(lft, &state); if (tmp < 0) { return NULL; @@ -435,8 +434,7 @@ multidict_itemsview_or1(_Multidict_ViewObject *self, PyObject *other) PyObject *value2 = NULL; PyObject *arg = NULL; PyObject *ret = NULL; - - pair_list_pos_t pos; + md_finder_t finder = {0}; PyObject *iter = PyObject_GetIter(other); if (iter == NULL) { @@ -451,8 +449,8 @@ multidict_itemsview_or1(_Multidict_ViewObject *self, PyObject *other) goto fail; } while ((arg = PyIter_Next(iter))) { - int tmp = _multidict_itemsview_parse_item(self, arg, - &identity, &key, &value); + int tmp = _multidict_itemsview_parse_item( + self, arg, &identity, &key, &value); if (tmp < 0) { goto fail; } else if (tmp == 0) { @@ -463,30 +461,30 @@ multidict_itemsview_or1(_Multidict_ViewObject *self, PyObject *other) continue; } - pair_list_init_pos(&self->md->pairs, &pos); + if (md_init_finder(self->md, identity, &finder) < 0) { + assert(PyErr_Occurred()); + goto fail; + } - while (true) { - tmp = pair_list_next_by_identity(&self->md->pairs, &pos, - identity, NULL, &value2); + while ((tmp = md_find_next(&finder, NULL, &value2)) > 0) { + tmp = PyObject_RichCompareBool(value, value2, Py_EQ); if (tmp < 0) { goto fail; - } else if (tmp == 0) { - if (PySet_Add(ret, arg) < 0) { - goto fail; - } + } + if (tmp > 0) { + Py_CLEAR(value2); break; - } else { - tmp = PyObject_RichCompareBool(value, value2, Py_EQ); - if (tmp < 0) { - goto fail; - } - if (tmp > 0) { - Py_CLEAR(value2); - break; - } } Py_CLEAR(value2); } + if (tmp < 0) { + goto fail; + } else if (tmp == 0) { + if (PySet_Add(ret, arg) < 0) { + goto fail; + } + } + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(key); @@ -498,6 +496,7 @@ multidict_itemsview_or1(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(iter); return ret; fail: + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(key); @@ -518,7 +517,7 @@ multidict_itemsview_or2(_Multidict_ViewObject *self, PyObject *other) PyObject *arg = NULL; PyObject *tmp_set = NULL; - pair_list_pos_t pos; + md_pos_t pos; PyObject *ret = PySet_New(other); if (ret == NULL) { @@ -537,8 +536,8 @@ multidict_itemsview_or2(_Multidict_ViewObject *self, PyObject *other) goto fail; } while ((arg = PyIter_Next(iter))) { - int tmp = _multidict_itemsview_parse_item(self, arg, - &identity, NULL, &value); + int tmp = _multidict_itemsview_parse_item( + self, arg, &identity, NULL, &value); if (tmp < 0) { goto fail; } else if (tmp > 0) { @@ -553,11 +552,10 @@ multidict_itemsview_or2(_Multidict_ViewObject *self, PyObject *other) } Py_CLEAR(iter); - pair_list_init_pos(&self->md->pairs, &pos); + md_init_pos(self->md, &pos); while (true) { - int tmp = pair_list_next(&self->md->pairs, &pos, - &identity, &key, &value); + int tmp = md_next(self->md, &pos, &identity, &key, &value); if (tmp < 0) { goto fail; } else if (tmp == 0) { @@ -597,7 +595,7 @@ fail: static inline PyObject * multidict_itemsview_or(PyObject *lft, PyObject *rht) { - mod_state * state; + mod_state *state; int tmp = get_mod_state_by_def_checked(lft, &state); if (tmp < 0) { return NULL; @@ -618,7 +616,6 @@ multidict_itemsview_or(PyObject *lft, PyObject *rht) Py_RETURN_NOTIMPLEMENTED; } - static inline PyObject * multidict_itemsview_sub1(_Multidict_ViewObject *self, PyObject *other) { @@ -629,7 +626,7 @@ multidict_itemsview_sub1(_Multidict_ViewObject *self, PyObject *other) PyObject *ret = NULL; PyObject *tmp_set = NULL; - pair_list_pos_t pos; + md_pos_t pos; PyObject *iter = PyObject_GetIter(other); if (iter == NULL) { @@ -648,8 +645,8 @@ multidict_itemsview_sub1(_Multidict_ViewObject *self, PyObject *other) goto fail; } while ((arg = PyIter_Next(iter))) { - int tmp = _multidict_itemsview_parse_item(self, arg, - &identity, NULL, &value); + int tmp = _multidict_itemsview_parse_item( + self, arg, &identity, NULL, &value); if (tmp < 0) { goto fail; } else if (tmp > 0) { @@ -664,11 +661,10 @@ multidict_itemsview_sub1(_Multidict_ViewObject *self, PyObject *other) } Py_CLEAR(iter); - pair_list_init_pos(&self->md->pairs, &pos); + md_init_pos(self->md, &pos); while (true) { - int tmp = pair_list_next(&self->md->pairs, &pos, - &identity, &key, &value); + int tmp = md_next(self->md, &pos, &identity, &key, &value); if (tmp < 0) { goto fail; } else if (tmp == 0) { @@ -715,8 +711,7 @@ multidict_itemsview_sub2(_Multidict_ViewObject *self, PyObject *other) PyObject *value2 = NULL; PyObject *ret = NULL; PyObject *iter = PyObject_GetIter(other); - - pair_list_pos_t pos; + md_finder_t finder = {0}; if (iter == NULL) { if (PyErr_ExceptionMatches(PyExc_TypeError)) { @@ -730,8 +725,8 @@ multidict_itemsview_sub2(_Multidict_ViewObject *self, PyObject *other) goto fail; } while ((arg = PyIter_Next(iter))) { - int tmp = _multidict_itemsview_parse_item(self, arg, - &identity, NULL, &value); + int tmp = _multidict_itemsview_parse_item( + self, arg, &identity, NULL, &value); if (tmp < 0) { goto fail; } else if (tmp == 0) { @@ -742,31 +737,30 @@ multidict_itemsview_sub2(_Multidict_ViewObject *self, PyObject *other) continue; } - pair_list_init_pos(&self->md->pairs, &pos); + if (md_init_finder(self->md, identity, &finder) < 0) { + assert(PyErr_Occurred()); + goto fail; + } - while (true) { - tmp = pair_list_next_by_identity(&self->md->pairs, &pos, - identity, NULL, &value2); + while ((tmp = md_find_next(&finder, NULL, &value2)) > 0) { + tmp = PyObject_RichCompareBool(value, value2, Py_EQ); if (tmp < 0) { goto fail; - } else if (tmp == 0) { - if (PySet_Add(ret, arg) < 0) { - goto fail; - } + } + if (tmp > 0) { + Py_CLEAR(value2); break; - } else { - tmp = PyObject_RichCompareBool(value, value2, Py_EQ); - if (tmp < 0) { - goto fail; - } - if (tmp > 0) { - Py_CLEAR(value2); - break; - } } Py_CLEAR(value2); } - + if (tmp < 0) { + goto fail; + } else if (tmp == 0) { + if (PySet_Add(ret, arg) < 0) { + goto fail; + } + } + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(key); @@ -778,6 +772,7 @@ multidict_itemsview_sub2(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(iter); return ret; fail: + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(key); @@ -790,7 +785,7 @@ fail: static inline PyObject * multidict_itemsview_sub(PyObject *lft, PyObject *rht) { - mod_state * state; + mod_state *state; int tmp = get_mod_state_by_def_checked(lft, &state); if (tmp < 0) { return NULL; @@ -814,7 +809,7 @@ multidict_itemsview_sub(PyObject *lft, PyObject *rht) static inline PyObject * multidict_itemsview_xor(_Multidict_ViewObject *self, PyObject *other) { - mod_state * state; + mod_state *state; int tmp = get_mod_state_by_def_checked((PyObject *)self, &state); if (tmp < 0) { return NULL; @@ -874,64 +869,88 @@ fail: static inline int multidict_itemsview_contains(_Multidict_ViewObject *self, PyObject *obj) { - PyObject *akey = NULL, - *aval = NULL, - *bkey = NULL, - *bval = NULL, - *iter = NULL, - *item = NULL; - int ret1, ret2; - - if (!PyTuple_Check(obj) || PyTuple_GET_SIZE(obj) != 2) { - return 0; - } - - bkey = PyTuple_GET_ITEM(obj, 0); - bval = PyTuple_GET_ITEM(obj, 1); - - iter = multidict_itemsview_iter(self); - if (iter == NULL) { - return 0; - } - - while ((item = PyIter_Next(iter)) != NULL) { - akey = PyTuple_GET_ITEM(item, 0); - aval = PyTuple_GET_ITEM(item, 1); + PyObject *identity = NULL; + PyObject *key = NULL; + PyObject *value = NULL; + PyObject *value2 = NULL; + int tmp; + int ret = 0; + md_finder_t finder = {0}; - ret1 = PyObject_RichCompareBool(akey, bkey, Py_EQ); - if (ret1 < 0) { - Py_DECREF(iter); - Py_DECREF(item); - return -1; + if (PyTuple_CheckExact(obj)) { + if (PyTuple_GET_SIZE(obj) != 2) { + return 0; + } + key = Py_NewRef(PyTuple_GET_ITEM(obj, 0)); + value = Py_NewRef(PyTuple_GET_ITEM(obj, 1)); + } else if (PyList_CheckExact(obj)) { + if (PyList_GET_SIZE(obj) != 2) { + return 0; + } + key = Py_NewRef(PyList_GET_ITEM(obj, 0)); + value = Py_NewRef(PyList_GET_ITEM(obj, 1)); + } else { + tmp = PyObject_Length(obj); + if (tmp < 0) { + PyErr_Clear(); + return 0; } - ret2 = PyObject_RichCompareBool(aval, bval, Py_EQ); - if (ret2 < 0) { - Py_DECREF(iter); - Py_DECREF(item); + if (tmp != 2) { + return 0; + } + key = PySequence_GetItem(obj, 0); + if (key == NULL) { return -1; } - if (ret1 > 0 && ret2 > 0) - { - Py_DECREF(iter); - Py_DECREF(item); - return 1; + value = PySequence_GetItem(obj, 1); + if (value == NULL) { + return -1; } + } - Py_DECREF(item); + identity = md_calc_identity(self->md, key); + if (identity == NULL) { + PyErr_Clear(); + ret = 0; + goto done; } - Py_DECREF(iter); + if (md_init_finder(self->md, identity, &finder) < 0) { + assert(PyErr_Occurred()); + ret = -1; + goto done; + } - if (PyErr_Occurred()) { - return -1; + while ((tmp = md_find_next(&finder, NULL, &value2)) > 0) { + tmp = PyObject_RichCompareBool(value, value2, Py_EQ); + Py_CLEAR(value2); + if (tmp < 0) { + ret = -1; + goto done; + } + if (tmp > 0) { + ret = 1; + goto done; + } + } + if (tmp < 0) { + ret = -1; + goto done; } - return 0; +done: + md_finder_cleanup(&finder); + Py_CLEAR(identity); + Py_CLEAR(key); + Py_CLEAR(value); + ASSERT_CONSISTENT(self->md, false); + return ret; } static inline PyObject * multidict_itemsview_isdisjoint(_Multidict_ViewObject *self, PyObject *other) { + md_finder_t finder = {0}; PyObject *iter = PyObject_GetIter(other); if (iter == NULL) { return NULL; @@ -941,11 +960,9 @@ multidict_itemsview_isdisjoint(_Multidict_ViewObject *self, PyObject *other) PyObject *value = NULL; PyObject *value2 = NULL; - pair_list_pos_t pos; - while ((arg = PyIter_Next(iter))) { - int tmp = _multidict_itemsview_parse_item(self, arg, - &identity, NULL, &value); + int tmp = _multidict_itemsview_parse_item( + self, arg, &identity, NULL, &value); if (tmp < 0) { goto fail; } else if (tmp == 0) { @@ -953,31 +970,31 @@ multidict_itemsview_isdisjoint(_Multidict_ViewObject *self, PyObject *other) continue; } - pair_list_init_pos(&self->md->pairs, &pos); + if (md_init_finder(self->md, identity, &finder) < 0) { + assert(PyErr_Occurred()); + goto fail; + } - while (true) { - tmp = pair_list_next_by_identity(&self->md->pairs, &pos, - identity, NULL, &value2); + while ((tmp = md_find_next(&finder, NULL, &value2)) > 0) { + tmp = PyObject_RichCompareBool(value, value2, Py_EQ); + Py_CLEAR(value2); if (tmp < 0) { goto fail; - } else if (tmp == 0) { - Py_CLEAR(value2); - break; - } else { - tmp = PyObject_RichCompareBool(value, value2, Py_EQ); - Py_CLEAR(value2); - if (tmp < 0) { - goto fail; - } - if (tmp > 0) { - Py_CLEAR(iter); - Py_CLEAR(arg); - Py_CLEAR(identity); - Py_CLEAR(value); - Py_RETURN_FALSE; - } } + if (tmp > 0) { + md_finder_cleanup(&finder); + Py_CLEAR(iter); + Py_CLEAR(arg); + Py_CLEAR(identity); + Py_CLEAR(value); + ASSERT_CONSISTENT(self->md, false); + Py_RETURN_FALSE; + } + } + if (tmp < 0) { + goto fail; } + md_finder_cleanup(&finder); Py_CLEAR(arg); Py_CLEAR(identity); Py_CLEAR(value); @@ -986,8 +1003,10 @@ multidict_itemsview_isdisjoint(_Multidict_ViewObject *self, PyObject *other) if (PyErr_Occurred()) { return NULL; } + ASSERT_CONSISTENT(self->md, false); Py_RETURN_TRUE; fail: + md_finder_cleanup(&finder); Py_CLEAR(iter); Py_CLEAR(arg); Py_CLEAR(identity); @@ -999,18 +1018,21 @@ fail: PyDoc_STRVAR(itemsview_isdisjoint_doc, "Return True if two sets have a null intersection."); - static PyMethodDef multidict_itemsview_methods[] = { - {"isdisjoint", (PyCFunction)multidict_itemsview_isdisjoint, - METH_O, itemsview_isdisjoint_doc}, - {NULL, NULL} /* sentinel */ + {"isdisjoint", + (PyCFunction)multidict_itemsview_isdisjoint, + METH_O, + itemsview_isdisjoint_doc}, + {NULL, NULL} /* sentinel */ }; static inline PyObject * -multidict_view_forbidden_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) +multidict_view_forbidden_new(PyTypeObject *type, PyObject *args, + PyObject *kwargs) { PyErr_Format(PyExc_TypeError, - "cannot create '%s' instances directly", type->tp_name); + "cannot create '%s' instances directly", + type->tp_name); return NULL; } @@ -1045,14 +1067,13 @@ static PyType_Spec multidict_itemsview_spec = { .slots = multidict_itemsview_slots, }; - /********** Keys **********/ static inline PyObject * multidict_keysview_new(MultiDictObject *md) { - _Multidict_ViewObject *mv = PyObject_GC_New( - _Multidict_ViewObject, md->pairs.state->KeysViewType); + _Multidict_ViewObject *mv = + PyObject_GC_New(_Multidict_ViewObject, md->state->KeysViewType); if (mv == NULL) { return NULL; } @@ -1072,11 +1093,12 @@ multidict_keysview_iter(_Multidict_ViewObject *self) static inline PyObject * multidict_keysview_repr(_Multidict_ViewObject *self) { - PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__"); + PyObject *name = + PyObject_GetAttrString((PyObject *)Py_TYPE(self), "__name__"); if (name == NULL) { return NULL; } - PyObject *ret = pair_list_repr(&self->md->pairs, name, true, false); + PyObject *ret = md_repr(self->md, name, true, false); Py_CLEAR(name); return ret; } @@ -1104,7 +1126,7 @@ multidict_keysview_and1(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(key); continue; } - int tmp = pair_list_contains(&self->md->pairs, key, &key2); + int tmp = md_contains(self->md, key, &key2); if (tmp < 0) { goto fail; } @@ -1151,7 +1173,7 @@ multidict_keysview_and2(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(key); continue; } - int tmp = pair_list_contains(&self->md->pairs, key, NULL); + int tmp = md_contains(self->md, key, NULL); if (tmp < 0) { goto fail; } @@ -1177,7 +1199,7 @@ fail: static inline PyObject * multidict_keysview_and(PyObject *lft, PyObject *rht) { - mod_state * state; + mod_state *state; int tmp = get_mod_state_by_def_checked(lft, &state); if (tmp < 0) { return NULL; @@ -1223,7 +1245,7 @@ multidict_keysview_or1(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(key); continue; } - int tmp = pair_list_contains(&self->md->pairs, key, NULL); + int tmp = md_contains(self->md, key, NULL); if (tmp < 0) { goto fail; } @@ -1274,7 +1296,7 @@ multidict_keysview_or2(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(key); continue; } - identity = pair_list_calc_identity(&self->md->pairs, key); + identity = md_calc_identity(self->md, key); if (identity == NULL) { goto fail; } @@ -1289,11 +1311,11 @@ multidict_keysview_or2(_Multidict_ViewObject *self, PyObject *other) } Py_CLEAR(iter); - pair_list_pos_t pos; - pair_list_init_pos(&self->md->pairs, &pos); + md_pos_t pos; + md_init_pos(self->md, &pos); while (true) { - int tmp = pair_list_next(&self->md->pairs, &pos, &identity, &key, NULL); + int tmp = md_next(self->md, &pos, &identity, &key, NULL); if (tmp < 0) { goto fail; } else if (tmp == 0) { @@ -1326,7 +1348,7 @@ fail: static inline PyObject * multidict_keysview_or(PyObject *lft, PyObject *rht) { - mod_state * state; + mod_state *state; int tmp = get_mod_state_by_def_checked(lft, &state); if (tmp < 0) { return NULL; @@ -1371,7 +1393,7 @@ multidict_keysview_sub1(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(key); continue; } - tmp = pair_list_contains(&self->md->pairs, key, &key2); + tmp = md_contains(self->md, key, &key2); if (tmp < 0) { goto fail; } @@ -1419,7 +1441,7 @@ multidict_keysview_sub2(_Multidict_ViewObject *self, PyObject *other) Py_CLEAR(key); continue; } - tmp = pair_list_contains(&self->md->pairs, key, NULL); + tmp = md_contains(self->md, key, NULL); if (tmp < 0) { goto fail; } @@ -1445,7 +1467,7 @@ fail: static inline PyObject * multidict_keysview_sub(PyObject *lft, PyObject *rht) { - mod_state * state; + mod_state *state; int tmp = get_mod_state_by_def_checked(lft, &state); if (tmp < 0) { return NULL; @@ -1469,7 +1491,7 @@ multidict_keysview_sub(PyObject *lft, PyObject *rht) static inline PyObject * multidict_keysview_xor(_Multidict_ViewObject *self, PyObject *other) { - mod_state * state; + mod_state *state; int tmp = get_mod_state_by_def_checked((PyObject *)self, &state); if (tmp < 0) { return NULL; @@ -1529,7 +1551,7 @@ fail: static inline int multidict_keysview_contains(_Multidict_ViewObject *self, PyObject *key) { - return pair_list_contains(&self->md->pairs, key, NULL); + return md_contains(self->md, key, NULL); } static inline PyObject * @@ -1541,7 +1563,7 @@ multidict_keysview_isdisjoint(_Multidict_ViewObject *self, PyObject *other) } PyObject *key = NULL; while ((key = PyIter_Next(iter))) { - int tmp = pair_list_contains(&self->md->pairs, key, NULL); + int tmp = md_contains(self->md, key, NULL); Py_CLEAR(key); if (tmp < 0) { Py_CLEAR(iter); @@ -1562,11 +1584,12 @@ multidict_keysview_isdisjoint(_Multidict_ViewObject *self, PyObject *other) PyDoc_STRVAR(keysview_isdisjoint_doc, "Return True if two sets have a null intersection."); - static PyMethodDef multidict_keysview_methods[] = { - {"isdisjoint", (PyCFunction)multidict_keysview_isdisjoint, - METH_O, keysview_isdisjoint_doc}, - {NULL, NULL} /* sentinel */ + {"isdisjoint", + (PyCFunction)multidict_keysview_isdisjoint, + METH_O, + keysview_isdisjoint_doc}, + {NULL, NULL} /* sentinel */ }; static PyType_Slot multidict_keysview_slots[] = { @@ -1605,8 +1628,8 @@ static PyType_Spec multidict_keysview_spec = { static inline PyObject * multidict_valuesview_new(MultiDictObject *md) { - _Multidict_ViewObject *mv = PyObject_GC_New( - _Multidict_ViewObject, md->pairs.state->ValuesViewType); + _Multidict_ViewObject *mv = + PyObject_GC_New(_Multidict_ViewObject, md->state->ValuesViewType); if (mv == NULL) { return NULL; } @@ -1633,12 +1656,13 @@ multidict_valuesview_repr(_Multidict_ViewObject *self) if (tmp > 0) { return PyUnicode_FromString("..."); } - PyObject *name = PyObject_GetAttrString((PyObject*)Py_TYPE(self), "__name__"); + PyObject *name = + PyObject_GetAttrString((PyObject *)Py_TYPE(self), "__name__"); if (name == NULL) { Py_ReprLeave((PyObject *)self); return NULL; } - PyObject *ret = pair_list_repr(&self->md->pairs, name, false, true); + PyObject *ret = md_repr(self->md, name, false, true); Py_ReprLeave((PyObject *)self); Py_CLEAR(name); return ret; @@ -1668,11 +1692,10 @@ static PyType_Spec multidict_valuesview_spec = { .slots = multidict_valuesview_slots, }; - static inline int multidict_views_init(PyObject *module, mod_state *state) { - PyObject * tmp; + PyObject *tmp; tmp = PyType_FromModuleAndSpec(module, &multidict_itemsview_spec, NULL); if (tmp == NULL) { return -1; diff --git a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.0 b/contrib/python/multidict/tests/cimultidict-c.pickle.0 index 7b2ed008451..7b2ed008451 100644 --- a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.0 +++ b/contrib/python/multidict/tests/cimultidict-c.pickle.0 diff --git a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.1 b/contrib/python/multidict/tests/cimultidict-c.pickle.1 Binary files differindex 225458ba299..225458ba299 100644 --- a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.1 +++ b/contrib/python/multidict/tests/cimultidict-c.pickle.1 diff --git a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.2 b/contrib/python/multidict/tests/cimultidict-c.pickle.2 Binary files differindex d33600e615a..d33600e615a 100644 --- a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.2 +++ b/contrib/python/multidict/tests/cimultidict-c.pickle.2 diff --git a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.3 b/contrib/python/multidict/tests/cimultidict-c.pickle.3 Binary files differindex cbb8624db07..cbb8624db07 100644 --- a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.3 +++ b/contrib/python/multidict/tests/cimultidict-c.pickle.3 diff --git a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.4 b/contrib/python/multidict/tests/cimultidict-c.pickle.4 Binary files differindex 1f5164ca37b..1f5164ca37b 100644 --- a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.4 +++ b/contrib/python/multidict/tests/cimultidict-c.pickle.4 diff --git a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.5 b/contrib/python/multidict/tests/cimultidict-c.pickle.5 Binary files differindex 11bf552c436..11bf552c436 100644 --- a/contrib/python/multidict/tests/cimultidict-c-extension.pickle.5 +++ b/contrib/python/multidict/tests/cimultidict-c.pickle.5 diff --git a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.0 b/contrib/python/multidict/tests/cimultidict-py.pickle.0 index bd39b6db202..bd39b6db202 100644 --- a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.0 +++ b/contrib/python/multidict/tests/cimultidict-py.pickle.0 diff --git a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.1 b/contrib/python/multidict/tests/cimultidict-py.pickle.1 Binary files differindex 866003d26ca..866003d26ca 100644 --- a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.1 +++ b/contrib/python/multidict/tests/cimultidict-py.pickle.1 diff --git a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.2 b/contrib/python/multidict/tests/cimultidict-py.pickle.2 Binary files differindex c9e43fef9c9..c9e43fef9c9 100644 --- a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.2 +++ b/contrib/python/multidict/tests/cimultidict-py.pickle.2 diff --git a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.3 b/contrib/python/multidict/tests/cimultidict-py.pickle.3 Binary files differindex 821659fe0c5..821659fe0c5 100644 --- a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.3 +++ b/contrib/python/multidict/tests/cimultidict-py.pickle.3 diff --git a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.4 b/contrib/python/multidict/tests/cimultidict-py.pickle.4 Binary files differindex a17c6e9b731..a17c6e9b731 100644 --- a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.4 +++ b/contrib/python/multidict/tests/cimultidict-py.pickle.4 diff --git a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.5 b/contrib/python/multidict/tests/cimultidict-py.pickle.5 Binary files differindex 479bfe3a5d3..479bfe3a5d3 100644 --- a/contrib/python/multidict/tests/cimultidict-pure-python.pickle.5 +++ b/contrib/python/multidict/tests/cimultidict-py.pickle.5 diff --git a/contrib/python/multidict/tests/conftest.py b/contrib/python/multidict/tests/conftest.py index a37f58f2d1f..dfc1a6703cd 100644 --- a/contrib/python/multidict/tests/conftest.py +++ b/contrib/python/multidict/tests/conftest.py @@ -39,7 +39,7 @@ class MultidictImplementation: @cached_property def tag(self) -> str: """Return a text representation of the pure-python attribute.""" - return "pure-python" if self.is_pure_python else "c-extension" + return "py" if self.is_pure_python else "c" @cached_property def imported_module(self) -> ModuleType: @@ -49,7 +49,7 @@ class MultidictImplementation: def __str__(self) -> str: """Render the implementation facade instance as a string.""" - return f"{self.tag}-module" + return self.tag @pytest.fixture( @@ -79,7 +79,7 @@ def multidict_module( @pytest.fixture( scope="session", params=("MultiDict", "CIMultiDict"), - ids=("case-sensitive", "case-insensitive"), + ids=("cs", "ci"), ) def any_multidict_class_name(request: pytest.FixtureRequest) -> str: """Return a class name of a mutable multidict implementation.""" diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.0 b/contrib/python/multidict/tests/istr-c.pickle.0 index 2be573802a5..2be573802a5 100644 --- a/contrib/python/multidict/tests/istr-c-extension.pickle.0 +++ b/contrib/python/multidict/tests/istr-c.pickle.0 diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.1 b/contrib/python/multidict/tests/istr-c.pickle.1 Binary files differindex 206775444b5..206775444b5 100644 --- a/contrib/python/multidict/tests/istr-c-extension.pickle.1 +++ b/contrib/python/multidict/tests/istr-c.pickle.1 diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.2 b/contrib/python/multidict/tests/istr-c.pickle.2 Binary files differindex 5c038d23faf..5c038d23faf 100644 --- a/contrib/python/multidict/tests/istr-c-extension.pickle.2 +++ b/contrib/python/multidict/tests/istr-c.pickle.2 diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.3 b/contrib/python/multidict/tests/istr-c.pickle.3 Binary files differindex a9184bb4c32..a9184bb4c32 100644 --- a/contrib/python/multidict/tests/istr-c-extension.pickle.3 +++ b/contrib/python/multidict/tests/istr-c.pickle.3 diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.4 b/contrib/python/multidict/tests/istr-c.pickle.4 Binary files differindex d6c52d24491..d6c52d24491 100644 --- a/contrib/python/multidict/tests/istr-c-extension.pickle.4 +++ b/contrib/python/multidict/tests/istr-c.pickle.4 diff --git a/contrib/python/multidict/tests/istr-c-extension.pickle.5 b/contrib/python/multidict/tests/istr-c.pickle.5 Binary files differindex fce4bc01efc..fce4bc01efc 100644 --- a/contrib/python/multidict/tests/istr-c-extension.pickle.5 +++ b/contrib/python/multidict/tests/istr-c.pickle.5 diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.0 b/contrib/python/multidict/tests/istr-py.pickle.0 index 9e3f0a2a6b7..9e3f0a2a6b7 100644 --- a/contrib/python/multidict/tests/istr-pure-python.pickle.0 +++ b/contrib/python/multidict/tests/istr-py.pickle.0 diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.1 b/contrib/python/multidict/tests/istr-py.pickle.1 Binary files differindex 88b7a9d434a..88b7a9d434a 100644 --- a/contrib/python/multidict/tests/istr-pure-python.pickle.1 +++ b/contrib/python/multidict/tests/istr-py.pickle.1 diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.2 b/contrib/python/multidict/tests/istr-py.pickle.2 Binary files differindex 9f17e5997e7..9f17e5997e7 100644 --- a/contrib/python/multidict/tests/istr-pure-python.pickle.2 +++ b/contrib/python/multidict/tests/istr-py.pickle.2 diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.3 b/contrib/python/multidict/tests/istr-py.pickle.3 Binary files differindex 09f70c0ae81..09f70c0ae81 100644 --- a/contrib/python/multidict/tests/istr-pure-python.pickle.3 +++ b/contrib/python/multidict/tests/istr-py.pickle.3 diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.4 b/contrib/python/multidict/tests/istr-py.pickle.4 Binary files differindex d092a4eb550..d092a4eb550 100644 --- a/contrib/python/multidict/tests/istr-pure-python.pickle.4 +++ b/contrib/python/multidict/tests/istr-py.pickle.4 diff --git a/contrib/python/multidict/tests/istr-pure-python.pickle.5 b/contrib/python/multidict/tests/istr-py.pickle.5 Binary files differindex b8f03af6562..b8f03af6562 100644 --- a/contrib/python/multidict/tests/istr-pure-python.pickle.5 +++ b/contrib/python/multidict/tests/istr-py.pickle.5 diff --git a/contrib/python/multidict/tests/multidict-c-extension.pickle.0 b/contrib/python/multidict/tests/multidict-c.pickle.0 index eb979fcf725..eb979fcf725 100644 --- a/contrib/python/multidict/tests/multidict-c-extension.pickle.0 +++ b/contrib/python/multidict/tests/multidict-c.pickle.0 diff --git a/contrib/python/multidict/tests/multidict-c-extension.pickle.1 b/contrib/python/multidict/tests/multidict-c.pickle.1 Binary files differindex a4f211d7b1b..a4f211d7b1b 100644 --- a/contrib/python/multidict/tests/multidict-c-extension.pickle.1 +++ b/contrib/python/multidict/tests/multidict-c.pickle.1 diff --git a/contrib/python/multidict/tests/multidict-c-extension.pickle.2 b/contrib/python/multidict/tests/multidict-c.pickle.2 Binary files differindex b4563f879d0..b4563f879d0 100644 --- a/contrib/python/multidict/tests/multidict-c-extension.pickle.2 +++ b/contrib/python/multidict/tests/multidict-c.pickle.2 diff --git a/contrib/python/multidict/tests/multidict-c-extension.pickle.3 b/contrib/python/multidict/tests/multidict-c.pickle.3 Binary files differindex 415960a3eed..415960a3eed 100644 --- a/contrib/python/multidict/tests/multidict-c-extension.pickle.3 +++ b/contrib/python/multidict/tests/multidict-c.pickle.3 diff --git a/contrib/python/multidict/tests/multidict-c-extension.pickle.4 b/contrib/python/multidict/tests/multidict-c.pickle.4 Binary files differindex 00ef17c3f7f..00ef17c3f7f 100644 --- a/contrib/python/multidict/tests/multidict-c-extension.pickle.4 +++ b/contrib/python/multidict/tests/multidict-c.pickle.4 diff --git a/contrib/python/multidict/tests/multidict-c-extension.pickle.5 b/contrib/python/multidict/tests/multidict-c.pickle.5 Binary files differindex 2c4ae0a0d38..2c4ae0a0d38 100644 --- a/contrib/python/multidict/tests/multidict-c-extension.pickle.5 +++ b/contrib/python/multidict/tests/multidict-c.pickle.5 diff --git a/contrib/python/multidict/tests/multidict-pure-python.pickle.0 b/contrib/python/multidict/tests/multidict-py.pickle.0 index e91023ecf97..e91023ecf97 100644 --- a/contrib/python/multidict/tests/multidict-pure-python.pickle.0 +++ b/contrib/python/multidict/tests/multidict-py.pickle.0 diff --git a/contrib/python/multidict/tests/multidict-pure-python.pickle.1 b/contrib/python/multidict/tests/multidict-py.pickle.1 Binary files differindex acce9bf7939..acce9bf7939 100644 --- a/contrib/python/multidict/tests/multidict-pure-python.pickle.1 +++ b/contrib/python/multidict/tests/multidict-py.pickle.1 diff --git a/contrib/python/multidict/tests/multidict-pure-python.pickle.2 b/contrib/python/multidict/tests/multidict-py.pickle.2 Binary files differindex 900446ad8da..900446ad8da 100644 --- a/contrib/python/multidict/tests/multidict-pure-python.pickle.2 +++ b/contrib/python/multidict/tests/multidict-py.pickle.2 diff --git a/contrib/python/multidict/tests/multidict-pure-python.pickle.3 b/contrib/python/multidict/tests/multidict-py.pickle.3 Binary files differindex 9b9073515a7..9b9073515a7 100644 --- a/contrib/python/multidict/tests/multidict-pure-python.pickle.3 +++ b/contrib/python/multidict/tests/multidict-py.pickle.3 diff --git a/contrib/python/multidict/tests/multidict-pure-python.pickle.4 b/contrib/python/multidict/tests/multidict-py.pickle.4 Binary files differindex db363f8d3c3..db363f8d3c3 100644 --- a/contrib/python/multidict/tests/multidict-pure-python.pickle.4 +++ b/contrib/python/multidict/tests/multidict-py.pickle.4 diff --git a/contrib/python/multidict/tests/multidict-pure-python.pickle.5 b/contrib/python/multidict/tests/multidict-py.pickle.5 Binary files differindex 7dc772d58fb..7dc772d58fb 100644 --- a/contrib/python/multidict/tests/multidict-pure-python.pickle.5 +++ b/contrib/python/multidict/tests/multidict-py.pickle.5 diff --git a/contrib/python/multidict/tests/test_circular_imports.py b/contrib/python/multidict/tests/test_circular_imports.py index f6ea323ee22..75741388b0b 100644 --- a/contrib/python/multidict/tests/test_circular_imports.py +++ b/contrib/python/multidict/tests/test_circular_imports.py @@ -82,8 +82,10 @@ def test_no_warnings(import_path: str) -> None: # fmt: off sys.executable, "-I", - "-W", "error", - "-c", f"import {import_path!s}", + "-W", + "error", + "-c", + f"import {import_path!s}", # fmt: on ) @@ -99,8 +101,10 @@ def test_c_extension_preferred_by_default(monkeypatch: pytest.MonkeyPatch) -> No # fmt: off sys.executable, "-I", - "-W", "error", - "-c", "import multidict; raise SystemExit(int(" + "-W", + "error", + "-c", + "import multidict; raise SystemExit(int(" "multidict.istr.__module__ != 'multidict._multidict' " "or multidict.USE_EXTENSIONS is not True))", # fmt: on diff --git a/contrib/python/multidict/tests/test_multidict.py b/contrib/python/multidict/tests/test_multidict.py index d1539fb45d2..eec0b862cd6 100644 --- a/contrib/python/multidict/tests/test_multidict.py +++ b/contrib/python/multidict/tests/test_multidict.py @@ -58,7 +58,7 @@ def chained_callable( @pytest.fixture -def cls( # type: ignore[misc] +def cls( request: pytest.FixtureRequest, multidict_module: ModuleType, ) -> Callable[..., MultiMapping[int | str] | MutableMultiMapping[int | str]]: @@ -708,7 +708,7 @@ class TestMultiDict(BaseMultiDictTest): ("MultiDict", "MultiDictProxy"), ], ) - def cls( # type: ignore[misc] + def cls( self, request: pytest.FixtureRequest, multidict_module: ModuleType, @@ -800,7 +800,7 @@ class TestCIMultiDict(BaseMultiDictTest): ("CIMultiDict", "CIMultiDictProxy"), ], ) - def cls( # type: ignore[misc] + def cls( self, request: pytest.FixtureRequest, multidict_module: ModuleType, @@ -1093,17 +1093,19 @@ class TestCIMultiDict(BaseMultiDictTest): assert arg & d.items() == expected def test_items_case_insensitive_or(self, cls: type[CIMultiDict[str]]) -> None: - d = cls([("KEY", "one")]) + d = cls([("K", "v"), ("KEY", "one")]) assert d.items() | {("key", "one"), ("other", "two")} == { + ("K", "v"), ("KEY", "one"), ("other", "two"), } def test_items_case_insensitive_ror(self, cls: type[CIMultiDict[str]]) -> None: - d = cls([("KEY", "one"), ("KEY2", "three")]) + d = cls([("K", "v"), ("KEY", "one"), ("KEY2", "three")]) assert [("key", "one"), ("other", "two")] | d.items() == { + ("K", "v"), ("key", "one"), ("other", "two"), ("KEY2", "three"), @@ -1333,13 +1335,19 @@ def test_view_direct_instantiation_segfault() -> None: This test only applies to the C extension implementation. """ # Test that _ItemsView cannot be instantiated directly - with pytest.raises(TypeError, match="cannot create '.*_ItemsView' instances directly"): + with pytest.raises( + TypeError, match="cannot create '.*_ItemsView' instances directly" + ): multidict._ItemsView() # type: ignore[attr-defined] # Test that _KeysView cannot be instantiated directly - with pytest.raises(TypeError, match="cannot create '.*_KeysView' instances directly"): + with pytest.raises( + TypeError, match="cannot create '.*_KeysView' instances directly" + ): multidict._KeysView() # type: ignore[attr-defined] # Test that _ValuesView cannot be instantiated directly - with pytest.raises(TypeError, match="cannot create '.*_ValuesView' instances directly"): + with pytest.raises( + TypeError, match="cannot create '.*_ValuesView' instances directly" + ): multidict._ValuesView() # type: ignore[attr-defined] diff --git a/contrib/python/multidict/tests/test_multidict_benchmarks.py b/contrib/python/multidict/tests/test_multidict_benchmarks.py index b5cc193c6af..b27041493ba 100644 --- a/contrib/python/multidict/tests/test_multidict_benchmarks.py +++ b/contrib/python/multidict/tests/test_multidict_benchmarks.py @@ -261,7 +261,8 @@ def test_multidict_getall_str_hit( @benchmark def _run() -> None: - md.getall("key3") + for i in range(30): + md.getall("key3") def test_multidict_getall_str_miss( @@ -273,7 +274,8 @@ def test_multidict_getall_str_miss( @benchmark def _run() -> None: - md.getall("miss", ()) + for i in range(30): + md.getall("miss", ()) def test_cimultidict_getall_istr_hit( @@ -287,7 +289,8 @@ def test_cimultidict_getall_istr_hit( @benchmark def _run() -> None: - md.getall(all_istr) + for i in range(30): + md.getall(all_istr) def test_cimultidict_getall_istr_miss( @@ -301,7 +304,8 @@ def test_cimultidict_getall_istr_miss( @benchmark def _run() -> None: - md.getall(miss_istr, ()) + for i in range(30): + md.getall(miss_istr, ()) def test_multidict_fetch( diff --git a/contrib/python/multidict/tests/test_mutable_multidict.py b/contrib/python/multidict/tests/test_mutable_multidict.py index 74bbd697fee..d6fe42fc255 100644 --- a/contrib/python/multidict/tests/test_mutable_multidict.py +++ b/contrib/python/multidict/tests/test_mutable_multidict.py @@ -34,8 +34,7 @@ class TestMutableMultiDict: d = case_sensitive_multidict_class([("key", "one"), ("key", "two")]) expected = ( - f"<{case_sensitive_multidict_class.__name__}" - "('key': 'one', 'key': 'two')>" + f"<{case_sensitive_multidict_class.__name__}('key': 'one', 'key': 'two')>" ) assert str(d) == expected @@ -174,6 +173,21 @@ class TestMutableMultiDict: d.add("key", "val2") assert ("key", "val2") == d.popitem() + assert len(d) == 1 + assert [("key", "val1")] == list(d.items()) + + def test_popitem2( + self, + case_sensitive_multidict_class: type[CIMultiDict[str]], + ) -> None: + d = case_sensitive_multidict_class() + d.add("key", "val1") + d.add("key", "val2") + d.add("key2", "val3") + + del d["key2"] # make dummy at the end + + assert ("key", "val2") == d.popitem() assert [("key", "val1")] == list(d.items()) def test_popitem_empty_multidict( @@ -365,6 +379,56 @@ class TestMutableMultiDict: with pytest.raises(TypeError): d.update("foo", "bar") # type: ignore[arg-type, call-arg] + def test_repr_with_dummy( + self, case_sensitive_multidict_class: type[MultiDict[int]] + ) -> None: + d = case_sensitive_multidict_class({"a": 1, "b": 2, "c": 3}) + cls = d.__class__.__name__ + del d["b"] # make a dummy entry + assert repr(d) == f"<{cls}('a': 1, 'c': 3)>" + + def test_items_repr_with_dummy( + self, case_sensitive_multidict_class: type[MultiDict[int]] + ) -> None: + d = case_sensitive_multidict_class({"a": 1, "b": 2, "c": 3}) + del d["b"] # make a dummy entry + cls = d.items().__class__.__name__ + assert repr(d.items()) == f"<{cls}('a': 1, 'c': 3)>" + + def test_keys_repr_with_dummy( + self, case_sensitive_multidict_class: type[MultiDict[int]] + ) -> None: + d = case_sensitive_multidict_class({"a": 1, "b": 2, "c": 3}) + del d["b"] # make a dummy entry + cls = d.keys().__class__.__name__ + assert repr(d.keys()) == f"<{cls}('a', 'c')>" + + def test_values_repr_with_dummy( + self, case_sensitive_multidict_class: type[MultiDict[int]] + ) -> None: + d = case_sensitive_multidict_class({"a": 1, "b": 2, "c": 3}) + del d["b"] # make a dummy entry + cls = d.values().__class__.__name__ + assert repr(d.values()) == f"<{cls}(1, 3)>" + + def test_huge_md( + self, + case_sensitive_multidict_class: type[MultiDict[int]], + ) -> None: + size = 1 << 16 + d = case_sensitive_multidict_class((str(i), i) for i in range(size)) + assert d[str(size // 2)] == size // 2 + + def test_create_from_proxy( + self, + case_sensitive_multidict_class: type[MultiDict[int]], + case_sensitive_multidict_proxy_class: type[MultiDictProxy[int]], + ) -> None: + d = case_sensitive_multidict_class({"a": 1, "b": 2, "c": 3}) + p = case_sensitive_multidict_proxy_class(d) + d2 = case_sensitive_multidict_class(p) + assert d2 == d + class TestCIMutableMultiDict: def test_getall( @@ -429,8 +493,7 @@ class TestCIMutableMultiDict: d = case_insensitive_multidict_class([("KEY", "one"), ("KEY", "two")]) expected = ( - f"<{case_insensitive_multidict_class.__name__}" - "('KEY': 'one', 'KEY': 'two')>" + f"<{case_insensitive_multidict_class.__name__}('KEY': 'one', 'KEY': 'two')>" ) assert str(d) == expected @@ -730,3 +793,23 @@ class TestCIMutableMultiDict: k, v = d.popitem() assert type(k) is case_insensitive_str_class + + def test_issue_1195( + self, case_insensitive_multidict_class: type[CIMultiDict[bytes]] + ) -> None: + md = case_insensitive_multidict_class( + { + "User-Agent": b"Bacon/1.0", + "Cookie": b"valued-visitor=yes;foo=bar", + "X-Bar": b"Foo", + "X-Foo": b"Bar", + "Referer": b"https://httpie.org/", + } + ) + + md2 = md.copy() + + md.popone("User-Agent") + assert md.keys() == md2.keys() - {"User-Agent"} + md.update([("User-Agent", b"Bacon/1.0")]) + assert md.keys() == md2.keys() diff --git a/contrib/python/multidict/tests/test_types.py b/contrib/python/multidict/tests/test_types.py index 6339006b68f..65f83f66a2a 100644 --- a/contrib/python/multidict/tests/test_types.py +++ b/contrib/python/multidict/tests/test_types.py @@ -75,8 +75,7 @@ def test_create_cimultidict_proxy_from_nonmultidict( with pytest.raises( TypeError, match=( - "ctor requires CIMultiDict or CIMultiDictProxy instance, " - "not <class 'dict'>" + "ctor requires CIMultiDict or CIMultiDictProxy instance, not <class 'dict'>" ), ): multidict_module.CIMultiDictProxy({}) diff --git a/contrib/python/multidict/tests/test_update.py b/contrib/python/multidict/tests/test_update.py index 46ab30a08bd..a1d8814a3b2 100644 --- a/contrib/python/multidict/tests/test_update.py +++ b/contrib/python/multidict/tests/test_update.py @@ -119,3 +119,32 @@ def test_update_deque_arg_and_kwds(any_multidict_class: _MD_Classes) -> None: obj.update(arg, b=2) assert list(obj.items()) == [("a", 1), ("b", 2)] assert arg == deque([("a", 1)]) + + +def test_update_with_second_md(any_multidict_class: _MD_Classes) -> None: + obj1 = any_multidict_class() + obj2 = any_multidict_class([("a", 2)]) + obj1.update(obj2) + assert obj1 == obj2 + + +def test_compact_after_deletion(any_multidict_class: _MD_Classes) -> None: + # multidict is resized when it is filled up to 2/3 of the index table size + NUM = 16 * 2 // 3 + obj = any_multidict_class((str(i), i) for i in range(NUM - 1)) + # keys.usable == 0 + # delete items, it adds empty entries but not reduce keys.usable + for i in range(5): + del obj[str(i)] + # adding an entry requres keys resizing to remove empty entries + dct = {str(i): i for i in range(100, 105)} + obj.extend(dct) + assert obj == {str(i): i for i in range(5, NUM - 1)} | dct + + +def test_update_with_empty_slots(any_multidict_class: _MD_Classes) -> None: + # multidict is resized when it is filled up to 2/3 of the index table size + obj = any_multidict_class([("0", 0), ("1", 1), ("1", 2)]) + del obj["0"] + obj.update({"1": 100}) + assert obj == {"1": 100} diff --git a/contrib/python/multidict/ya.make b/contrib/python/multidict/ya.make index 81c5990eb8a..c24361e5d33 100644 --- a/contrib/python/multidict/ya.make +++ b/contrib/python/multidict/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.4.4) +VERSION(6.5.1) LICENSE(Apache-2.0) |