@@ -7,19 +7,19 @@ use std::collections::HashSet as Set;
7
7
use std:: sync:: Arc ;
8
8
9
9
use crate :: internal:: {
10
- Arena , DecisionLevel , IncompDpId , Incompatibility , PartialSolution , Relation , SatisfierSearch ,
11
- SmallVec ,
10
+ Arena , DecisionLevel , HashArena , Id , IncompDpId , Incompatibility , PartialSolution , Relation ,
11
+ SatisfierSearch , SmallVec ,
12
12
} ;
13
13
use crate :: { DependencyProvider , DerivationTree , Map , NoSolutionError , VersionSet } ;
14
14
15
15
/// Current state of the PubGrub algorithm.
16
16
#[ derive( Clone ) ]
17
17
pub ( crate ) struct State < DP : DependencyProvider > {
18
- root_package : DP :: P ,
18
+ pub root_package : Id < DP :: P > ,
19
19
root_version : DP :: V ,
20
20
21
21
#[ allow( clippy:: type_complexity) ]
22
- incompatibilities : Map < DP :: P , Vec < IncompDpId < DP > > > ,
22
+ incompatibilities : Map < Id < DP :: P > , Vec < IncompDpId < DP > > > ,
23
23
24
24
/// Store the ids of incompatibilities that are already contradicted.
25
25
/// For each one keep track of the decision level when it was found to be contradicted.
@@ -29,7 +29,7 @@ pub(crate) struct State<DP: DependencyProvider> {
29
29
/// All incompatibilities expressing dependencies,
30
30
/// with common dependents merged.
31
31
#[ allow( clippy:: type_complexity) ]
32
- merged_dependencies : Map < ( DP :: P , DP :: P ) , SmallVec < IncompDpId < DP > > > ,
32
+ merged_dependencies : Map < ( Id < DP :: P > , Id < DP :: P > ) , SmallVec < IncompDpId < DP > > > ,
33
33
34
34
/// Partial solution.
35
35
/// TODO: remove pub.
@@ -38,29 +38,35 @@ pub(crate) struct State<DP: DependencyProvider> {
38
38
/// The store is the reference storage for all incompatibilities.
39
39
pub ( crate ) incompatibility_store : Arena < Incompatibility < DP :: P , DP :: VS , DP :: M > > ,
40
40
41
+ /// The store is the reference storage for all packages.
42
+ pub ( crate ) package_store : HashArena < DP :: P > ,
43
+
41
44
/// This is a stack of work to be done in `unit_propagation`.
42
45
/// It can definitely be a local variable to that method, but
43
46
/// this way we can reuse the same allocation for better performance.
44
- unit_propagation_buffer : SmallVec < DP :: P > ,
47
+ unit_propagation_buffer : SmallVec < Id < DP :: P > > ,
45
48
}
46
49
47
50
impl < DP : DependencyProvider > State < DP > {
48
51
/// Initialization of PubGrub state.
49
52
pub ( crate ) fn init ( root_package : DP :: P , root_version : DP :: V ) -> Self {
50
53
let mut incompatibility_store = Arena :: new ( ) ;
54
+ let mut package_store = HashArena :: new ( ) ;
55
+ let root_package = package_store. alloc ( root_package) ;
51
56
let not_root_id = incompatibility_store. alloc ( Incompatibility :: not_root (
52
- root_package. clone ( ) ,
57
+ root_package,
53
58
root_version. clone ( ) ,
54
59
) ) ;
55
60
let mut incompatibilities = Map :: default ( ) ;
56
- incompatibilities. insert ( root_package. clone ( ) , vec ! [ not_root_id] ) ;
61
+ incompatibilities. insert ( root_package, vec ! [ not_root_id] ) ;
57
62
Self {
58
63
root_package,
59
64
root_version,
60
65
incompatibilities,
61
66
contradicted_incompatibilities : Map :: default ( ) ,
62
67
partial_solution : PartialSolution :: empty ( ) ,
63
68
incompatibility_store,
69
+ package_store,
64
70
unit_propagation_buffer : SmallVec :: Empty ,
65
71
merged_dependencies : Map :: default ( ) ,
66
72
}
@@ -75,18 +81,19 @@ impl<DP: DependencyProvider> State<DP> {
75
81
/// Add an incompatibility to the state.
76
82
pub ( crate ) fn add_incompatibility_from_dependencies (
77
83
& mut self ,
78
- package : DP :: P ,
84
+ package : Id < DP :: P > ,
79
85
version : DP :: V ,
80
86
deps : impl IntoIterator < Item = ( DP :: P , DP :: VS ) > ,
81
87
) -> std:: ops:: Range < IncompDpId < DP > > {
82
88
// Create incompatibilities and allocate them in the store.
83
89
let new_incompats_id_range =
84
90
self . incompatibility_store
85
- . alloc_iter ( deps. into_iter ( ) . map ( |dep| {
91
+ . alloc_iter ( deps. into_iter ( ) . map ( |( dep_p, dep_vs) | {
92
+ let dep_pid = self . package_store . alloc ( dep_p) ;
86
93
Incompatibility :: from_dependency (
87
- package. clone ( ) ,
94
+ package,
88
95
<DP :: VS as VersionSet >:: singleton ( version. clone ( ) ) ,
89
- dep ,
96
+ ( dep_pid , dep_vs ) ,
90
97
)
91
98
} ) ) ;
92
99
// Merge the newly created incompatibilities with the older ones.
@@ -98,7 +105,10 @@ impl<DP: DependencyProvider> State<DP> {
98
105
99
106
/// Unit propagation is the core mechanism of the solving algorithm.
100
107
/// CF <https://github.com/dart-lang/pub/blob/master/doc/solver.md#unit-propagation>
101
- pub ( crate ) fn unit_propagation ( & mut self , package : DP :: P ) -> Result < ( ) , NoSolutionError < DP > > {
108
+ pub ( crate ) fn unit_propagation (
109
+ & mut self ,
110
+ package : Id < DP :: P > ,
111
+ ) -> Result < ( ) , NoSolutionError < DP > > {
102
112
self . unit_propagation_buffer . clear ( ) ;
103
113
self . unit_propagation_buffer . push ( package) ;
104
114
while let Some ( current_package) = self . unit_propagation_buffer . pop ( ) {
@@ -120,7 +130,7 @@ impl<DP: DependencyProvider> State<DP> {
120
130
Relation :: Satisfied => {
121
131
log:: info!(
122
132
"Start conflict resolution because incompat satisfied:\n {}" ,
123
- current_incompat
133
+ current_incompat. display ( & self . package_store )
124
134
) ;
125
135
conflict_id = Some ( incompat_id) ;
126
136
break ;
@@ -131,7 +141,7 @@ impl<DP: DependencyProvider> State<DP> {
131
141
// but so does allocating a hash map and hashing each item.
132
142
// In practice `unit_propagation_buffer` is small enough that we can just do a linear scan.
133
143
if !self . unit_propagation_buffer . contains ( & package_almost) {
134
- self . unit_propagation_buffer . push ( package_almost. clone ( ) ) ;
144
+ self . unit_propagation_buffer . push ( package_almost) ;
135
145
}
136
146
// Add (not term) to the partial solution with incompat as cause.
137
147
self . partial_solution . add_derivation (
@@ -157,7 +167,7 @@ impl<DP: DependencyProvider> State<DP> {
157
167
self . build_derivation_tree ( terminal_incompat_id)
158
168
} ) ?;
159
169
self . unit_propagation_buffer . clear ( ) ;
160
- self . unit_propagation_buffer . push ( package_almost. clone ( ) ) ;
170
+ self . unit_propagation_buffer . push ( package_almost) ;
161
171
// Add to the partial solution with incompat as cause.
162
172
self . partial_solution . add_derivation (
163
173
package_almost,
@@ -180,12 +190,12 @@ impl<DP: DependencyProvider> State<DP> {
180
190
fn conflict_resolution (
181
191
& mut self ,
182
192
incompatibility : IncompDpId < DP > ,
183
- ) -> Result < ( DP :: P , IncompDpId < DP > ) , IncompDpId < DP > > {
193
+ ) -> Result < ( Id < DP :: P > , IncompDpId < DP > ) , IncompDpId < DP > > {
184
194
let mut current_incompat_id = incompatibility;
185
195
let mut current_incompat_changed = false ;
186
196
loop {
187
197
if self . incompatibility_store [ current_incompat_id]
188
- . is_terminal ( & self . root_package , & self . root_version )
198
+ . is_terminal ( self . root_package , & self . root_version )
189
199
{
190
200
return Err ( current_incompat_id) ;
191
201
} else {
@@ -197,7 +207,6 @@ impl<DP: DependencyProvider> State<DP> {
197
207
SatisfierSearch :: DifferentDecisionLevels {
198
208
previous_satisfier_level,
199
209
} => {
200
- let package = package. clone ( ) ;
201
210
self . backtrack (
202
211
current_incompat_id,
203
212
current_incompat_changed,
@@ -213,7 +222,7 @@ impl<DP: DependencyProvider> State<DP> {
213
222
package,
214
223
& self . incompatibility_store ,
215
224
) ;
216
- log:: info!( "prior cause: {}" , prior_cause) ;
225
+ log:: info!( "prior cause: {}" , prior_cause. display ( & self . package_store ) ) ;
217
226
current_incompat_id = self . incompatibility_store . alloc ( prior_cause) ;
218
227
current_incompat_changed = true ;
219
228
}
@@ -256,19 +265,16 @@ impl<DP: DependencyProvider> State<DP> {
256
265
fn merge_incompatibility ( & mut self , mut id : IncompDpId < DP > ) {
257
266
if let Some ( ( p1, p2) ) = self . incompatibility_store [ id] . as_dependency ( ) {
258
267
// If we are a dependency, there's a good chance we can be merged with a previous dependency
259
- let deps_lookup = self
260
- . merged_dependencies
261
- . entry ( ( p1. clone ( ) , p2. clone ( ) ) )
262
- . or_default ( ) ;
268
+ let deps_lookup = self . merged_dependencies . entry ( ( p1, p2) ) . or_default ( ) ;
263
269
if let Some ( ( past, merged) ) = deps_lookup. as_mut_slice ( ) . iter_mut ( ) . find_map ( |past| {
264
270
self . incompatibility_store [ id]
265
271
. merge_dependents ( & self . incompatibility_store [ * past] )
266
272
. map ( |m| ( past, m) )
267
273
} ) {
268
274
let new = self . incompatibility_store . alloc ( merged) ;
269
- for ( pkg, _) in self . incompatibility_store [ new] . iter ( ) {
275
+ for ( & pkg, _) in self . incompatibility_store [ new] . iter ( ) {
270
276
self . incompatibilities
271
- . entry ( pkg. clone ( ) )
277
+ . entry ( pkg)
272
278
. or_default ( )
273
279
. retain ( |id| id != past) ;
274
280
}
@@ -278,14 +284,11 @@ impl<DP: DependencyProvider> State<DP> {
278
284
deps_lookup. push ( id) ;
279
285
}
280
286
}
281
- for ( pkg, term) in self . incompatibility_store [ id] . iter ( ) {
287
+ for ( & pkg, term) in self . incompatibility_store [ id] . iter ( ) {
282
288
if cfg ! ( debug_assertions) {
283
289
assert_ne ! ( term, & crate :: term:: Term :: any( ) ) ;
284
290
}
285
- self . incompatibilities
286
- . entry ( pkg. clone ( ) )
287
- . or_default ( )
288
- . push ( id) ;
291
+ self . incompatibilities . entry ( pkg) . or_default ( ) . push ( id) ;
289
292
}
290
293
}
291
294
@@ -320,6 +323,7 @@ impl<DP: DependencyProvider> State<DP> {
320
323
id,
321
324
& shared_ids,
322
325
& self . incompatibility_store ,
326
+ & self . package_store ,
323
327
& precomputed,
324
328
) ;
325
329
precomputed. insert ( id, Arc :: new ( tree) ) ;
0 commit comments