Skip to content

Commit c2955f8

Browse files
TeamTamoadyouknowone
authored andcommitted
Fix hash function for Tuple
1 parent 6d5bbd9 commit c2955f8

File tree

3 files changed

+49
-10
lines changed

3 files changed

+49
-10
lines changed

Lib/test/test_tuple.py

-2
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,6 @@ def f():
7777
# We expect tuples whose base components have deterministic hashes to
7878
# have deterministic hashes too - and, indeed, the same hashes across
7979
# platforms with hash codes of the same bit width.
80-
# TODO: RUSTPYTHON
81-
@unittest.expectedFailure
8280
def test_hash_exact(self):
8381
def check_one_exact(t, e32, e64):
8482
got = hash(t)

vm/src/builtins/range.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use super::{PyInt, PyIntRef, PySlice, PyTupleRef, PyType, PyTypeRef};
2-
use crate::atomic_func;
3-
use crate::common::hash::PyHash;
1+
use super::{
2+
builtins_iter, tuple::tuple_hash, PyInt, PyIntRef, PySlice, PyTupleRef, PyType, PyTypeRef,
3+
};
44
use crate::{
5-
builtins::builtins_iter,
5+
atomic_func,
66
class::PyClassImpl,
7+
common::hash::PyHash,
78
function::{FuncArgs, OptionalArg, PyComparisonValue},
89
protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods},
910
types::{
@@ -442,7 +443,7 @@ impl Hashable for PyRange {
442443
zelf.step().into(),
443444
]
444445
};
445-
crate::utils::hash_iter(elements.iter(), vm)
446+
tuple_hash(&elements, vm)
446447
}
447448
}
448449

vm/src/builtins/tuple.rs

+43-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
use once_cell::sync::Lazy;
22

33
use super::{PositionIterInternal, PyGenericAlias, PyType, PyTypeRef};
4-
use crate::atomic_func;
5-
use crate::common::{hash::PyHash, lock::PyMutex};
4+
use crate::common::{
5+
hash::{PyHash, PyUHash},
6+
lock::PyMutex,
7+
};
68
use crate::{
9+
atomic_func,
710
class::PyClassImpl,
811
convert::{ToPyObject, TransmuteFromObject},
912
function::{OptionalArg, PyArithmeticValue, PyComparisonValue},
@@ -372,7 +375,7 @@ impl AsSequence for PyTuple {
372375
impl Hashable for PyTuple {
373376
#[inline]
374377
fn hash(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
375-
crate::utils::hash_iter(zelf.elements.iter(), vm)
378+
tuple_hash(zelf.as_slice(), vm)
376379
}
377380
}
378381

@@ -523,3 +526,40 @@ impl<T: TransmuteFromObject> ToPyObject for PyTupleTyped<T> {
523526
self.tuple.into()
524527
}
525528
}
529+
530+
pub fn tuple_hash(elements: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<PyHash> {
531+
#[cfg(target_pointer_width = "64")]
532+
const PRIME1: PyUHash = 11400714785074694791;
533+
#[cfg(target_pointer_width = "64")]
534+
const PRIME2: PyUHash = 14029467366897019727;
535+
#[cfg(target_pointer_width = "64")]
536+
const PRIME5: PyUHash = 2870177450012600261;
537+
#[cfg(target_pointer_width = "64")]
538+
const ROTATE: u32 = 31;
539+
540+
#[cfg(target_pointer_width = "32")]
541+
const PRIME1: PyUHash = 2654435761;
542+
#[cfg(target_pointer_width = "32")]
543+
const PRIME2: PyUHash = 2246822519;
544+
#[cfg(target_pointer_width = "32")]
545+
const PRIME5: PyUHash = 374761393;
546+
#[cfg(target_pointer_width = "32")]
547+
const ROTATE: u32 = 13;
548+
549+
let mut acc = PRIME5;
550+
let len = elements.len() as PyUHash;
551+
552+
for val in elements {
553+
let lane = val.hash(vm)? as PyUHash;
554+
acc = acc.wrapping_add(lane.wrapping_mul(PRIME2));
555+
acc = acc.rotate_left(ROTATE);
556+
acc = acc.wrapping_mul(PRIME1);
557+
}
558+
559+
acc = acc.wrapping_add(len ^ (PRIME5 ^ 3527539));
560+
561+
if acc as PyHash == -1 {
562+
return Ok(1546275796);
563+
}
564+
Ok(acc as PyHash)
565+
}

0 commit comments

Comments
 (0)