33use std:: borrow:: Cow ;
44use std:: convert:: { TryFrom , TryInto } ;
55use std:: fmt;
6+ use std:: hash;
67use std:: iter;
78use std:: ops:: { Deref , Range } ;
89use std:: ptr;
@@ -25,7 +26,7 @@ use crate::ty;
2526/// Its public API is rather low-level, working directly with allocation offsets and a custom error
2627/// type to account for the lack of an AllocId on this level. The Miri/CTFE core engine `memory`
2728/// 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 ) ]
2930#[ derive( HashStable ) ]
3031pub struct Allocation < Tag = AllocId , Extra = ( ) > {
3132 /// The actual bytes of the allocation.
@@ -49,6 +50,46 @@ pub struct Allocation<Tag = AllocId, Extra = ()> {
4950 pub extra : Extra ,
5051}
5152
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+
5293/// Interned types generally have an `Outer` type and an `Inner` type, where
5394/// `Outer` is a newtype around `Interned<Inner>`, and all the operations are
5495/// done on `Outer`, because all occurrences are interned. E.g. `Ty` is an
@@ -640,13 +681,41 @@ type Block = u64;
640681
641682/// A bitmask where each bit refers to the byte with the same index. If the bit is `true`, the byte
642683/// 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 ) ]
644685#[ derive( HashStable ) ]
645686pub struct InitMask {
646687 blocks : Vec < Block > ,
647688 len : Size ,
648689}
649690
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+
650719impl InitMask {
651720 pub const BLOCK_SIZE : u64 = 64 ;
652721
0 commit comments