changeset 45526:26c53ee51c68

hg-core: Add a limited read only `revlog` implementation Only covers the needs of the upcoming `rhg debugdata` command. Differential Revision: https://phab.mercurial-scm.org/D8958
author Antoine Cezar <antoine.cezar@octobus.net>
date Fri, 04 Sep 2020 11:55:07 +0200
parents 590a840fa367
children b56df13a0450
files rust/Cargo.lock rust/hg-core/Cargo.toml rust/hg-core/src/revlog.rs rust/hg-core/src/revlog/index.rs rust/hg-core/src/revlog/patch.rs rust/hg-core/src/revlog/revlog.rs
diffstat 6 files changed, 1283 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/rust/Cargo.lock	Wed Sep 16 18:09:32 2020 +0530
+++ b/rust/Cargo.lock	Fri Sep 04 11:55:07 2020 +0200
@@ -1,8 +1,13 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
 [[package]]
+name = "adler"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "aho-corasick"
-version = "0.7.10"
+version = "0.7.13"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -13,7 +18,7 @@
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -21,14 +26,14 @@
 version = "0.2.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hermit-abi 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "autocfg"
-version = "1.0.0"
+version = "1.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -42,13 +47,21 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "cc"
+version = "1.0.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "cfg-if"
 version = "0.1.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "clap"
-version = "2.33.1"
+version = "2.33.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -56,8 +69,8 @@
  "bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -65,28 +78,36 @@
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
- "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)",
  "python27-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "python3-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "crc32fast"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "crossbeam"
 version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-channel 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crossbeam-queue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crossbeam-channel"
-version = "0.4.2"
+version = "0.4.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -108,22 +129,23 @@
 version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "memoffset 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "crossbeam-queue"
-version = "0.2.1"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -131,18 +153,18 @@
 version = "0.7.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "ctor"
-version = "0.1.13"
+version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -152,7 +174,7 @@
 
 [[package]]
 name = "either"
-version = "1.5.3"
+version = "1.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -162,27 +184,44 @@
 dependencies = [
  "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
  "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
-name = "getrandom"
-version = "0.1.14"
+name = "flate2"
+version = "1.0.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
+ "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libz-sys 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "miniz_oxide 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "getrandom"
+version = "0.1.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
  "wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "glob"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "hermit-abi"
-version = "0.1.8"
+version = "0.1.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -195,23 +234,25 @@
 version = "0.1.0"
 dependencies = [
  "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
- "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "flate2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "micro-timer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "micro-timer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand_distr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "zstd 0.5.3+zstd.1.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -221,8 +262,8 @@
  "cpython 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "hg-core 0.1.0",
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
- "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -234,18 +275,44 @@
 ]
 
 [[package]]
+name = "itertools"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "jobserver"
+version = "0.1.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "lazy_static"
 version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "libc"
-version = "0.2.67"
+version = "0.2.77"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
+name = "libz-sys"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)",
+ "pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)",
+ "vcpkg 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "log"
-version = "0.4.8"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -266,53 +333,62 @@
 version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "memoffset"
-version = "0.5.3"
+version = "0.5.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "micro-timer"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "micro-timer-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "micro-timer-macros 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "micro-timer-macros"
-version = "0.3.0"
+version = "0.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "syn 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "miniz_oxide"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "num-traits"
-version = "0.2.11"
+version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "num_cpus"
-version = "1.12.0"
+version = "1.13.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
+ "hermit-abi 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -320,12 +396,17 @@
 version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
+name = "pkg-config"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
 name = "ppv-lite86"
-version = "0.2.6"
+version = "0.2.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -334,17 +415,17 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "ctor 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ctor 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
  "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.9"
+version = "1.0.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -352,7 +433,7 @@
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -361,7 +442,7 @@
 version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -372,10 +453,10 @@
 
 [[package]]
 name = "quote"
-version = "1.0.3"
+version = "1.0.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -383,8 +464,8 @@
 version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
+ "getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -395,7 +476,7 @@
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
+ "ppv-lite86 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -404,7 +485,7 @@
 version = "0.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
+ "getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -433,29 +514,30 @@
 
 [[package]]
 name = "rayon"
-version = "1.3.0"
+version = "1.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
+ "autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "rayon-core 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rayon-core"
-version = "1.7.0"
+version = "1.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
+ "crossbeam-channel 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "redox_syscall"
-version = "0.1.56"
+version = "0.1.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -463,7 +545,7 @@
 version = "1.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "aho-corasick 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
  "memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)",
  "thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -476,34 +558,26 @@
 
 [[package]]
 name = "remove_dir_all"
-version = "0.5.2"
+version = "0.5.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "rhg"
 version = "0.1.0"
 dependencies = [
- "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "hg-core 0.1.0",
 ]
 
 [[package]]
-name = "rustc_version"
-version = "0.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
 name = "same-file"
 version = "1.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -512,31 +586,18 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
-name = "semver"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-dependencies = [
- "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
-]
-
-[[package]]
-name = "semver-parser"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-
-[[package]]
 name = "strsim"
 version = "0.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "syn"
-version = "1.0.16"
+version = "1.0.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
- "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "proc-macro2 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)",
+ "quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -545,11 +606,11 @@
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
  "rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
- "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)",
+ "remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -557,7 +618,7 @@
 version = "1.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -565,7 +626,7 @@
 version = "0.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
+ "unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -586,17 +647,22 @@
 
 [[package]]
 name = "unicode-width"
-version = "0.1.7"
+version = "0.1.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "unicode-xid"
-version = "0.2.0"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "vcpkg"
+version = "0.2.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
 name = "vec_map"
-version = "0.8.1"
+version = "0.8.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
 [[package]]
@@ -606,7 +672,7 @@
 
 [[package]]
 name = "winapi"
-version = "0.3.8"
+version = "0.3.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -620,10 +686,10 @@
 
 [[package]]
 name = "winapi-util"
-version = "0.1.3"
+version = "0.1.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
@@ -631,78 +697,117 @@
 version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 
+[[package]]
+name = "zstd"
+version = "0.5.3+zstd.1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "zstd-safe 2.0.5+zstd.1.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "zstd-safe"
+version = "2.0.5+zstd.1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
+ "zstd-sys 1.4.17+zstd.1.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
+name = "zstd-sys"
+version = "1.4.17+zstd.1.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cc 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)",
+ "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [metadata]
-"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
+"checksum adler 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2a4ec343196209d6594e19543ae87a39f96d5534d7174822a3ad825dd6ed7e"
+"checksum aho-corasick 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)" = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86"
 "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
 "checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
-"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
+"checksum autocfg 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
 "checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
 "checksum byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de"
+"checksum cc 1.0.60 (registry+https://github.com/rust-lang/crates.io-index)" = "ef611cc68ff783f18535d77ddd080185275713d852c4f5cbb6122c462a7a825c"
 "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
-"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
+"checksum clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)" = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
 "checksum cpython 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bfaf3847ab963e40c4f6dd8d6be279bdf74007ae2413786a0dcbb28c52139a95"
+"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
 "checksum crossbeam 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "69323bff1fb41c635347b8ead484a5ca6c3f11914d784170b158d8449ab07f8e"
-"checksum crossbeam-channel 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cced8691919c02aac3cb0a1bc2e9b73d89e832bf9a06fc579d4e71b68a2da061"
+"checksum crossbeam-channel 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b153fe7cbef478c567df0f972e02e6d736db11affe43dfc9c56a9374d1adfb87"
 "checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285"
 "checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
-"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
+"checksum crossbeam-queue 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
 "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
-"checksum ctor 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "47c5e5ac752e18207b12e16b10631ae5f7f68f8805f335f9b817ead83d9ffce1"
+"checksum ctor 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "7fbaabec2c953050352311293be5c6aba8e141ba19d6811862b232d6fd020484"
 "checksum difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
-"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
+"checksum either 1.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
 "checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
-"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
-"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
+"checksum flate2 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)" = "766d0e77a2c1502169d4a93ff3b8c15a71fd946cd0126309752104e5f3c46d94"
+"checksum getrandom 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6"
+"checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
+"checksum hermit-abi 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c30f6d0bc6b00693347368a67d41b58f2fb851215ff1da49e90fe2c5c667151"
 "checksum hex 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35"
 "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f"
+"checksum itertools 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
+"checksum jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2"
 "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
-"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
+"checksum libc 0.2.77 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f96b10ec2560088a8e76961b00d47107b3a625fecb76dedb29ee7ccbf98235"
+"checksum libz-sys 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "602113192b08db8f38796c4e85c39e960c145965140e918018bcde1952429655"
+"checksum log 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
 "checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
 "checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
 "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
-"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9"
-"checksum micro-timer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25b31d6cb9112984323d05d7a353f272ae5d7a307074f9ab9b25c00121b8c947"
-"checksum micro-timer-macros 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5694085dd384bb9e824207facc040c248d9df653f55e28c3ad0686958b448504"
-"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
-"checksum num_cpus 1.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46203554f085ff89c235cd12f7075f3233af9b11ed7c9e16dfe2560d03313ce6"
+"checksum memoffset 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "043175f069eda7b85febe4a74abbaeff828d9f8b448515d3151a14a3542811aa"
+"checksum micro-timer 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2620153e1d903d26b72b89f0e9c48d8c4756cba941c185461dddc234980c298c"
+"checksum micro-timer-macros 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e28a3473e6abd6e9aab36aaeef32ad22ae0bd34e79f376643594c2b152ec1c5d"
+"checksum miniz_oxide 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c60c0dfe32c10b43a144bad8fc83538c52f58302c92300ea7ec7bf7b38d5a7b9"
+"checksum num-traits 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
+"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
 "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
-"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
+"checksum pkg-config 0.3.18 (registry+https://github.com/rust-lang/crates.io-index)" = "d36492546b6af1463394d46f0c834346f31548646f6ba10849802c9c9a27ac33"
+"checksum ppv-lite86 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20"
 "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427"
-"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
+"checksum proc-macro2 1.0.21 (registry+https://github.com/rust-lang/crates.io-index)" = "36e28516df94f3dd551a587da5357459d9b36d945a7c37c3557928c1c2ff2a2c"
 "checksum python27-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "67cb041de8615111bf224dd75667af5f25c6e032118251426fed7f1b70ce4c8c"
 "checksum python3-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90af11779515a1e530af60782d273b59ac79d33b0e253c071a728563957c76d4"
 "checksum quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
-"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
+"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
 "checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
 "checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
 "checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
 "checksum rand_distr 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2"
 "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
 "checksum rand_pcg 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
-"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098"
-"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9"
-"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
+"checksum rayon 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cfd016f0c045ad38b5251be2c9c0ab806917f82da4d36b2a327e5166adad9270"
+"checksum rayon-core 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e8c4fec834fb6e6d2dd5eece3c7b432a52f0ba887cf40e595190c4107edc08bf"
+"checksum redox_syscall 0.1.57 (registry+https://github.com/rust-lang/crates.io-index)" = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
 "checksum regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
 "checksum regex-syntax 0.6.18 (registry+https://github.com/rust-lang/crates.io-index)" = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
-"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
-"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
+"checksum remove_dir_all 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
 "checksum same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
 "checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
-"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
 "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
-"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
+"checksum syn 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "6690e3e9f692504b941dc6c3b188fd28df054f7fb8469ab40680df52fdcc842b"
 "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
 "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f"
 "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
 "checksum thread_local 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14"
 "checksum twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bfd5b7557925ce778ff9b9ef90e3ade34c524b5ff10e239c69a42d546d2af56"
-"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
-"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
-"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
+"checksum unicode-width 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
+"checksum vcpkg 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c"
+"checksum vec_map 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
 "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
-"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
+"checksum winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
 "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
-"checksum winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4ccfbf554c6ad11084fb7517daca16cfdcaccbdadba4fc336f032a8b12c2ad80"
+"checksum winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
 "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
+"checksum zstd 0.5.3+zstd.1.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "01b32eaf771efa709e8308605bbf9319bf485dc1503179ec0469b611937c0cd8"
+"checksum zstd-safe 2.0.5+zstd.1.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cfb642e0d27f64729a639c52db457e0ae906e7bc6f5fe8f5c453230400f1055"
+"checksum zstd-sys 1.4.17+zstd.1.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b89249644df056b522696b1bb9e7c18c87e8ffa3e2f0dc3b0155875d6498f01b"
--- a/rust/hg-core/Cargo.toml	Wed Sep 16 18:09:32 2020 +0530
+++ b/rust/hg-core/Cargo.toml	Fri Sep 04 11:55:07 2020 +0200
@@ -23,9 +23,19 @@
 crossbeam = "0.7.3"
 micro-timer = "0.3.0"
 log = "0.4.8"
+memmap = "0.7.0"
+zstd = "0.5.3"
+
+# We don't use the `miniz-oxide` backend because its minimum Rust version is
+# `1.36`. However, this PR (https://github.com/Frommi/miniz_oxide/pull/84/files)
+# introduces a flag `no_extern_crate_alloc` to bring the requirement back down
+# to `1.34`.
+[dependencies.flate2]
+version = "1.0.16"
+features = ["zlib"]
+default-features = false
 
 [dev-dependencies]
 clap = "*"
-memmap = "0.7.0"
 pretty_assertions = "0.6.1"
 tempfile = "3.1.0"
--- a/rust/hg-core/src/revlog.rs	Wed Sep 16 18:09:32 2020 +0530
+++ b/rust/hg-core/src/revlog.rs	Fri Sep 04 11:55:07 2020 +0200
@@ -8,6 +8,9 @@
 pub mod node;
 pub mod nodemap;
 pub use node::{Node, NodeError, NodePrefix, NodePrefixRef};
+pub mod index;
+pub mod patch;
+pub mod revlog;
 
 /// Mercurial revision numbers
 ///
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/revlog/index.rs	Fri Sep 04 11:55:07 2020 +0200
@@ -0,0 +1,299 @@
+use crate::revlog::{Revision, NULL_REVISION};
+use byteorder::{BigEndian, ByteOrder};
+
+pub const INDEX_ENTRY_SIZE: usize = 64;
+
+/// A Revlog index
+#[derive(Debug)]
+pub struct Index<'a> {
+    bytes: &'a [u8],
+    /// Offsets of starts of index blocks.
+    /// Only needed when the index is interleaved with data.
+    offsets: Option<Vec<usize>>,
+}
+
+impl<'a> Index<'a> {
+    /// Create an index from bytes.
+    /// Calculate the start of each entry when is_inline is true.
+    pub fn new(bytes: &'a [u8], is_inline: bool) -> Self {
+        if is_inline {
+            let mut offset: usize = 0;
+            let mut offsets = Vec::new();
+
+            while (bytes.len() - offset) >= INDEX_ENTRY_SIZE {
+                offsets.push(offset);
+                let end = offset + INDEX_ENTRY_SIZE;
+                let entry = IndexEntry {
+                    bytes: &bytes[offset..end],
+                    offset_override: None,
+                };
+
+                offset += INDEX_ENTRY_SIZE + entry.compressed_len();
+            }
+
+            Self {
+                bytes,
+                offsets: Some(offsets),
+            }
+        } else {
+            Self {
+                bytes,
+                offsets: None,
+            }
+        }
+    }
+
+    /// Return the index entry corresponding to the given revision if it
+    /// exists.
+    pub fn get_entry(&self, rev: Revision) -> Option<IndexEntry> {
+        if rev == NULL_REVISION {
+            return None;
+        }
+        if let Some(offsets) = &self.offsets {
+            self.get_entry_inline(rev, offsets)
+        } else {
+            self.get_entry_separated(rev)
+        }
+    }
+
+    fn get_entry_inline(
+        &self,
+        rev: Revision,
+        offsets: &[usize],
+    ) -> Option<IndexEntry> {
+        let start = *offsets.get(rev as usize)?;
+        let end = start.checked_add(INDEX_ENTRY_SIZE)?;
+        let bytes = &self.bytes[start..end];
+
+        // See IndexEntry for an explanation of this override.
+        let offset_override = Some(end);
+
+        Some(IndexEntry {
+            bytes,
+            offset_override,
+        })
+    }
+
+    fn get_entry_separated(&self, rev: Revision) -> Option<IndexEntry> {
+        let max_rev = self.bytes.len() / INDEX_ENTRY_SIZE;
+        if rev as usize >= max_rev {
+            return None;
+        }
+        let start = rev as usize * INDEX_ENTRY_SIZE;
+        let end = start + INDEX_ENTRY_SIZE;
+        let bytes = &self.bytes[start..end];
+
+        // See IndexEntry for an explanation of this override.
+        let offset_override = match rev {
+            0 => Some(0),
+            _ => None,
+        };
+
+        Some(IndexEntry {
+            bytes,
+            offset_override,
+        })
+    }
+}
+
+#[derive(Debug)]
+pub struct IndexEntry<'a> {
+    bytes: &'a [u8],
+    /// Allows to override the offset value of the entry.
+    ///
+    /// For interleaved index and data, the offset stored in the index
+    /// corresponds to the separated data offset.
+    /// It has to be overridden with the actual offset in the interleaved
+    /// index which is just after the index block.
+    ///
+    /// For separated index and data, the offset stored in the first index
+    /// entry is mixed with the index headers.
+    /// It has to be overridden with 0.
+    offset_override: Option<usize>,
+}
+
+impl<'a> IndexEntry<'a> {
+    /// Return the offset of the data if not overridden by offset_override.
+    pub fn offset(&self) -> usize {
+        if let Some(offset_override) = self.offset_override {
+            offset_override
+        } else {
+            let mut bytes = [0; 8];
+            bytes[2..8].copy_from_slice(&self.bytes[0..=5]);
+            BigEndian::read_u64(&bytes[..]) as usize
+        }
+    }
+
+    /// Return the compressed length of the data.
+    pub fn compressed_len(&self) -> usize {
+        BigEndian::read_u32(&self.bytes[8..=11]) as usize
+    }
+
+    /// Return the uncompressed length of the data.
+    pub fn uncompressed_len(&self) -> usize {
+        BigEndian::read_u32(&self.bytes[12..=15]) as usize
+    }
+
+    /// Return the revision upon which the data has been derived.
+    pub fn base_revision(&self) -> Revision {
+        // TODO Maybe return an Option when base_revision == rev?
+        //      Requires to add rev to IndexEntry
+
+        BigEndian::read_i32(&self.bytes[16..])
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[cfg(test)]
+    #[derive(Debug, Copy, Clone)]
+    pub struct IndexEntryBuilder {
+        is_first: bool,
+        is_inline: bool,
+        is_general_delta: bool,
+        version: u16,
+        offset: usize,
+        compressed_len: usize,
+        uncompressed_len: usize,
+        base_revision: Revision,
+    }
+
+    #[cfg(test)]
+    impl IndexEntryBuilder {
+        pub fn new() -> Self {
+            Self {
+                is_first: false,
+                is_inline: false,
+                is_general_delta: true,
+                version: 2,
+                offset: 0,
+                compressed_len: 0,
+                uncompressed_len: 0,
+                base_revision: 0,
+            }
+        }
+
+        pub fn is_first(&mut self, value: bool) -> &mut Self {
+            self.is_first = value;
+            self
+        }
+
+        pub fn with_inline(&mut self, value: bool) -> &mut Self {
+            self.is_inline = value;
+            self
+        }
+
+        pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
+            self.is_general_delta = value;
+            self
+        }
+
+        pub fn with_version(&mut self, value: u16) -> &mut Self {
+            self.version = value;
+            self
+        }
+
+        pub fn with_offset(&mut self, value: usize) -> &mut Self {
+            self.offset = value;
+            self
+        }
+
+        pub fn with_compressed_len(&mut self, value: usize) -> &mut Self {
+            self.compressed_len = value;
+            self
+        }
+
+        pub fn with_uncompressed_len(&mut self, value: usize) -> &mut Self {
+            self.uncompressed_len = value;
+            self
+        }
+
+        pub fn with_base_revision(&mut self, value: Revision) -> &mut Self {
+            self.base_revision = value;
+            self
+        }
+
+        pub fn build(&self) -> Vec<u8> {
+            let mut bytes = Vec::with_capacity(INDEX_ENTRY_SIZE);
+            if self.is_first {
+                bytes.extend(&match (self.is_general_delta, self.is_inline) {
+                    (false, false) => [0u8, 0],
+                    (false, true) => [0u8, 1],
+                    (true, false) => [0u8, 2],
+                    (true, true) => [0u8, 3],
+                });
+                bytes.extend(&self.version.to_be_bytes());
+                // Remaining offset bytes.
+                bytes.extend(&[0u8; 2]);
+            } else {
+                // Offset is only 6 bytes will usize is 8.
+                bytes.extend(&self.offset.to_be_bytes()[2..]);
+            }
+            bytes.extend(&[0u8; 2]); // Revision flags.
+            bytes.extend(&self.compressed_len.to_be_bytes()[4..]);
+            bytes.extend(&self.uncompressed_len.to_be_bytes()[4..]);
+            bytes.extend(&self.base_revision.to_be_bytes());
+            bytes
+        }
+    }
+
+    #[test]
+    fn test_offset() {
+        let bytes = IndexEntryBuilder::new().with_offset(1).build();
+        let entry = IndexEntry {
+            bytes: &bytes,
+            offset_override: None,
+        };
+
+        assert_eq!(entry.offset(), 1)
+    }
+
+    #[test]
+    fn test_with_overridden_offset() {
+        let bytes = IndexEntryBuilder::new().with_offset(1).build();
+        let entry = IndexEntry {
+            bytes: &bytes,
+            offset_override: Some(2),
+        };
+
+        assert_eq!(entry.offset(), 2)
+    }
+
+    #[test]
+    fn test_compressed_len() {
+        let bytes = IndexEntryBuilder::new().with_compressed_len(1).build();
+        let entry = IndexEntry {
+            bytes: &bytes,
+            offset_override: None,
+        };
+
+        assert_eq!(entry.compressed_len(), 1)
+    }
+
+    #[test]
+    fn test_uncompressed_len() {
+        let bytes = IndexEntryBuilder::new().with_uncompressed_len(1).build();
+        let entry = IndexEntry {
+            bytes: &bytes,
+            offset_override: None,
+        };
+
+        assert_eq!(entry.uncompressed_len(), 1)
+    }
+
+    #[test]
+    fn test_base_revision() {
+        let bytes = IndexEntryBuilder::new().with_base_revision(1).build();
+        let entry = IndexEntry {
+            bytes: &bytes,
+            offset_override: None,
+        };
+
+        assert_eq!(entry.base_revision(), 1)
+    }
+}
+
+#[cfg(test)]
+pub use tests::IndexEntryBuilder;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/revlog/patch.rs	Fri Sep 04 11:55:07 2020 +0200
@@ -0,0 +1,367 @@
+use byteorder::{BigEndian, ByteOrder};
+
+/// A chunk of data to insert, delete or replace in a patch
+///
+/// A chunk is:
+/// - an insertion when `!data.is_empty() && start == end`
+/// - an deletion when `data.is_empty() && start < end`
+/// - a replacement when `!data.is_empty() && start < end`
+/// - not doing anything when `data.is_empty() && start == end`
+#[derive(Debug, Clone)]
+struct PatchFrag<'a> {
+    /// The start position of the chunk of data to replace
+    start: i32,
+    /// The end position of the chunk of data to replace (open end interval)
+    end: i32,
+    /// The data replacing the chunk
+    data: &'a [u8],
+}
+
+impl<'a> PatchFrag<'a> {
+    /// Adjusted start of the chunk to replace.
+    ///
+    /// Offset allow to take into account the growth/shrinkage of data
+    /// induced by previously applied chunks.
+    fn start_offseted_by(&self, offset: i32) -> i32 {
+        self.start + offset
+    }
+
+    /// Adjusted end of the chunk to replace.
+    ///
+    /// Offset allow to take into account the growth/shrinkage of data
+    /// induced by previously applied chunks.
+    fn end_offseted_by(&self, offset: i32) -> i32 {
+        self.start_offseted_by(offset) + (self.data.len() as i32)
+    }
+
+    /// Length of the replaced chunk.
+    fn replaced_len(&self) -> i32 {
+        self.end - self.start
+    }
+
+    /// Length difference between the replacing data and the replaced data.
+    fn len_diff(&self) -> i32 {
+        (self.data.len() as i32) - self.replaced_len()
+    }
+}
+
+/// The delta between two revisions data.
+#[derive(Debug, Clone)]
+pub struct PatchList<'a> {
+    /// A collection of chunks to apply.
+    ///
+    /// Those chunks are:
+    /// - ordered from the left-most replacement to the right-most replacement
+    /// - non-overlapping, meaning that two chucks can not change the same
+    ///   chunk of the patched data
+    frags: Vec<PatchFrag<'a>>,
+}
+
+impl<'a> PatchList<'a> {
+    /// Create a `PatchList` from bytes.
+    pub fn new(data: &'a [u8]) -> Self {
+        let mut frags = vec![];
+        let mut data = data;
+        while !data.is_empty() {
+            let start = BigEndian::read_i32(&data[0..]);
+            let end = BigEndian::read_i32(&data[4..]);
+            let len = BigEndian::read_i32(&data[8..]);
+            assert!(0 <= start && start <= end && len >= 0);
+            frags.push(PatchFrag {
+                start,
+                end,
+                data: &data[12..12 + (len as usize)],
+            });
+            data = &data[12 + (len as usize)..];
+        }
+        PatchList { frags }
+    }
+
+    /// Return the final length of data after patching
+    /// given its initial length .
+    fn size(&self, initial_size: i32) -> i32 {
+        self.frags
+            .iter()
+            .fold(initial_size, |acc, frag| acc + frag.len_diff())
+    }
+
+    /// Apply the patch to some data.
+    pub fn apply(&self, initial: &[u8]) -> Vec<u8> {
+        let mut last: usize = 0;
+        let mut vec =
+            Vec::with_capacity(self.size(initial.len() as i32) as usize);
+        for PatchFrag { start, end, data } in self.frags.iter() {
+            vec.extend(&initial[last..(*start as usize)]);
+            vec.extend(data.iter());
+            last = *end as usize;
+        }
+        vec.extend(&initial[last..]);
+        vec
+    }
+
+    /// Combine two patch lists into a single patch list.
+    ///
+    /// Applying consecutive patches can lead to waste of time and memory
+    /// as the changes introduced by one patch can be overridden by the next.
+    /// Combining patches optimizes the whole patching sequence.
+    fn combine(&mut self, other: &mut Self) -> Self {
+        let mut frags = vec![];
+
+        // Keep track of each growth/shrinkage resulting from applying a chunk
+        // in order to adjust the start/end of subsequent chunks.
+        let mut offset = 0i32;
+
+        // Keep track of the chunk of self.chunks to process.
+        let mut pos = 0;
+
+        // For each chunk of `other`, chunks of `self` are processed
+        // until they start after the end of the current chunk.
+        for PatchFrag { start, end, data } in other.frags.iter() {
+            // Add chunks of `self` that start before this chunk of `other`
+            // without overlap.
+            while pos < self.frags.len()
+                && self.frags[pos].end_offseted_by(offset) <= *start
+            {
+                let first = self.frags[pos].clone();
+                offset += first.len_diff();
+                frags.push(first);
+                pos += 1;
+            }
+
+            // The current chunk of `self` starts before this chunk of `other`
+            // with overlap.
+            // The left-most part of data is added as an insertion chunk.
+            // The right-most part data is kept in the chunk.
+            if pos < self.frags.len()
+                && self.frags[pos].start_offseted_by(offset) < *start
+            {
+                let first = &mut self.frags[pos];
+
+                let (data_left, data_right) = first.data.split_at(
+                    (*start - first.start_offseted_by(offset)) as usize,
+                );
+                let left = PatchFrag {
+                    start: first.start,
+                    end: first.start,
+                    data: data_left,
+                };
+
+                first.data = data_right;
+
+                offset += left.len_diff();
+
+                frags.push(left);
+
+                // There is no index incrementation because the right-most part
+                // needs further examination.
+            }
+
+            // At this point remaining chunks of `self` starts after
+            // the current chunk of `other`.
+
+            // `start_offset` will be used to adjust the start of the current
+            // chunk of `other`.
+            // Offset tracking continues with `end_offset` to adjust the end
+            // of the current chunk of `other`.
+            let mut next_offset = offset;
+
+            // Discard the chunks of `self` that are totally overridden
+            // by the current chunk of `other`
+            while pos < self.frags.len()
+                && self.frags[pos].end_offseted_by(next_offset) <= *end
+            {
+                let first = &self.frags[pos];
+                next_offset += first.len_diff();
+                pos += 1;
+            }
+
+            // Truncate the left-most part of chunk of `self` that overlaps
+            // the current chunk of `other`.
+            if pos < self.frags.len()
+                && self.frags[pos].start_offseted_by(next_offset) < *end
+            {
+                let first = &mut self.frags[pos];
+
+                let how_much_to_discard =
+                    *end - first.start_offseted_by(next_offset);
+
+                first.data = &first.data[(how_much_to_discard as usize)..];
+
+                next_offset += how_much_to_discard;
+            }
+
+            // Add the chunk of `other` with adjusted position.
+            frags.push(PatchFrag {
+                start: *start - offset,
+                end: *end - next_offset,
+                data,
+            });
+
+            // Go back to normal offset tracking for the next `o` chunk
+            offset = next_offset;
+        }
+
+        // Add remaining chunks of `self`.
+        for elt in &self.frags[pos..] {
+            frags.push(elt.clone());
+        }
+        PatchList { frags }
+    }
+}
+
+/// Combine a list of patch list into a single patch optimized patch list.
+pub fn fold_patch_lists<'a>(lists: &[PatchList<'a>]) -> PatchList<'a> {
+    if lists.len() <= 1 {
+        if lists.is_empty() {
+            PatchList { frags: vec![] }
+        } else {
+            lists[0].clone()
+        }
+    } else {
+        let (left, right) = lists.split_at(lists.len() / 2);
+        let mut left_res = fold_patch_lists(left);
+        let mut right_res = fold_patch_lists(right);
+        left_res.combine(&mut right_res)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    struct PatchDataBuilder {
+        data: Vec<u8>,
+    }
+
+    impl PatchDataBuilder {
+        pub fn new() -> Self {
+            Self { data: vec![] }
+        }
+
+        pub fn replace(
+            &mut self,
+            start: usize,
+            end: usize,
+            data: &[u8],
+        ) -> &mut Self {
+            assert!(start <= end);
+            self.data.extend(&(start as i32).to_be_bytes());
+            self.data.extend(&(end as i32).to_be_bytes());
+            self.data.extend(&(data.len() as i32).to_be_bytes());
+            self.data.extend(data.iter());
+            self
+        }
+
+        pub fn get(&mut self) -> &[u8] {
+            &self.data[..]
+        }
+    }
+
+    #[test]
+    fn test_ends_before() {
+        let data = vec![0u8, 0u8, 0u8];
+        let mut patch1_data = PatchDataBuilder::new();
+        patch1_data.replace(0, 1, &[1, 2]);
+        let mut patch1 = PatchList::new(patch1_data.get());
+
+        let mut patch2_data = PatchDataBuilder::new();
+        patch2_data.replace(2, 4, &[3, 4]);
+        let mut patch2 = PatchList::new(patch2_data.get());
+
+        let patch = patch1.combine(&mut patch2);
+
+        let result = patch.apply(&data);
+
+        assert_eq!(result, vec![1u8, 2, 3, 4]);
+    }
+
+    #[test]
+    fn test_starts_after() {
+        let data = vec![0u8, 0u8, 0u8];
+        let mut patch1_data = PatchDataBuilder::new();
+        patch1_data.replace(2, 3, &[3]);
+        let mut patch1 = PatchList::new(patch1_data.get());
+
+        let mut patch2_data = PatchDataBuilder::new();
+        patch2_data.replace(1, 2, &[1, 2]);
+        let mut patch2 = PatchList::new(patch2_data.get());
+
+        let patch = patch1.combine(&mut patch2);
+
+        let result = patch.apply(&data);
+
+        assert_eq!(result, vec![0u8, 1, 2, 3]);
+    }
+
+    #[test]
+    fn test_overridden() {
+        let data = vec![0u8, 0, 0];
+        let mut patch1_data = PatchDataBuilder::new();
+        patch1_data.replace(1, 2, &[3, 4]);
+        let mut patch1 = PatchList::new(patch1_data.get());
+
+        let mut patch2_data = PatchDataBuilder::new();
+        patch2_data.replace(1, 4, &[1, 2, 3]);
+        let mut patch2 = PatchList::new(patch2_data.get());
+
+        let patch = patch1.combine(&mut patch2);
+
+        let result = patch.apply(&data);
+
+        assert_eq!(result, vec![0u8, 1, 2, 3]);
+    }
+
+    #[test]
+    fn test_right_most_part_is_overridden() {
+        let data = vec![0u8, 0, 0];
+        let mut patch1_data = PatchDataBuilder::new();
+        patch1_data.replace(0, 1, &[1, 3]);
+        let mut patch1 = PatchList::new(patch1_data.get());
+
+        let mut patch2_data = PatchDataBuilder::new();
+        patch2_data.replace(1, 4, &[2, 3, 4]);
+        let mut patch2 = PatchList::new(patch2_data.get());
+
+        let patch = patch1.combine(&mut patch2);
+
+        let result = patch.apply(&data);
+
+        assert_eq!(result, vec![1u8, 2, 3, 4]);
+    }
+
+    #[test]
+    fn test_left_most_part_is_overridden() {
+        let data = vec![0u8, 0, 0];
+        let mut patch1_data = PatchDataBuilder::new();
+        patch1_data.replace(1, 3, &[1, 3, 4]);
+        let mut patch1 = PatchList::new(patch1_data.get());
+
+        let mut patch2_data = PatchDataBuilder::new();
+        patch2_data.replace(0, 2, &[1, 2]);
+        let mut patch2 = PatchList::new(patch2_data.get());
+
+        let patch = patch1.combine(&mut patch2);
+
+        let result = patch.apply(&data);
+
+        assert_eq!(result, vec![1u8, 2, 3, 4]);
+    }
+
+    #[test]
+    fn test_mid_is_overridden() {
+        let data = vec![0u8, 0, 0];
+        let mut patch1_data = PatchDataBuilder::new();
+        patch1_data.replace(0, 3, &[1, 3, 3, 4]);
+        let mut patch1 = PatchList::new(patch1_data.get());
+
+        let mut patch2_data = PatchDataBuilder::new();
+        patch2_data.replace(1, 3, &[2, 3]);
+        let mut patch2 = PatchList::new(patch2_data.get());
+
+        let patch = patch1.combine(&mut patch2);
+
+        let result = patch.apply(&data);
+
+        assert_eq!(result, vec![1u8, 2, 3, 4]);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/rust/hg-core/src/revlog/revlog.rs	Fri Sep 04 11:55:07 2020 +0200
@@ -0,0 +1,353 @@
+use std::borrow::Cow;
+use std::fs::File;
+use std::io::Read;
+use std::ops::Deref;
+use std::path::Path;
+
+use byteorder::{BigEndian, ByteOrder};
+use flate2::read::ZlibDecoder;
+use memmap::{Mmap, MmapOptions};
+use micro_timer::timed;
+use zstd;
+
+use super::index::Index;
+use super::patch;
+use crate::revlog::Revision;
+
+pub enum RevlogError {
+    IoError(std::io::Error),
+    UnsuportedVersion(u16),
+    InvalidRevision,
+    Corrupted,
+    UnknowDataFormat(u8),
+}
+
+fn mmap_open(path: &Path) -> Result<Mmap, std::io::Error> {
+    let file = File::open(path)?;
+    let mmap = unsafe { MmapOptions::new().map(&file) }?;
+    Ok(mmap)
+}
+
+/// Read only implementation of revlog.
+pub struct Revlog {
+    /// When index and data are not interleaved: bytes of the revlog index.
+    /// When index and data are interleaved: bytes of the revlog index and
+    /// data.
+    index_bytes: Box<dyn Deref<Target = [u8]> + Send>,
+    /// When index and data are not interleaved: bytes of the revlog data
+    data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>>,
+}
+
+impl Revlog {
+    /// Open a revlog index file.
+    ///
+    /// It will also open the associated data file if index and data are not
+    /// interleaved.
+    #[timed]
+    pub fn open(index_path: &Path) -> Result<Self, RevlogError> {
+        let index_mmap =
+            mmap_open(&index_path).map_err(RevlogError::IoError)?;
+
+        let version = get_version(&index_mmap);
+        if version != 1 {
+            return Err(RevlogError::UnsuportedVersion(version));
+        }
+
+        let is_inline = is_inline(&index_mmap);
+
+        let index_bytes = Box::new(index_mmap);
+
+        // TODO load data only when needed //
+        // type annotation required
+        // won't recognize Mmap as Deref<Target = [u8]>
+        let data_bytes: Option<Box<dyn Deref<Target = [u8]> + Send>> =
+            if is_inline {
+                None
+            } else {
+                let data_path = index_path.with_extension("d");
+                let data_mmap =
+                    mmap_open(&data_path).map_err(RevlogError::IoError)?;
+                Some(Box::new(data_mmap))
+            };
+
+        Ok(Revlog {
+            index_bytes,
+            data_bytes,
+        })
+    }
+
+    /// Return the full data associated to a revision.
+    ///
+    /// All entries required to build the final data out of deltas will be
+    /// retrieved as needed, and the deltas will be applied to the inital
+    /// snapshot to rebuild the final data.
+    #[timed]
+    pub fn get_rev_data(&self, rev: Revision) -> Result<Vec<u8>, RevlogError> {
+        // Todo return -> Cow
+        let mut entry = self.get_entry(rev)?;
+        let mut delta_chain = vec![];
+        while let Some(base_rev) = entry.base_rev {
+            delta_chain.push(entry);
+            entry = self
+                .get_entry(base_rev)
+                .map_err(|_| RevlogError::Corrupted)?;
+        }
+
+        if delta_chain.is_empty() {
+            Ok(entry.data()?.into())
+        } else {
+            Revlog::build_data_from_deltas(entry, &delta_chain)
+        }
+    }
+
+    /// Build the full data of a revision out its snapshot
+    /// and its deltas.
+    #[timed]
+    fn build_data_from_deltas(
+        snapshot: RevlogEntry,
+        deltas: &[RevlogEntry],
+    ) -> Result<Vec<u8>, RevlogError> {
+        let snapshot = snapshot.data()?;
+        let deltas = deltas
+            .iter()
+            .rev()
+            .map(RevlogEntry::data)
+            .collect::<Result<Vec<Cow<'_, [u8]>>, RevlogError>>()?;
+        let patches: Vec<_> =
+            deltas.iter().map(|d| patch::PatchList::new(d)).collect();
+        let patch = patch::fold_patch_lists(&patches);
+        Ok(patch.apply(&snapshot))
+    }
+
+    /// Return the revlog index.
+    fn index(&self) -> Index {
+        let is_inline = self.data_bytes.is_none();
+        Index::new(&self.index_bytes, is_inline)
+    }
+
+    /// Return the revlog data.
+    fn data(&self) -> &[u8] {
+        match self.data_bytes {
+            Some(ref data_bytes) => &data_bytes,
+            None => &self.index_bytes,
+        }
+    }
+
+    /// Get an entry of the revlog.
+    fn get_entry(&self, rev: Revision) -> Result<RevlogEntry, RevlogError> {
+        let index = self.index();
+        let index_entry =
+            index.get_entry(rev).ok_or(RevlogError::InvalidRevision)?;
+        let start = index_entry.offset();
+        let end = start + index_entry.compressed_len();
+        let entry = RevlogEntry {
+            rev,
+            bytes: &self.data()[start..end],
+            compressed_len: index_entry.compressed_len(),
+            uncompressed_len: index_entry.uncompressed_len(),
+            base_rev: if index_entry.base_revision() == rev {
+                None
+            } else {
+                Some(index_entry.base_revision())
+            },
+        };
+        Ok(entry)
+    }
+}
+
+/// The revlog entry's bytes and the necessary informations to extract
+/// the entry's data.
+#[derive(Debug)]
+pub struct RevlogEntry<'a> {
+    rev: Revision,
+    bytes: &'a [u8],
+    compressed_len: usize,
+    uncompressed_len: usize,
+    base_rev: Option<Revision>,
+}
+
+impl<'a> RevlogEntry<'a> {
+    /// Extract the data contained in the entry.
+    pub fn data(&self) -> Result<Cow<'_, [u8]>, RevlogError> {
+        if self.bytes.is_empty() {
+            return Ok(Cow::Borrowed(&[]));
+        }
+        match self.bytes[0] {
+            // Revision data is the entirety of the entry, including this
+            // header.
+            b'\0' => Ok(Cow::Borrowed(self.bytes)),
+            // Raw revision data follows.
+            b'u' => Ok(Cow::Borrowed(&self.bytes[1..])),
+            // zlib (RFC 1950) data.
+            b'x' => Ok(Cow::Owned(self.uncompressed_zlib_data())),
+            // zstd data.
+            b'\x28' => Ok(Cow::Owned(self.uncompressed_zstd_data())),
+            format_type => Err(RevlogError::UnknowDataFormat(format_type)),
+        }
+    }
+
+    fn uncompressed_zlib_data(&self) -> Vec<u8> {
+        let mut decoder = ZlibDecoder::new(self.bytes);
+        if self.is_delta() {
+            let mut buf = Vec::with_capacity(self.compressed_len);
+            decoder.read_to_end(&mut buf).expect("corrupted zlib data");
+            buf
+        } else {
+            let mut buf = vec![0; self.uncompressed_len];
+            decoder.read_exact(&mut buf).expect("corrupted zlib data");
+            buf
+        }
+    }
+
+    fn uncompressed_zstd_data(&self) -> Vec<u8> {
+        if self.is_delta() {
+            let mut buf = Vec::with_capacity(self.compressed_len);
+            zstd::stream::copy_decode(self.bytes, &mut buf)
+                .expect("corrupted zstd data");
+            buf
+        } else {
+            let mut buf = vec![0; self.uncompressed_len];
+            let len = zstd::block::decompress_to_buffer(self.bytes, &mut buf)
+                .expect("corrupted zstd data");
+            assert_eq!(len, self.uncompressed_len, "corrupted zstd data");
+            buf
+        }
+    }
+
+    /// Tell if the entry is a snapshot or a delta
+    /// (influences on decompression).
+    fn is_delta(&self) -> bool {
+        self.base_rev.is_some()
+    }
+}
+
+/// Value of the inline flag.
+pub fn is_inline(index_bytes: &[u8]) -> bool {
+    match &index_bytes[0..=1] {
+        [0, 0] | [0, 2] => false,
+        _ => true,
+    }
+}
+
+/// Format version of the revlog.
+pub fn get_version(index_bytes: &[u8]) -> u16 {
+    BigEndian::read_u16(&index_bytes[2..=3])
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    use super::super::index::IndexEntryBuilder;
+
+    #[cfg(test)]
+    pub struct RevlogBuilder {
+        version: u16,
+        is_general_delta: bool,
+        is_inline: bool,
+        offset: usize,
+        index: Vec<Vec<u8>>,
+        data: Vec<Vec<u8>>,
+    }
+
+    #[cfg(test)]
+    impl RevlogBuilder {
+        pub fn new() -> Self {
+            Self {
+                version: 2,
+                is_inline: false,
+                is_general_delta: true,
+                offset: 0,
+                index: vec![],
+                data: vec![],
+            }
+        }
+
+        pub fn with_inline(&mut self, value: bool) -> &mut Self {
+            self.is_inline = value;
+            self
+        }
+
+        pub fn with_general_delta(&mut self, value: bool) -> &mut Self {
+            self.is_general_delta = value;
+            self
+        }
+
+        pub fn with_version(&mut self, value: u16) -> &mut Self {
+            self.version = value;
+            self
+        }
+
+        pub fn push(
+            &mut self,
+            mut index: IndexEntryBuilder,
+            data: Vec<u8>,
+        ) -> &mut Self {
+            if self.index.is_empty() {
+                index.is_first(true);
+                index.with_general_delta(self.is_general_delta);
+                index.with_inline(self.is_inline);
+                index.with_version(self.version);
+            } else {
+                index.with_offset(self.offset);
+            }
+            self.index.push(index.build());
+            self.offset += data.len();
+            self.data.push(data);
+            self
+        }
+
+        pub fn build_inline(&self) -> Vec<u8> {
+            let mut bytes =
+                Vec::with_capacity(self.index.len() + self.data.len());
+            for (index, data) in self.index.iter().zip(self.data.iter()) {
+                bytes.extend(index);
+                bytes.extend(data);
+            }
+            bytes
+        }
+    }
+
+    #[test]
+    fn is_not_inline_when_no_inline_flag_test() {
+        let bytes = RevlogBuilder::new()
+            .with_general_delta(false)
+            .with_inline(false)
+            .push(IndexEntryBuilder::new(), vec![])
+            .build_inline();
+
+        assert_eq!(is_inline(&bytes), false)
+    }
+
+    #[test]
+    fn is_inline_when_inline_flag_test() {
+        let bytes = RevlogBuilder::new()
+            .with_general_delta(false)
+            .with_inline(true)
+            .push(IndexEntryBuilder::new(), vec![])
+            .build_inline();
+
+        assert_eq!(is_inline(&bytes), true)
+    }
+
+    #[test]
+    fn is_inline_when_inline_and_generaldelta_flags_test() {
+        let bytes = RevlogBuilder::new()
+            .with_general_delta(true)
+            .with_inline(true)
+            .push(IndexEntryBuilder::new(), vec![])
+            .build_inline();
+
+        assert_eq!(is_inline(&bytes), true)
+    }
+
+    #[test]
+    fn version_test() {
+        let bytes = RevlogBuilder::new()
+            .with_version(1)
+            .push(IndexEntryBuilder::new(), vec![])
+            .build_inline();
+
+        assert_eq!(get_version(&bytes), 1)
+    }
+}