3
3
use std:: borrow:: Cow ;
4
4
use std:: convert:: { TryFrom , TryInto } ;
5
5
use std:: fmt;
6
+ use std:: hash;
6
7
use std:: iter;
7
8
use std:: ops:: { Deref , Range } ;
8
9
use std:: ptr;
@@ -25,7 +26,7 @@ use crate::ty;
25
26
/// Its public API is rather low-level, working directly with allocation offsets and a custom error
26
27
/// type to account for the lack of an AllocId on this level. The Miri/CTFE core engine `memory`
27
28
/// module provides higher-level access.
28
- #[ derive( Clone , Debug , Eq , PartialEq , PartialOrd , Ord , Hash , TyEncodable , TyDecodable ) ]
29
+ #[ derive( Clone , Debug , Eq , PartialEq , PartialOrd , Ord , TyEncodable , TyDecodable ) ]
29
30
#[ derive( HashStable ) ]
30
31
pub struct Allocation < Tag = AllocId , Extra = ( ) > {
31
32
/// The actual bytes of the allocation.
@@ -49,6 +50,46 @@ pub struct Allocation<Tag = AllocId, Extra = ()> {
49
50
pub extra : Extra ,
50
51
}
51
52
53
+ /// This is the maximum size we will hash at a time from these two structures, when interning. Note,
54
+ /// we hash that amount of bytes twice: at the start, and at the end of a buffer. Used when an
55
+ /// `Allocation` and its `InitMask` are large: we only partially hash the larger fields in that
56
+ /// situation. See the comment at the top of their respective `Hash` impl for more details.
57
+ const MAX_BYTES_TO_HASH : usize = 64 ;
58
+
59
+ /// This is the maximum size (in bytes) for which a buffer will be fully hashed when interning.
60
+ /// Otherwise, it will be partially hashed in 2 slices, requiring at least 2 `MAX_BYTES_TO_HASH`
61
+ /// bytes.
62
+ const MAX_HASHED_BUFFER_LEN : usize = 2 * MAX_BYTES_TO_HASH ;
63
+
64
+ // Const allocations are only hashed for interning. However, they can be large, making the hashing
65
+ // expensive especially since it uses `FxHash`: it's better suited to short keys, not potentially
66
+ // big buffers like the actual bytes of allocation. We can partially hash some fields when they're
67
+ // large.
68
+ impl hash:: Hash for Allocation {
69
+ fn hash < H : hash:: Hasher > ( & self , state : & mut H ) {
70
+ // Partially hash the `bytes` buffer when it is large. To limit collisions with common
71
+ // prefixes and suffixes, we hash the length and some slices of the buffer.
72
+ let byte_count = self . bytes . len ( ) ;
73
+ if byte_count > MAX_HASHED_BUFFER_LEN {
74
+ // Hash the buffer's length.
75
+ byte_count. hash ( state) ;
76
+
77
+ // And its head and tail.
78
+ self . bytes [ ..MAX_BYTES_TO_HASH ] . hash ( state) ;
79
+ self . bytes [ byte_count - MAX_BYTES_TO_HASH ..] . hash ( state) ;
80
+ } else {
81
+ self . bytes . hash ( state) ;
82
+ }
83
+
84
+ // Hash the other fields as usual.
85
+ self . relocations . hash ( state) ;
86
+ self . init_mask . hash ( state) ;
87
+ self . align . hash ( state) ;
88
+ self . mutability . hash ( state) ;
89
+ self . extra . hash ( state) ;
90
+ }
91
+ }
92
+
52
93
/// Interned types generally have an `Outer` type and an `Inner` type, where
53
94
/// `Outer` is a newtype around `Interned<Inner>`, and all the operations are
54
95
/// done on `Outer`, because all occurrences are interned. E.g. `Ty` is an
@@ -640,13 +681,41 @@ type Block = u64;
640
681
641
682
/// A bitmask where each bit refers to the byte with the same index. If the bit is `true`, the byte
642
683
/// is initialized. If it is `false` the byte is uninitialized.
643
- #[ derive( Clone , Debug , Eq , PartialEq , PartialOrd , Ord , Hash , TyEncodable , TyDecodable ) ]
684
+ #[ derive( Clone , Debug , Eq , PartialEq , PartialOrd , Ord , TyEncodable , TyDecodable ) ]
644
685
#[ derive( HashStable ) ]
645
686
pub struct InitMask {
646
687
blocks : Vec < Block > ,
647
688
len : Size ,
648
689
}
649
690
691
+ // Const allocations are only hashed for interning. However, they can be large, making the hashing
692
+ // expensive especially since it uses `FxHash`: it's better suited to short keys, not potentially
693
+ // big buffers like the allocation's init mask. We can partially hash some fields when they're
694
+ // large.
695
+ impl hash:: Hash for InitMask {
696
+ fn hash < H : hash:: Hasher > ( & self , state : & mut H ) {
697
+ const MAX_BLOCKS_TO_HASH : usize = MAX_BYTES_TO_HASH / std:: mem:: size_of :: < Block > ( ) ;
698
+ const MAX_BLOCKS_LEN : usize = MAX_HASHED_BUFFER_LEN / std:: mem:: size_of :: < Block > ( ) ;
699
+
700
+ // Partially hash the `blocks` buffer when it is large. To limit collisions with common
701
+ // prefixes and suffixes, we hash the length and some slices of the buffer.
702
+ let block_count = self . blocks . len ( ) ;
703
+ if block_count > MAX_BLOCKS_LEN {
704
+ // Hash the buffer's length.
705
+ block_count. hash ( state) ;
706
+
707
+ // And its head and tail.
708
+ self . blocks [ ..MAX_BLOCKS_TO_HASH ] . hash ( state) ;
709
+ self . blocks [ block_count - MAX_BLOCKS_TO_HASH ..] . hash ( state) ;
710
+ } else {
711
+ self . blocks . hash ( state) ;
712
+ }
713
+
714
+ // Hash the other fields as usual.
715
+ self . len . hash ( state) ;
716
+ }
717
+ }
718
+
650
719
impl InitMask {
651
720
pub const BLOCK_SIZE : u64 = 64 ;
652
721
0 commit comments