Show More
@@ -17,6 +17,7 pub mod manifest; | |||||
17 | pub mod patch; |
|
17 | pub mod patch; | |
18 |
|
18 | |||
19 | use std::borrow::Cow; |
|
19 | use std::borrow::Cow; | |
|
20 | use std::collections::HashSet; | |||
20 | use std::io::Read; |
|
21 | use std::io::Read; | |
21 | use std::ops::Deref; |
|
22 | use std::ops::Deref; | |
22 | use std::path::Path; |
|
23 | use std::path::Path; | |
@@ -31,7 +32,12 use self::nodemap_docket::NodeMapDocket; | |||||
31 | use super::index::Index; |
|
32 | use super::index::Index; | |
32 | use super::index::INDEX_ENTRY_SIZE; |
|
33 | use super::index::INDEX_ENTRY_SIZE; | |
33 | use super::nodemap::{NodeMap, NodeMapError}; |
|
34 | use super::nodemap::{NodeMap, NodeMapError}; | |
|
35 | use crate::config::{Config, ResourceProfileValue}; | |||
34 | use crate::errors::HgError; |
|
36 | use crate::errors::HgError; | |
|
37 | use crate::exit_codes; | |||
|
38 | use crate::requirements::{ | |||
|
39 | GENERALDELTA_REQUIREMENT, NARROW_REQUIREMENT, SPARSEREVLOG_REQUIREMENT, | |||
|
40 | }; | |||
35 | use crate::vfs::Vfs; |
|
41 | use crate::vfs::Vfs; | |
36 |
|
42 | |||
37 | /// As noted in revlog.c, revision numbers are actually encoded in |
|
43 | /// As noted in revlog.c, revision numbers are actually encoded in | |
@@ -217,6 +223,383 impl RevlogError { | |||||
217 | } |
|
223 | } | |
218 | } |
|
224 | } | |
219 |
|
225 | |||
|
226 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | |||
|
227 | pub enum RevlogType { | |||
|
228 | Changelog, | |||
|
229 | Manifestlog, | |||
|
230 | Filelog, | |||
|
231 | } | |||
|
232 | ||||
|
233 | impl TryFrom<usize> for RevlogType { | |||
|
234 | type Error = HgError; | |||
|
235 | ||||
|
236 | fn try_from(value: usize) -> Result<Self, Self::Error> { | |||
|
237 | match value { | |||
|
238 | 1001 => Ok(Self::Changelog), | |||
|
239 | 1002 => Ok(Self::Manifestlog), | |||
|
240 | 1003 => Ok(Self::Filelog), | |||
|
241 | t => Err(HgError::abort( | |||
|
242 | format!("Unknown revlog type {}", t), | |||
|
243 | exit_codes::ABORT, | |||
|
244 | None, | |||
|
245 | )), | |||
|
246 | } | |||
|
247 | } | |||
|
248 | } | |||
|
249 | ||||
|
250 | #[derive(Debug, Copy, Clone, PartialEq, Eq)] | |||
|
251 | pub enum CompressionEngine { | |||
|
252 | Zlib { | |||
|
253 | /// Between 0 and 9 included | |||
|
254 | level: u32, | |||
|
255 | }, | |||
|
256 | Zstd { | |||
|
257 | /// Between 0 and 22 included | |||
|
258 | level: u32, | |||
|
259 | /// Never used in practice for now | |||
|
260 | threads: u32, | |||
|
261 | }, | |||
|
262 | /// No compression is performed | |||
|
263 | None, | |||
|
264 | } | |||
|
265 | impl CompressionEngine { | |||
|
266 | pub fn set_level(&mut self, new_level: usize) -> Result<(), HgError> { | |||
|
267 | match self { | |||
|
268 | CompressionEngine::Zlib { level } => { | |||
|
269 | if new_level > 9 { | |||
|
270 | return Err(HgError::abort( | |||
|
271 | format!( | |||
|
272 | "invalid compression zlib compression level {}", | |||
|
273 | new_level | |||
|
274 | ), | |||
|
275 | exit_codes::ABORT, | |||
|
276 | None, | |||
|
277 | )); | |||
|
278 | } | |||
|
279 | *level = new_level as u32; | |||
|
280 | } | |||
|
281 | CompressionEngine::Zstd { level, .. } => { | |||
|
282 | if new_level > 22 { | |||
|
283 | return Err(HgError::abort( | |||
|
284 | format!( | |||
|
285 | "invalid compression zstd compression level {}", | |||
|
286 | new_level | |||
|
287 | ), | |||
|
288 | exit_codes::ABORT, | |||
|
289 | None, | |||
|
290 | )); | |||
|
291 | } | |||
|
292 | *level = new_level as u32; | |||
|
293 | } | |||
|
294 | CompressionEngine::None => {} | |||
|
295 | } | |||
|
296 | Ok(()) | |||
|
297 | } | |||
|
298 | ||||
|
299 | pub fn zstd( | |||
|
300 | zstd_level: Option<u32>, | |||
|
301 | ) -> Result<CompressionEngine, HgError> { | |||
|
302 | let mut engine = CompressionEngine::Zstd { | |||
|
303 | level: 3, | |||
|
304 | threads: 0, | |||
|
305 | }; | |||
|
306 | if let Some(level) = zstd_level { | |||
|
307 | engine.set_level(level as usize)?; | |||
|
308 | } | |||
|
309 | Ok(engine) | |||
|
310 | } | |||
|
311 | } | |||
|
312 | ||||
|
313 | impl Default for CompressionEngine { | |||
|
314 | fn default() -> Self { | |||
|
315 | Self::Zlib { level: 6 } | |||
|
316 | } | |||
|
317 | } | |||
|
318 | ||||
|
319 | #[derive(Debug, Clone, Copy, PartialEq)] | |||
|
320 | /// Holds configuration values about how the revlog data is read | |||
|
321 | pub struct RevlogDataConfig { | |||
|
322 | /// Should we try to open the "pending" version of the revlog | |||
|
323 | pub try_pending: bool, | |||
|
324 | /// Should we try to open the "split" version of the revlog | |||
|
325 | pub try_split: bool, | |||
|
326 | /// When True, `indexfile` should be opened with `checkambig=True` at | |||
|
327 | /// writing time, to avoid file stat ambiguity | |||
|
328 | pub check_ambig: bool, | |||
|
329 | /// If true, use mmap instead of reading to deal with large indexes | |||
|
330 | pub mmap_large_index: bool, | |||
|
331 | /// How much data is considered large | |||
|
332 | pub mmap_index_threshold: Option<u64>, | |||
|
333 | /// How much data to read and cache into the raw revlog data cache | |||
|
334 | pub chunk_cache_size: u64, | |||
|
335 | /// The size of the uncompressed cache compared to the largest revision | |||
|
336 | /// seen | |||
|
337 | pub uncompressed_cache_factor: Option<f64>, | |||
|
338 | /// The number of chunks cached | |||
|
339 | pub uncompressed_cache_count: Option<u64>, | |||
|
340 | /// Allow sparse reading of the revlog data | |||
|
341 | pub with_sparse_read: bool, | |||
|
342 | /// Minimal density of a sparse read chunk | |||
|
343 | pub sr_density_threshold: f64, | |||
|
344 | /// Minimal size of the data we skip when performing sparse reads | |||
|
345 | pub sr_min_gap_size: u64, | |||
|
346 | /// Whether deltas are encoded against arbitrary bases | |||
|
347 | pub general_delta: bool, | |||
|
348 | } | |||
|
349 | ||||
|
350 | impl RevlogDataConfig { | |||
|
351 | pub fn new( | |||
|
352 | config: &Config, | |||
|
353 | requirements: &HashSet<String>, | |||
|
354 | ) -> Result<Self, HgError> { | |||
|
355 | let mut data_config = Self::default(); | |||
|
356 | if let Some(chunk_cache_size) = | |||
|
357 | config.get_byte_size(b"format", b"chunkcachesize")? | |||
|
358 | { | |||
|
359 | data_config.chunk_cache_size = chunk_cache_size; | |||
|
360 | } | |||
|
361 | ||||
|
362 | let memory_profile = config.get_resource_profile(Some("memory")); | |||
|
363 | if memory_profile.value >= ResourceProfileValue::Medium { | |||
|
364 | data_config.uncompressed_cache_count = Some(10_000); | |||
|
365 | data_config.uncompressed_cache_factor = Some(4.0); | |||
|
366 | if memory_profile.value >= ResourceProfileValue::High { | |||
|
367 | data_config.uncompressed_cache_factor = Some(10.0) | |||
|
368 | } | |||
|
369 | } | |||
|
370 | ||||
|
371 | if let Some(mmap_index_threshold) = | |||
|
372 | config.get_byte_size(b"experimental", b"mmapindexthreshold")? | |||
|
373 | { | |||
|
374 | data_config.mmap_index_threshold = Some(mmap_index_threshold); | |||
|
375 | } | |||
|
376 | ||||
|
377 | let with_sparse_read = | |||
|
378 | config.get_bool(b"experimental", b"sparse-read")?; | |||
|
379 | if let Some(sr_density_threshold) = config | |||
|
380 | .get_f64(b"experimental", b"sparse-read.density-threshold")? | |||
|
381 | { | |||
|
382 | data_config.sr_density_threshold = sr_density_threshold; | |||
|
383 | } | |||
|
384 | data_config.with_sparse_read = with_sparse_read; | |||
|
385 | if let Some(sr_min_gap_size) = config | |||
|
386 | .get_byte_size(b"experimental", b"sparse-read.min-gap-size")? | |||
|
387 | { | |||
|
388 | data_config.sr_min_gap_size = sr_min_gap_size; | |||
|
389 | } | |||
|
390 | ||||
|
391 | data_config.with_sparse_read = | |||
|
392 | requirements.contains(SPARSEREVLOG_REQUIREMENT); | |||
|
393 | ||||
|
394 | Ok(data_config) | |||
|
395 | } | |||
|
396 | } | |||
|
397 | ||||
|
398 | impl Default for RevlogDataConfig { | |||
|
399 | fn default() -> Self { | |||
|
400 | Self { | |||
|
401 | chunk_cache_size: 65536, | |||
|
402 | sr_density_threshold: 0.50, | |||
|
403 | sr_min_gap_size: 262144, | |||
|
404 | try_pending: Default::default(), | |||
|
405 | try_split: Default::default(), | |||
|
406 | check_ambig: Default::default(), | |||
|
407 | mmap_large_index: Default::default(), | |||
|
408 | mmap_index_threshold: Default::default(), | |||
|
409 | uncompressed_cache_factor: Default::default(), | |||
|
410 | uncompressed_cache_count: Default::default(), | |||
|
411 | with_sparse_read: Default::default(), | |||
|
412 | general_delta: Default::default(), | |||
|
413 | } | |||
|
414 | } | |||
|
415 | } | |||
|
416 | ||||
|
417 | #[derive(Debug, Clone, Copy, PartialEq)] | |||
|
418 | /// Holds configuration values about how new deltas are computed. | |||
|
419 | /// | |||
|
420 | /// Some attributes are duplicated from [`RevlogDataConfig`] to help having | |||
|
421 | /// each object self contained. | |||
|
422 | pub struct RevlogDeltaConfig { | |||
|
423 | /// Whether deltas can be encoded against arbitrary bases | |||
|
424 | pub general_delta: bool, | |||
|
425 | /// Allow sparse writing of the revlog data | |||
|
426 | pub sparse_revlog: bool, | |||
|
427 | /// Maximum length of a delta chain | |||
|
428 | pub max_chain_len: Option<u64>, | |||
|
429 | /// Maximum distance between a delta chain's start and end | |||
|
430 | pub max_deltachain_span: Option<u64>, | |||
|
431 | /// If `upper_bound_comp` is not None, this is the expected maximal | |||
|
432 | /// gain from compression for the data content | |||
|
433 | pub upper_bound_comp: Option<f64>, | |||
|
434 | /// Should we try a delta against both parents | |||
|
435 | pub delta_both_parents: bool, | |||
|
436 | /// Test delta base candidate groups by chunks of this maximal size | |||
|
437 | pub candidate_group_chunk_size: u64, | |||
|
438 | /// Should we display debug information about delta computation | |||
|
439 | pub debug_delta: bool, | |||
|
440 | /// Trust incoming deltas by default | |||
|
441 | pub lazy_delta: bool, | |||
|
442 | /// Trust the base of incoming deltas by default | |||
|
443 | pub lazy_delta_base: bool, | |||
|
444 | } | |||
|
445 | impl RevlogDeltaConfig { | |||
|
446 | pub fn new( | |||
|
447 | config: &Config, | |||
|
448 | requirements: &HashSet<String>, | |||
|
449 | revlog_type: RevlogType, | |||
|
450 | ) -> Result<Self, HgError> { | |||
|
451 | let mut delta_config = Self { | |||
|
452 | delta_both_parents: config | |||
|
453 | .get_option_no_default( | |||
|
454 | b"storage", | |||
|
455 | b"revlog.optimize-delta-parent-choice", | |||
|
456 | )? | |||
|
457 | .unwrap_or(true), | |||
|
458 | candidate_group_chunk_size: config | |||
|
459 | .get_u64( | |||
|
460 | b"storage", | |||
|
461 | b"revlog.delta-parent-search.candidate-group-chunk-size", | |||
|
462 | )? | |||
|
463 | .unwrap_or_default(), | |||
|
464 | ..Default::default() | |||
|
465 | }; | |||
|
466 | ||||
|
467 | delta_config.debug_delta = | |||
|
468 | config.get_bool(b"debug", b"revlog.debug-delta")?; | |||
|
469 | ||||
|
470 | delta_config.general_delta = | |||
|
471 | requirements.contains(GENERALDELTA_REQUIREMENT); | |||
|
472 | ||||
|
473 | let lazy_delta = | |||
|
474 | config.get_bool(b"storage", b"revlog.reuse-external-delta")?; | |||
|
475 | ||||
|
476 | if revlog_type == RevlogType::Manifestlog { | |||
|
477 | // upper bound of what we expect from compression | |||
|
478 | // (real life value seems to be 3) | |||
|
479 | delta_config.upper_bound_comp = Some(3.0) | |||
|
480 | } | |||
|
481 | ||||
|
482 | let mut lazy_delta_base = false; | |||
|
483 | if lazy_delta { | |||
|
484 | lazy_delta_base = match config.get_option_no_default( | |||
|
485 | b"storage", | |||
|
486 | b"revlog.reuse-external-delta-parent", | |||
|
487 | )? { | |||
|
488 | Some(base) => base, | |||
|
489 | None => config.get_bool(b"format", b"generaldelta")?, | |||
|
490 | }; | |||
|
491 | } | |||
|
492 | delta_config.lazy_delta = lazy_delta; | |||
|
493 | delta_config.lazy_delta_base = lazy_delta_base; | |||
|
494 | ||||
|
495 | delta_config.max_deltachain_span = | |||
|
496 | match config.get_i64(b"experimental", b"maxdeltachainspan")? { | |||
|
497 | Some(span) => { | |||
|
498 | if span < 0 { | |||
|
499 | None | |||
|
500 | } else { | |||
|
501 | Some(span as u64) | |||
|
502 | } | |||
|
503 | } | |||
|
504 | None => None, | |||
|
505 | }; | |||
|
506 | ||||
|
507 | delta_config.sparse_revlog = | |||
|
508 | requirements.contains(SPARSEREVLOG_REQUIREMENT); | |||
|
509 | ||||
|
510 | delta_config.max_chain_len = | |||
|
511 | config.get_byte_size_no_default(b"format", b"maxchainlen")?; | |||
|
512 | ||||
|
513 | Ok(delta_config) | |||
|
514 | } | |||
|
515 | } | |||
|
516 | ||||
|
517 | impl Default for RevlogDeltaConfig { | |||
|
518 | fn default() -> Self { | |||
|
519 | Self { | |||
|
520 | delta_both_parents: true, | |||
|
521 | lazy_delta: true, | |||
|
522 | general_delta: Default::default(), | |||
|
523 | sparse_revlog: Default::default(), | |||
|
524 | max_chain_len: Default::default(), | |||
|
525 | max_deltachain_span: Default::default(), | |||
|
526 | upper_bound_comp: Default::default(), | |||
|
527 | candidate_group_chunk_size: Default::default(), | |||
|
528 | debug_delta: Default::default(), | |||
|
529 | lazy_delta_base: Default::default(), | |||
|
530 | } | |||
|
531 | } | |||
|
532 | } | |||
|
533 | ||||
|
534 | #[derive(Debug, Default, Clone, Copy, PartialEq)] | |||
|
535 | /// Holds configuration values about the available revlog features | |||
|
536 | pub struct RevlogFeatureConfig { | |||
|
537 | /// The compression engine and its options | |||
|
538 | pub compression_engine: CompressionEngine, | |||
|
539 | /// Can we use censor on this revlog | |||
|
540 | pub censorable: bool, | |||
|
541 | /// Does this revlog use the "side data" feature | |||
|
542 | pub has_side_data: bool, | |||
|
543 | /// Might remove this configuration once the rank computation has no | |||
|
544 | /// impact | |||
|
545 | pub compute_rank: bool, | |||
|
546 | /// Parent order is supposed to be semantically irrelevant, so we | |||
|
547 | /// normally re-sort parents to ensure that the first parent is non-null, | |||
|
548 | /// if there is a non-null parent at all. | |||
|
549 | /// filelog abuses the parent order as a flag to mark some instances of | |||
|
550 | /// meta-encoded files, so allow it to disable this behavior. | |||
|
551 | pub canonical_parent_order: bool, | |||
|
552 | /// Can ellipsis commit be used | |||
|
553 | pub enable_ellipsis: bool, | |||
|
554 | } | |||
|
555 | impl RevlogFeatureConfig { | |||
|
556 | pub fn new( | |||
|
557 | config: &Config, | |||
|
558 | requirements: &HashSet<String>, | |||
|
559 | ) -> Result<Self, HgError> { | |||
|
560 | let mut feature_config = Self::default(); | |||
|
561 | ||||
|
562 | let zlib_level = config.get_u32(b"storage", b"revlog.zlib.level")?; | |||
|
563 | let zstd_level = config.get_u32(b"storage", b"revlog.zstd.level")?; | |||
|
564 | ||||
|
565 | feature_config.compression_engine = CompressionEngine::default(); | |||
|
566 | ||||
|
567 | for requirement in requirements { | |||
|
568 | if requirement.starts_with("revlog-compression-") | |||
|
569 | || requirement.starts_with("exp-compression-") | |||
|
570 | { | |||
|
571 | let split = &mut requirement.splitn(3, '-'); | |||
|
572 | split.next(); | |||
|
573 | split.next(); | |||
|
574 | feature_config.compression_engine = match split.next().unwrap() | |||
|
575 | { | |||
|
576 | "zstd" => CompressionEngine::zstd(zstd_level)?, | |||
|
577 | e => { | |||
|
578 | return Err(HgError::UnsupportedFeature(format!( | |||
|
579 | "Unsupported compression engine '{e}'" | |||
|
580 | ))) | |||
|
581 | } | |||
|
582 | }; | |||
|
583 | } | |||
|
584 | } | |||
|
585 | if let Some(level) = zlib_level { | |||
|
586 | if matches!( | |||
|
587 | feature_config.compression_engine, | |||
|
588 | CompressionEngine::Zlib { .. } | |||
|
589 | ) { | |||
|
590 | feature_config | |||
|
591 | .compression_engine | |||
|
592 | .set_level(level as usize)?; | |||
|
593 | } | |||
|
594 | } | |||
|
595 | ||||
|
596 | feature_config.enable_ellipsis = | |||
|
597 | requirements.contains(NARROW_REQUIREMENT); | |||
|
598 | ||||
|
599 | Ok(feature_config) | |||
|
600 | } | |||
|
601 | } | |||
|
602 | ||||
220 | /// Read only implementation of revlog. |
|
603 | /// Read only implementation of revlog. | |
221 | pub struct Revlog { |
|
604 | pub struct Revlog { | |
222 | /// When index and data are not interleaved: bytes of the revlog index. |
|
605 | /// When index and data are not interleaved: bytes of the revlog index. |
General Comments 0
You need to be logged in to leave comments.
Login now