Mercurial > hg
comparison rust/hg-core/src/utils.rs @ 46586:435d9fc72646
copies-rust: extract generic map merge logic from merge_copies_dict
This deduplicates the copy-tracing-specific logic
Differential Revision: https://phab.mercurial-scm.org/D9682
author | Simon Sapin <simon.sapin@octobus.net> |
---|---|
date | Wed, 23 Dec 2020 11:48:16 +0100 |
parents | a25033eb43b5 |
children | 755c31a1caf9 |
comparison
equal
deleted
inserted
replaced
46585:60b2b7ecf9cb | 46586:435d9fc72646 |
---|---|
7 | 7 |
8 //! Contains useful functions, traits, structs, etc. for use in core. | 8 //! Contains useful functions, traits, structs, etc. for use in core. |
9 | 9 |
10 use crate::errors::{HgError, IoErrorContext}; | 10 use crate::errors::{HgError, IoErrorContext}; |
11 use crate::utils::hg_path::HgPath; | 11 use crate::utils::hg_path::HgPath; |
12 use im_rc::ordmap::DiffItem; | |
13 use im_rc::ordmap::OrdMap; | |
12 use std::{io::Write, ops::Deref}; | 14 use std::{io::Write, ops::Deref}; |
13 | 15 |
14 pub mod files; | 16 pub mod files; |
15 pub mod hg_path; | 17 pub mod hg_path; |
16 pub mod path_auditor; | 18 pub mod path_auditor; |
197 std::env::current_exe().map_err(|error| HgError::IoError { | 199 std::env::current_exe().map_err(|error| HgError::IoError { |
198 error, | 200 error, |
199 context: IoErrorContext::CurrentExe, | 201 context: IoErrorContext::CurrentExe, |
200 }) | 202 }) |
201 } | 203 } |
204 | |
205 pub(crate) enum MergeResult<V> { | |
206 UseLeftValue, | |
207 UseRightValue, | |
208 UseNewValue(V), | |
209 } | |
210 | |
211 /// Return the union of the two given maps, | |
212 /// calling `merge(key, left_value, right_value)` to resolve keys that exist in | |
213 /// both. | |
214 /// | |
215 /// CC https://github.com/bodil/im-rs/issues/166 | |
216 pub(crate) fn ordmap_union_with_merge<K, V>( | |
217 left: OrdMap<K, V>, | |
218 right: OrdMap<K, V>, | |
219 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>, | |
220 ) -> OrdMap<K, V> | |
221 where | |
222 K: Clone + Ord, | |
223 V: Clone + PartialEq, | |
224 { | |
225 if left.ptr_eq(&right) { | |
226 // One of the two maps is an unmodified clone of the other | |
227 left | |
228 } else if left.len() / 2 > right.len() { | |
229 // When two maps have different sizes, | |
230 // their size difference is a lower bound on | |
231 // how many keys of the larger map are not also in the smaller map. | |
232 // This in turn is a lower bound on the number of differences in | |
233 // `OrdMap::diff` and the "amount of work" that would be done | |
234 // by `ordmap_union_with_merge_by_diff`. | |
235 // | |
236 // Here `left` is more than twice the size of `right`, | |
237 // so the number of differences is more than the total size of | |
238 // `right`. Therefore an algorithm based on iterating `right` | |
239 // is more efficient. | |
240 // | |
241 // This helps a lot when a tiny (or empty) map is merged | |
242 // with a large one. | |
243 ordmap_union_with_merge_by_iter(left, right, merge) | |
244 } else if left.len() < right.len() / 2 { | |
245 // Same as above but with `left` and `right` swapped | |
246 ordmap_union_with_merge_by_iter(right, left, |key, a, b| { | |
247 // Also swapped in `merge` arguments: | |
248 match merge(key, b, a) { | |
249 MergeResult::UseNewValue(v) => MergeResult::UseNewValue(v), | |
250 // … and swap back in `merge` result: | |
251 MergeResult::UseLeftValue => MergeResult::UseRightValue, | |
252 MergeResult::UseRightValue => MergeResult::UseLeftValue, | |
253 } | |
254 }) | |
255 } else { | |
256 // For maps of similar size, use the algorithm based on `OrdMap::diff` | |
257 ordmap_union_with_merge_by_diff(left, right, merge) | |
258 } | |
259 } | |
260 | |
261 /// Efficient if `right` is much smaller than `left` | |
262 fn ordmap_union_with_merge_by_iter<K, V>( | |
263 mut left: OrdMap<K, V>, | |
264 right: OrdMap<K, V>, | |
265 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>, | |
266 ) -> OrdMap<K, V> | |
267 where | |
268 K: Clone + Ord, | |
269 V: Clone, | |
270 { | |
271 for (key, right_value) in right { | |
272 match left.get(&key) { | |
273 None => { | |
274 left.insert(key, right_value); | |
275 } | |
276 Some(left_value) => match merge(&key, left_value, &right_value) { | |
277 MergeResult::UseLeftValue => {} | |
278 MergeResult::UseRightValue => { | |
279 left.insert(key, right_value); | |
280 } | |
281 MergeResult::UseNewValue(new_value) => { | |
282 left.insert(key, new_value); | |
283 } | |
284 }, | |
285 } | |
286 } | |
287 left | |
288 } | |
289 | |
290 /// Fallback when both maps are of similar size | |
291 fn ordmap_union_with_merge_by_diff<K, V>( | |
292 mut left: OrdMap<K, V>, | |
293 mut right: OrdMap<K, V>, | |
294 mut merge: impl FnMut(&K, &V, &V) -> MergeResult<V>, | |
295 ) -> OrdMap<K, V> | |
296 where | |
297 K: Clone + Ord, | |
298 V: Clone + PartialEq, | |
299 { | |
300 // (key, value) pairs that would need to be inserted in either map | |
301 // in order to turn it into the union. | |
302 // | |
303 // TODO: if/when https://github.com/bodil/im-rs/pull/168 is accepted, | |
304 // change these from `Vec<(K, V)>` to `Vec<(&K, Cow<V>)>` | |
305 // with `left_updates` only borrowing from `right` and `right_updates` from | |
306 // `left`, and with `Cow::Owned` used for `MergeResult::UseNewValue`. | |
307 // | |
308 // This would allow moving all `.clone()` calls to after we’ve decided | |
309 // which of `right_updates` or `left_updates` to use | |
310 // (value ones becoming `Cow::into_owned`), | |
311 // and avoid making clones we don’t end up using. | |
312 let mut left_updates = Vec::new(); | |
313 let mut right_updates = Vec::new(); | |
314 | |
315 for difference in left.diff(&right) { | |
316 match difference { | |
317 DiffItem::Add(key, value) => { | |
318 left_updates.push((key.clone(), value.clone())) | |
319 } | |
320 DiffItem::Remove(key, value) => { | |
321 right_updates.push((key.clone(), value.clone())) | |
322 } | |
323 DiffItem::Update { | |
324 old: (key, left_value), | |
325 new: (_, right_value), | |
326 } => match merge(key, left_value, right_value) { | |
327 MergeResult::UseLeftValue => { | |
328 right_updates.push((key.clone(), left_value.clone())) | |
329 } | |
330 MergeResult::UseRightValue => { | |
331 left_updates.push((key.clone(), right_value.clone())) | |
332 } | |
333 MergeResult::UseNewValue(new_value) => { | |
334 left_updates.push((key.clone(), new_value.clone())); | |
335 right_updates.push((key.clone(), new_value)) | |
336 } | |
337 }, | |
338 } | |
339 } | |
340 if left_updates.len() < right_updates.len() { | |
341 for (key, value) in left_updates { | |
342 left.insert(key, value); | |
343 } | |
344 left | |
345 } else { | |
346 for (key, value) in right_updates { | |
347 right.insert(key, value); | |
348 } | |
349 right | |
350 } | |
351 } |