Skip to content

Commit cc65be9

Browse files
TeamTamoadyouknowone
authored andcommitted
Fix hash function for Tuple
1 parent 5ee5019 commit cc65be9

File tree

3 files changed

+46
-7
lines changed

3 files changed

+46
-7
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

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
use super::{PyInt, PyIntRef, PySlice, PyTupleRef, PyType, PyTypeRef};
1+
use super::{
2+
builtins_iter, tuple::tuple_hash, PyInt, PyIntRef, PySlice, PyTupleRef, PyType, PyTypeRef,
3+
};
24
use crate::common::hash::PyHash;
35
use crate::{
4-
builtins::builtins_iter,
56
class::PyClassImpl,
67
function::{FuncArgs, OptionalArg, PyComparisonValue},
78
protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods},
@@ -440,7 +441,7 @@ impl Hashable for PyRange {
440441
zelf.step().into(),
441442
]
442443
};
443-
crate::utils::hash_iter(elements.iter(), vm)
444+
tuple_hash(&elements, vm)
444445
}
445446
}
446447

vm/src/builtins/tuple.rs

+42-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use super::{PositionIterInternal, PyGenericAlias, PyType, PyTypeRef};
2-
use crate::common::{hash::PyHash, lock::PyMutex};
2+
use crate::common::{
3+
hash::{PyHash, PyUHash},
4+
lock::PyMutex,
5+
};
36
use crate::{
47
class::PyClassImpl,
58
convert::{ToPyObject, TransmuteFromObject},
@@ -371,7 +374,7 @@ impl AsSequence for PyTuple {
371374
impl Hashable for PyTuple {
372375
#[inline]
373376
fn hash(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
374-
crate::utils::hash_iter(zelf.elements.iter(), vm)
377+
tuple_hash(zelf.as_slice(), vm)
375378
}
376379
}
377380

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

0 commit comments

Comments
 (0)