##// END OF EJS Templates
rust-revlog: add revlog-specific config objects...
Raphaël Gomès -
r52758:06046734 default
parent child Browse files
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