Skip to content

Commit c5f11a7

Browse files
authored
Merge pull request RustPython#2586 from RustPython/coolreader18/hash-neg1-to-neg2
Make PyStr.hash an AtomicI64
2 parents 3f9159c + 7172616 commit c5f11a7

File tree

13 files changed

+288
-149
lines changed

13 files changed

+288
-149
lines changed

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_numeric_tower.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,6 @@ def test_bools(self):
2525
self.check_equal_hash(False, 0)
2626
self.check_equal_hash(True, 1)
2727

28-
# TODO: RUSTPYTHON
29-
@unittest.expectedFailure
3028
def test_integers(self):
3129
# check that equal values hash equal
3230

common/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ siphasher = "0.3"
2121
rand = "0.8"
2222
derive_more = "0.99.9"
2323
volatile = "0.3"
24+
radium = "0.6"

common/src/atomic.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
pub use core::sync::atomic::*;
2+
pub use radium::Radium;
3+
4+
mod sealed {
5+
pub trait Sealed {}
6+
}
7+
pub trait PyAtomicScalar: sealed::Sealed {
8+
type Radium: Radium<Item = Self>;
9+
}
10+
11+
pub type PyAtomic<T> = <T as PyAtomicScalar>::Radium;
12+
13+
#[cfg(feature = "threading")]
14+
macro_rules! atomic_ty {
15+
($i:ty, $atomic:ty) => {
16+
$atomic
17+
};
18+
}
19+
#[cfg(not(feature = "threading"))]
20+
macro_rules! atomic_ty {
21+
($i:ty, $atomic:ty) => {
22+
core::cell::Cell<$i>
23+
};
24+
}
25+
macro_rules! impl_atomic_scalar {
26+
($(($i:ty, $atomic:ty),)*) => {
27+
$(
28+
impl sealed::Sealed for $i {}
29+
impl PyAtomicScalar for $i {
30+
type Radium = atomic_ty!($i, $atomic);
31+
}
32+
)*
33+
};
34+
}
35+
impl_atomic_scalar!(
36+
(u8, AtomicU8),
37+
(i8, AtomicI8),
38+
(u16, AtomicU16),
39+
(i16, AtomicI16),
40+
(u32, AtomicU32),
41+
(i32, AtomicI32),
42+
(u64, AtomicU64),
43+
(i64, AtomicI64),
44+
(usize, AtomicUsize),
45+
(isize, AtomicIsize),
46+
(bool, AtomicBool),
47+
);
48+
49+
impl<T> sealed::Sealed for *mut T {}
50+
impl<T> PyAtomicScalar for *mut T {
51+
type Radium = atomic_ty!(*mut T, AtomicPtr<T>);
52+
}

common/src/hash.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ use std::num::Wrapping;
99
pub type PyHash = i64;
1010
pub type PyUHash = u64;
1111

12+
/// A PyHash value used to represent a missing hash value, e.g. means "not yet computed" for
13+
/// `str`'s hash cache
14+
pub const SENTINEL: PyHash = -1;
15+
1216
/// Prime multiplier used in string and various other hashes.
1317
pub const MULTIPLIER: PyHash = 1_000_003; // 0xf4243
1418
/// Numeric hashes are based on reduction modulo the prime 2**_BITS - 1
@@ -59,7 +63,7 @@ impl HashSecret {
5963
pub fn hash_value<T: Hash + ?Sized>(&self, data: &T) -> PyHash {
6064
let mut hasher = self.build_hasher();
6165
data.hash(&mut hasher);
62-
mod_int(hasher.finish() as PyHash)
66+
fix_sentinel(mod_int(hasher.finish() as PyHash))
6367
}
6468

6569
pub fn hash_iter<'a, T: 'a, I, F, E>(&self, iter: I, hashf: F) -> Result<PyHash, E>
@@ -72,7 +76,7 @@ impl HashSecret {
7276
let item_hash = hashf(element)?;
7377
item_hash.hash(&mut hasher);
7478
}
75-
Ok(mod_int(hasher.finish() as PyHash))
79+
Ok(fix_sentinel(mod_int(hasher.finish() as PyHash)))
7680
}
7781

7882
pub fn hash_bytes(&self, value: &[u8]) -> PyHash {
@@ -130,14 +134,14 @@ pub fn hash_float(value: f64) -> PyHash {
130134
};
131135
x = ((x << e) & MODULUS) | x >> (BITS32 - e);
132136

133-
x as PyHash * value.signum() as PyHash
137+
fix_sentinel(x as PyHash * value.signum() as PyHash)
134138
}
135139

136140
pub fn hash_complex(value: &Complex64) -> PyHash {
137141
let re_hash = hash_float(value.re);
138142
let im_hash = hash_float(value.im);
139143
let Wrapping(ret) = Wrapping(re_hash) + Wrapping(im_hash) * Wrapping(IMAG);
140-
ret
144+
fix_sentinel(ret)
141145
}
142146

143147
pub fn hash_iter_unordered<'a, T: 'a, I, F, E>(iter: I, hashf: F) -> Result<PyHash, E>
@@ -151,18 +155,27 @@ where
151155
// xor is commutative and hash should be independent of order
152156
hash ^= item_hash;
153157
}
154-
Ok(mod_int(hash))
158+
Ok(fix_sentinel(mod_int(hash)))
155159
}
156160

157161
pub fn hash_bigint(value: &BigInt) -> PyHash {
158-
value.to_i64().map_or_else(
159-
|| {
160-
(value % MODULUS).to_i64().unwrap_or_else(||
161-
// guaranteed to be safe by mod
162-
unsafe { std::hint::unreachable_unchecked() })
163-
},
164-
mod_int,
165-
)
162+
let ret = match value.to_i64() {
163+
Some(i) => mod_int(i),
164+
None => (value % MODULUS).to_i64().unwrap_or_else(|| unsafe {
165+
// SAFETY: MODULUS < i64::MAX, so value % MODULUS is guaranteed to be in the range of i64
166+
std::hint::unreachable_unchecked()
167+
}),
168+
};
169+
fix_sentinel(ret)
170+
}
171+
172+
#[inline(always)]
173+
fn fix_sentinel(x: PyHash) -> PyHash {
174+
if x == SENTINEL {
175+
-2
176+
} else {
177+
x
178+
}
166179
}
167180

168181
#[inline]

common/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! A crate to hold types and functions common to all rustpython components.
22
3+
pub mod atomic;
34
pub mod borrow;
45
pub mod boxvec;
56
pub mod cmp;

vm/src/builtins/dict.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crossbeam_utils::atomic::AtomicCell;
22
use std::fmt;
33
use std::mem::size_of;
44

5-
use super::pystr;
5+
use super::pystr::PyStrRef;
66
use super::pytype::PyTypeRef;
77
use super::set::PySet;
88
use crate::dictdatatype::{self, DictKey};
@@ -448,8 +448,8 @@ impl PyDictRef {
448448
pub fn to_attributes(self) -> PyAttributes {
449449
let mut attrs = PyAttributes::default();
450450
for (key, value) in self {
451-
let key = pystr::clone_value(&key);
452-
attrs.insert(key, value);
451+
let key: PyStrRef = key.downcast().expect("dict has non-string keys");
452+
attrs.insert(key.as_ref().to_owned(), value);
453453
}
454454
attrs
455455
}

0 commit comments

Comments
 (0)