1
1
use crate :: index:: diff:: Error ;
2
2
use crate :: { Change , CrateVersion } ;
3
- use ahash:: AHashSet ;
3
+ use ahash:: { AHashSet , RandomState } ;
4
4
use bstr:: BStr ;
5
5
use git_repository as git;
6
- use std:: hash:: Hash ;
7
-
8
- #[ repr( transparent) ]
9
- struct ChecksumWithVersion ( CrateVersion ) ;
10
-
11
- impl AsRef < ChecksumWithVersion > for CrateVersion {
12
- fn as_ref ( & self ) -> & ChecksumWithVersion {
13
- // Safety: this is safe because ChecksumWithVersion is just a repr[transparent] wrapper around CrateVersion
14
- unsafe { & * ( self as * const CrateVersion as * const ChecksumWithVersion ) }
15
- }
16
- }
17
- impl Hash for ChecksumWithVersion {
18
- fn hash < H : std:: hash:: Hasher > ( & self , state : & mut H ) {
19
- self . 0 . checksum . hash ( state)
20
- }
21
- }
22
-
23
- impl PartialEq for ChecksumWithVersion {
24
- fn eq ( & self , other : & Self ) -> bool {
25
- self . 0 . checksum == other. 0 . checksum
26
- }
27
- }
28
- impl Eq for ChecksumWithVersion { }
6
+ use hashbrown:: raw:: RawTable ;
29
7
30
8
#[ derive( Default ) ]
31
9
pub ( crate ) struct Delegate {
32
10
changes : Vec < Change > ,
33
11
err : Option < Error > ,
34
- temporary_line_map : AHashSet < & ' static [ u8 ] > ,
35
- temporary_version_map : AHashSet < ChecksumWithVersion > ,
36
12
}
37
13
38
14
impl Delegate {
@@ -85,32 +61,46 @@ impl Delegate {
85
61
}
86
62
Modification { .. } => {
87
63
if let Some ( diff) = change. event . diff ( ) . transpose ( ) ? {
64
+ let mut old_lines = AHashSet :: with_capacity ( 1024 ) ;
88
65
let location = change. location ;
89
66
for line in diff. old . data . lines ( ) {
90
67
// Safety: We transform an &'_ [u8] to and &'static [u8] here
91
68
// this is safe because we always drain the hashmap at the end of the function
92
69
// the reason the HashMap has a static is that we want to reuse
93
70
// the allocation for modifications
94
- self . temporary_line_map
95
- . insert ( unsafe { & * ( line as * const [ u8 ] ) } ) ;
71
+ old_lines. insert ( line) ;
96
72
}
97
73
74
+ // A RawTable is used to represent a Checksum -> CrateVersion map
75
+ // because the checksum is already stored in the CrateVersion
76
+ // and we want to avoid storing the checksum twice for performance reasons
77
+ let mut new_versions = RawTable :: with_capacity ( old_lines. len ( ) . min ( 1024 ) ) ;
78
+ let hasher = RandomState :: new ( ) ;
79
+
98
80
for line in diff. new . data . lines ( ) {
99
81
// first quickly check if the exact same line is already present in this file in that case we don't need to do anything else
100
- if self . temporary_line_map . remove ( line) {
82
+ if old_lines . remove ( line) {
101
83
continue ;
102
84
}
85
+ // no need to check if the checksum already exists in the hashmap
86
+ // as each checksum appear only once
103
87
let new_version = version_from_json_line ( line, location) ?;
104
- self . temporary_version_map
105
- . insert ( ChecksumWithVersion ( new_version) ) ;
88
+ new_versions. insert (
89
+ hasher. hash_one ( new_version. checksum ) ,
90
+ new_version,
91
+ |rehashed| hasher. hash_one ( rehashed. checksum ) ,
92
+ ) ;
106
93
}
107
94
108
95
let mut deleted = Vec :: new ( ) ;
109
- for line in self . temporary_line_map . drain ( ) {
96
+ for line in old_lines . drain ( ) {
110
97
let old_version = version_from_json_line ( line, location) ?;
111
- let new_version = self . temporary_version_map . take ( old_version. as_ref ( ) ) ;
98
+ let new_version = new_versions
99
+ . remove_entry ( hasher. hash_one ( old_version. checksum ) , |version| {
100
+ version. checksum == old_version. checksum
101
+ } ) ;
112
102
match new_version {
113
- Some ( ChecksumWithVersion ( new_version) ) => {
103
+ Some ( new_version) => {
114
104
let change = match ( old_version. yanked , new_version. yanked ) {
115
105
( true , false ) => Change :: Unyanked ( new_version) ,
116
106
( false , true ) => Change :: Yanked ( new_version) ,
@@ -127,7 +117,7 @@ impl Delegate {
127
117
versions : deleted,
128
118
} )
129
119
}
130
- for ChecksumWithVersion ( version) in self . temporary_version_map . drain ( ) {
120
+ for version in new_versions . drain ( ) {
131
121
let change = if version. yanked {
132
122
Change :: AddedAndYanked ( version)
133
123
} else {
0 commit comments