mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Reuse stored hashes in dict.fromkeys() (#8503)
* Reuse stored hashes in dict.fromkeys() Closes #8490. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * Make fromkeys_known_hashes a PyDict associated fn --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
3a98ef746a
commit
db5de5e5e2
3 files changed
+43
-5
No files matched your search
Vendored
-1
@@ -330,7 +330,6 @@ class TestJointOps:
|
||||
name = repr(s).partition('(')[0] # strip class name
|
||||
self.assertEqual(repr(s), '%s({%s(...)})' % (name, name))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_do_not_rehash_dict_keys(self):
|
||||
n = 10
|
||||
d = dict.fromkeys(map(HashCountingInt, range(n)))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
IterStatus, PositionIterInternal, PyBaseExceptionRef, PyGenericAlias, PyMappingProxy, PySet,
|
||||
PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, set::PySetInner,
|
||||
PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, set, set::PySetInner,
|
||||
};
|
||||
use crate::common::lock::LazyLock;
|
||||
use crate::object::{Traverse, TraverseFn};
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
TryFromObject, atomic_func,
|
||||
builtins::{PyList, PyTuple, iter::builtins_iter, type_::PyAttributes},
|
||||
class::{PyClassDef, PyClassImpl},
|
||||
common::ascii,
|
||||
common::{ascii, hash::PyHash},
|
||||
dict_inner::{self, DictKey},
|
||||
function::{ArgIterable, FuncArgs, KwArgs, OptionalArg, PyArithmeticValue, PyComparisonValue},
|
||||
iter::PyExactSizeIterator,
|
||||
@@ -354,6 +354,20 @@ impl PyDict {
|
||||
) -> PyResult<Option<PyObjectRef>> {
|
||||
self.entries.get(vm, key)
|
||||
}
|
||||
|
||||
/// Keys of `obj` with their stored hashes, or `None` if it must be iterated
|
||||
/// generically. Only exact dicts and sets qualify, as in CPython's
|
||||
/// `_PyDict_FromKeys`: a subclass may override `__iter__`.
|
||||
fn fromkeys_known_hashes(
|
||||
obj: &PyObject,
|
||||
vm: &VirtualMachine,
|
||||
) -> Option<Vec<(PyObjectRef, PyHash)>> {
|
||||
if let Some(dict) = obj.downcast_ref_if_exact::<Self>(vm) {
|
||||
Some(dict.entries.keys_with_hashes())
|
||||
} else {
|
||||
set::exact_set_keys_with_hashes(obj, vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Python dict methods:
|
||||
@@ -384,8 +398,16 @@ impl PyDict {
|
||||
let d = PyType::call(&class, ().into(), vm)?;
|
||||
match d.downcast_exact::<Self>(vm) {
|
||||
Ok(pydict) => {
|
||||
for key in iterable.iter(vm)? {
|
||||
pydict.__setitem__(key?, value.clone(), vm)?;
|
||||
if let Some(keys) = Self::fromkeys_known_hashes(iterable.as_object(), vm) {
|
||||
for (key, hash) in keys {
|
||||
pydict
|
||||
.entries
|
||||
.insert_known_hash(vm, &*key, hash, value.clone())?;
|
||||
}
|
||||
} else {
|
||||
for key in iterable.iter(vm)? {
|
||||
pydict.__setitem__(key?, value.clone(), vm)?;
|
||||
}
|
||||
}
|
||||
Ok(pydict.into_pyref().into())
|
||||
}
|
||||
|
||||
@@ -605,6 +605,23 @@ fn extract_set(obj: &PyObject) -> Option<&PySetInner> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Elements of `obj` with their stored hashes, or `None` unless `obj` is exactly
|
||||
/// a `set` or `frozenset` — `PyAnySet_CheckExact`, where [`extract_set`] is the
|
||||
/// subclass-inclusive `PyAnySet_Check`.
|
||||
pub(super) fn exact_set_keys_with_hashes(
|
||||
obj: &PyObject,
|
||||
vm: &VirtualMachine,
|
||||
) -> Option<Vec<(PyObjectRef, PyHash)>> {
|
||||
let inner = obj
|
||||
.downcast_ref_if_exact::<PySet>(vm)
|
||||
.map(|set| &set.inner)
|
||||
.or_else(|| {
|
||||
obj.downcast_ref_if_exact::<PyFrozenSet>(vm)
|
||||
.map(|frozen| &frozen.inner)
|
||||
})?;
|
||||
Some(inner.content.keys_with_hashes())
|
||||
}
|
||||
|
||||
fn reduce_set(zelf: &PyObject, vm: &VirtualMachine) -> (PyTypeRef, PyTupleRef, Option<PyDictRef>) {
|
||||
(
|
||||
zelf.class().to_owned(),
|
||||
|
||||
Reference in new issue
Block a user