:)
This commit is contained in:
parent
4d646c02f2
commit
17b8ef8bd9
567
Cargo.lock
generated
567
Cargo.lock
generated
@ -1,567 +0,0 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "adler32"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.1"
|
||||
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)",
|
||||
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32c"
|
||||
version = "0.4.0"
|
||||
source = "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.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (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.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.71 (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 = "hermit-abi"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"libc 0.2.71 (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 = "lexical-core"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cc 1.0.54 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nodrop"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "5.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lexical-core 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.18"
|
||||
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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quickcheck_macros"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
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.71 (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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
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)",
|
||||
"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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
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)",
|
||||
]
|
||||
|
||||
[[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 = "ryu"
|
||||
version = "1.0.5"
|
||||
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 = "static_assertions"
|
||||
version = "0.3.4"
|
||||
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.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.71 (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.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
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)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thinp"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"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)",
|
||||
"crc32c 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"fixedbitset 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quick-xml 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quickcheck_macros 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread_local"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.8"
|
||||
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)",
|
||||
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[metadata]
|
||||
"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
|
||||
"checksum aho-corasick 0.7.10 (registry+https://github.com/rust-lang/crates.io-index)" = "8716408b8bc624ed7f65d223ddb9ac2d044c0547b6fa4b0d554f3a9540496ada"
|
||||
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
"checksum anyhow 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "85bb70cc08ec97ca5450e6eba421deeea5f172c0fc61f78b5357b2a8e8be195f"
|
||||
"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9"
|
||||
"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 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.54 (registry+https://github.com/rust-lang/crates.io-index)" = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
|
||||
"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
|
||||
"checksum clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
|
||||
"checksum crc32c 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "77ba37ef26c12988c1cee882d522d65e1d5d2ad8c3864665b88ee92767ed84c5"
|
||||
"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1"
|
||||
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||
"checksum fixedbitset 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2fc4fcacf5cd3681968f6524ea159383132937739c6c40dabab9e37ed515911b"
|
||||
"checksum flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)" = "2cfff41391129e0a856d6d822600b8d71179d46879e310417eb9c762eb178b42"
|
||||
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
|
||||
"checksum hermit-abi 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "91780f809e750b0a89f5544be56617ff6b1227ee485bcb06ebe10cdf89bd3b71"
|
||||
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
"checksum lexical-core 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f86d66d380c9c5a685aaac7a11818bdfa1f733198dfd9ec09c70b762cd12ad6f"
|
||||
"checksum libc 0.2.71 (registry+https://github.com/rust-lang/crates.io-index)" = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
||||
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
|
||||
"checksum memchr 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
"checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5"
|
||||
"checksum nix 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363"
|
||||
"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||
"checksum nom 5.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6"
|
||||
"checksum num-derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0c8b15b261814f992e33760b1fca9fe8b693d8a65299f20c9901688636cfb746"
|
||||
"checksum num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096"
|
||||
"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
"checksum ppv-lite86 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
|
||||
"checksum proc-macro2 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
||||
"checksum quick-xml 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3cc440ee4802a86e357165021e3e255a9143724da31db1e2ea540214c96a0f82"
|
||||
"checksum quickcheck 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f"
|
||||
"checksum quickcheck_macros 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f"
|
||||
"checksum quote 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
|
||||
"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_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
"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.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum ryu 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
"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 static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"checksum syn 1.0.30 (registry+https://github.com/rust-lang/crates.io-index)" = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2"
|
||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
"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 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.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
"checksum version_check 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"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-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
27
Cargo.toml
27
Cargo.toml
@ -1,27 +0,0 @@
|
||||
[package]
|
||||
name = "thinp"
|
||||
version = "0.1.0"
|
||||
authors = ["Joe Thornber <ejt@redhat.com>"]
|
||||
edition = "2018"
|
||||
license = "GPL3"
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
byteorder = "1.3"
|
||||
clap = "2.33"
|
||||
crc32c = "0.4"
|
||||
flate2 = "1.0"
|
||||
fixedbitset = "0.3"
|
||||
libc = "0.2.71"
|
||||
quick-xml = "0.18"
|
||||
nix = "0.17"
|
||||
nom = "5.1"
|
||||
num_cpus = "1.13"
|
||||
rand = "0.7"
|
||||
tempfile = "3.1"
|
||||
num-traits = "0.2"
|
||||
num-derive = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "0.9"
|
||||
quickcheck_macros = "0.9"
|
13
Makefile.in
13
Makefile.in
@ -338,18 +338,7 @@ ifeq ("@DEVTOOLS@", "yes")
|
||||
ln -s -f pdata_tools $(BINDIR)/thin_scan
|
||||
endif
|
||||
|
||||
.PHONY: install install-rust-tools rust-tools
|
||||
|
||||
rust-tools:
|
||||
cargo build --release
|
||||
|
||||
install-rust-tools: man8/thin_metadata_pack.8 man8/thin_metadata_unpack.8 rust-tools
|
||||
$(INSTALL_PROGRAM) target/release/thin_metadata_pack $(BINDIR)
|
||||
$(INSTALL_PROGRAM) target/release/thin_metadata_unpack $(BINDIR)
|
||||
$(STRIP) $(BINDIR)/thin_metadata_pack
|
||||
$(STRIP) $(BINDIR)/thin_metadata_unpack
|
||||
$(INSTALL_DATA) man8/thin_metadata_pack.8 $(MANPATH)/man8
|
||||
$(INSTALL_DATA) man8/thin_metadata_unpack.8 $(MANPATH)/man8
|
||||
.PHONY: install
|
||||
|
||||
ifeq ("@TESTING@", "yes")
|
||||
include unit-tests/Makefile
|
||||
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
../target/release/thin_metadata_pack
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
../target/release/thin_metadata_unpack
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1 +0,0 @@
|
||||
pdata_tools
|
@ -1,39 +0,0 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
use thinp::file_utils;
|
||||
|
||||
fn main() {
|
||||
let parser = App::new("thin_metadata_pack")
|
||||
.version(thinp::version::TOOLS_VERSION)
|
||||
.about("Produces a compressed file of thin metadata. Only packs metadata blocks that are actually used.")
|
||||
.arg(Arg::with_name("INPUT")
|
||||
.help("Specify thinp metadata binary device/file")
|
||||
.required(true)
|
||||
.short("i")
|
||||
.value_name("DEV")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("OUTPUT")
|
||||
.help("Specify packed output file")
|
||||
.required(true)
|
||||
.short("o")
|
||||
.value_name("FILE")
|
||||
.takes_value(true));
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if !file_utils::file_exists(&input_file) {
|
||||
eprintln!("Couldn't find input file '{}'.", &input_file.display());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if let Err(reason) = thinp::pack::toplevel::pack(&input_file, &output_file) {
|
||||
println!("Application error: {}\n", reason);
|
||||
exit(1);
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process;
|
||||
use thinp::file_utils;
|
||||
|
||||
use std::process::exit;
|
||||
|
||||
fn main() {
|
||||
let parser = App::new("thin_metadata_unpack")
|
||||
.version(thinp::version::TOOLS_VERSION)
|
||||
.about("Unpack a compressed file of thin metadata.")
|
||||
.arg(
|
||||
Arg::with_name("INPUT")
|
||||
.help("Specify thinp metadata binary device/file")
|
||||
.required(true)
|
||||
.short("i")
|
||||
.value_name("DEV")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("OUTPUT")
|
||||
.help("Specify packed output file")
|
||||
.required(true)
|
||||
.short("o")
|
||||
.value_name("FILE")
|
||||
.takes_value(true),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
|
||||
if !file_utils::file_exists(input_file) {
|
||||
eprintln!("Couldn't find input file '{}'.", &input_file.display());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if let Err(reason) = thinp::pack::toplevel::unpack(&input_file, &output_file) {
|
||||
println!("Application error: {}", reason);
|
||||
process::exit(1);
|
||||
}
|
||||
}
|
@ -1,74 +0,0 @@
|
||||
extern crate clap;
|
||||
extern crate thinp;
|
||||
|
||||
use clap::{App, Arg};
|
||||
use std::path::Path;
|
||||
use std::process::exit;
|
||||
use thinp::file_utils;
|
||||
|
||||
fn main() {
|
||||
let parser = App::new("thin_shrink")
|
||||
.version(thinp::version::TOOLS_VERSION)
|
||||
.about("Rewrite xml metadata and move data in an inactive pool.")
|
||||
.arg(
|
||||
Arg::with_name("INPUT")
|
||||
.help("Specify thinp metadata xml file")
|
||||
.required(true)
|
||||
.long("input")
|
||||
.value_name("INPUT")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("OUTPUT")
|
||||
.help("Specify output xml file")
|
||||
.required(true)
|
||||
.long("output")
|
||||
.value_name("OUTPUT")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("DATA")
|
||||
.help("Specify pool data device where data will be moved")
|
||||
.required(true)
|
||||
.long("data")
|
||||
.value_name("DATA")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("NOCOPY")
|
||||
.help("Skip the copying of data, useful for benchmarking")
|
||||
.required(false)
|
||||
.long("no-copy")
|
||||
.value_name("NOCOPY")
|
||||
.takes_value(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("SIZE")
|
||||
.help("Specify new size for the pool (in data blocks)")
|
||||
.required(true)
|
||||
.long("nr-blocks")
|
||||
.value_name("SIZE")
|
||||
.takes_value(true),
|
||||
);
|
||||
|
||||
let matches = parser.get_matches();
|
||||
|
||||
// FIXME: check these look like xml
|
||||
let input_file = Path::new(matches.value_of("INPUT").unwrap());
|
||||
let output_file = Path::new(matches.value_of("OUTPUT").unwrap());
|
||||
let size = matches.value_of("SIZE").unwrap().parse::<u64>().unwrap();
|
||||
let data_file = Path::new(matches.value_of("DATA").unwrap());
|
||||
let do_copy = !matches.is_present("NOCOPY");
|
||||
|
||||
if !file_utils::file_exists(input_file) {
|
||||
eprintln!("Couldn't find input file '{}'.", input_file.display());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if let Err(reason) =
|
||||
thinp::shrink::toplevel::shrink(&input_file, &output_file, &data_file, size, do_copy)
|
||||
{
|
||||
println!("Application error: {}\n", reason);
|
||||
exit(1);
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
use std::io;
|
||||
use std::io::{Read, Seek};
|
||||
use std::fs::OpenOptions;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::fs::File;
|
||||
|
||||
pub const BLOCK_SIZE: usize = 4096;
|
||||
|
||||
#[repr(align(4096))]
|
||||
pub struct Block {
|
||||
pub data: [u8; BLOCK_SIZE as usize],
|
||||
}
|
||||
|
||||
pub struct BlockManager {
|
||||
pub nr_blocks: u64,
|
||||
input: File,
|
||||
}
|
||||
|
||||
fn get_nr_blocks(path: &str) -> io::Result<u64> {
|
||||
let metadata = std::fs::metadata(path)?;
|
||||
Ok(metadata.len() / (BLOCK_SIZE as u64))
|
||||
}
|
||||
|
||||
impl BlockManager {
|
||||
pub fn new(path: &str, _cache_size: usize) -> io::Result<BlockManager> {
|
||||
let input = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.custom_flags(libc::O_DIRECT)
|
||||
.open(path)?;
|
||||
|
||||
Ok(BlockManager {
|
||||
nr_blocks: get_nr_blocks(path)?,
|
||||
input,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get(&mut self, b: u64) -> io::Result<Block> {
|
||||
self.read_block(b)
|
||||
}
|
||||
|
||||
fn read_block(&mut self, b: u64) -> io::Result<Block>
|
||||
{
|
||||
let mut buf = Block {data: [0; BLOCK_SIZE]};
|
||||
|
||||
self.input.seek(io::SeekFrom::Start(b * (BLOCK_SIZE as u64)))?;
|
||||
self.input.read_exact(&mut buf.data)?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
13
src/check.rs
13
src/check.rs
@ -1,13 +0,0 @@
|
||||
use std::error::Error;
|
||||
|
||||
use crate::block_manager::BlockManager;
|
||||
|
||||
pub fn check(dev: &str) -> Result<(), Box<dyn Error>> {
|
||||
let mut bm = BlockManager::new(dev, 1024)?;
|
||||
|
||||
for b in 0..100 {
|
||||
let _block = bm.get(b)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,100 +0,0 @@
|
||||
use nix::sys::stat;
|
||||
use nix::sys::stat::{FileStat, SFlag};
|
||||
use std::fs::{File, OpenOptions};
|
||||
use std::io;
|
||||
use std::io::{Seek, Write};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::path::Path;
|
||||
use tempfile::tempfile;
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
fn check_bits(mode: u32, flag: &SFlag) -> bool {
|
||||
(mode & flag.bits()) != 0
|
||||
}
|
||||
|
||||
pub fn is_file_or_blk(info: FileStat) -> bool {
|
||||
check_bits(info.st_mode, &stat::SFlag::S_IFBLK)
|
||||
|| check_bits(info.st_mode, &stat::SFlag::S_IFREG)
|
||||
}
|
||||
|
||||
pub fn file_exists(path: &Path) -> bool {
|
||||
match stat::stat(path) {
|
||||
Ok(info) => is_file_or_blk(info),
|
||||
_ => {
|
||||
// FIXME: assuming all errors indicate the file doesn't
|
||||
// exist.
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
const BLKGETSIZE64_CODE: u8 = 0x12;
|
||||
const BLKGETSIZE64_SEQ: u8 = 114;
|
||||
ioctl_read!(ioctl_blkgetsize64, BLKGETSIZE64_CODE, BLKGETSIZE64_SEQ, u64);
|
||||
|
||||
pub fn fail<T>(msg: &str) -> io::Result<T> {
|
||||
let e = io::Error::new(io::ErrorKind::Other, msg);
|
||||
Err(e)
|
||||
}
|
||||
|
||||
fn get_device_size(path: &Path) -> io::Result<u64> {
|
||||
let file = File::open(path)?;
|
||||
let fd = file.as_raw_fd();
|
||||
let mut cap = 0u64;
|
||||
unsafe {
|
||||
match ioctl_blkgetsize64(fd, &mut cap) {
|
||||
Ok(_) => Ok(cap),
|
||||
_ => fail("BLKGETSIZE64 ioctl failed"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file_size(path: &Path) -> io::Result<u64> {
|
||||
match stat::stat(path) {
|
||||
Ok(info) => {
|
||||
if check_bits(info.st_mode, &SFlag::S_IFREG) {
|
||||
Ok(info.st_size as u64)
|
||||
} else if check_bits(info.st_mode, &SFlag::S_IFBLK) {
|
||||
get_device_size(path)
|
||||
} else {
|
||||
fail("not a regular file or block device")
|
||||
}
|
||||
}
|
||||
_ => fail("stat failed"),
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
fn set_size<W: Write + Seek>(w: &mut W, nr_bytes: u64) -> io::Result<()> {
|
||||
let zeroes: Vec<u8> = vec![0; 1];
|
||||
|
||||
if nr_bytes > 0 {
|
||||
w.seek(io::SeekFrom::Start(nr_bytes - 1))?;
|
||||
w.write_all(&zeroes)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn temp_file_sized(nr_bytes: u64) -> io::Result<std::fs::File> {
|
||||
let mut file = tempfile()?;
|
||||
set_size(&mut file, nr_bytes)?;
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
pub fn create_sized_file(path: &Path, nr_bytes: u64) -> io::Result<std::fs::File> {
|
||||
let mut file = OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(path)?;
|
||||
set_size(&mut file, nr_bytes)?;
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
//---------------------------------------
|
24
src/lib.rs
24
src/lib.rs
@ -1,24 +0,0 @@
|
||||
extern crate anyhow;
|
||||
extern crate byteorder;
|
||||
extern crate crc32c;
|
||||
extern crate flate2;
|
||||
extern crate nom;
|
||||
extern crate num_cpus;
|
||||
|
||||
#[macro_use]
|
||||
extern crate nix;
|
||||
|
||||
#[cfg(test)]
|
||||
extern crate quickcheck;
|
||||
#[cfg(test)]
|
||||
#[macro_use(quickcheck)]
|
||||
#[cfg(test)]
|
||||
extern crate quickcheck_macros;
|
||||
|
||||
pub mod block_manager;
|
||||
pub mod check;
|
||||
pub mod file_utils;
|
||||
pub mod pack;
|
||||
pub mod shrink;
|
||||
pub mod thin;
|
||||
pub mod version;
|
@ -1,175 +0,0 @@
|
||||
//-------------------------------------------------
|
||||
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Delta {
|
||||
Base { n: u64 },
|
||||
Const { count: u64 },
|
||||
Pos { delta: u64, count: u64 },
|
||||
Neg { delta: u64, count: u64 },
|
||||
}
|
||||
|
||||
use Delta::*;
|
||||
|
||||
pub fn to_delta(ns: &[u64]) -> Vec<Delta> {
|
||||
use std::cmp::Ordering::*;
|
||||
|
||||
let mut ds = Vec::new();
|
||||
|
||||
if !ns.is_empty() {
|
||||
let mut base = ns[0];
|
||||
ds.push(Base { n: base });
|
||||
|
||||
let mut i = 1;
|
||||
while i < ns.len() {
|
||||
let n = ns[i];
|
||||
match n.cmp(&base) {
|
||||
Less => {
|
||||
let delta = base - n;
|
||||
let mut count = 1;
|
||||
while i < ns.len() && (ns[i] + (count * delta) == base) {
|
||||
i += 1;
|
||||
count += 1;
|
||||
}
|
||||
count -= 1;
|
||||
ds.push(Neg {
|
||||
delta,
|
||||
count,
|
||||
});
|
||||
base -= delta * count;
|
||||
}
|
||||
Equal => {
|
||||
let mut count = 1;
|
||||
while i < ns.len() && ns[i] == base {
|
||||
i += 1;
|
||||
count += 1;
|
||||
}
|
||||
count -= 1;
|
||||
ds.push(Const { count });
|
||||
}
|
||||
Greater => {
|
||||
let delta = n - base;
|
||||
let mut count = 1;
|
||||
while i < ns.len() && (ns[i] == (base + (count * delta))) {
|
||||
i += 1;
|
||||
count += 1;
|
||||
}
|
||||
count -= 1;
|
||||
ds.push(Pos {
|
||||
delta,
|
||||
count,
|
||||
});
|
||||
base += delta * count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ds
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn from_delta(ds: &[Delta]) -> Vec<u64> {
|
||||
let mut ns: Vec<u64> = Vec::new();
|
||||
let mut base = 0u64;
|
||||
|
||||
for d in ds {
|
||||
match d {
|
||||
Base { n } => {
|
||||
ns.push(*n);
|
||||
base = *n;
|
||||
}
|
||||
Const { count } => {
|
||||
for _ in 0..*count {
|
||||
ns.push(base);
|
||||
}
|
||||
}
|
||||
Pos { delta, count } => {
|
||||
for _ in 0..*count {
|
||||
base += delta;
|
||||
ns.push(base);
|
||||
}
|
||||
}
|
||||
Neg { delta, count } => {
|
||||
for _ in 0..*count {
|
||||
assert!(base >= *delta);
|
||||
base -= delta;
|
||||
ns.push(base);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ns
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_delta() {
|
||||
struct TestCase(Vec<u64>, Vec<Delta>);
|
||||
|
||||
let cases = [
|
||||
TestCase(vec![], vec![]),
|
||||
TestCase(vec![1], vec![Base { n: 1 }]),
|
||||
TestCase(vec![1, 2], vec![Base { n: 1 }, Pos { delta: 1, count: 1 }]),
|
||||
TestCase(
|
||||
vec![1, 2, 3, 4],
|
||||
vec![Base { n: 1 }, Pos { delta: 1, count: 3 }],
|
||||
),
|
||||
TestCase(
|
||||
vec![2, 4, 6, 8],
|
||||
vec![Base { n: 2 }, Pos { delta: 2, count: 3 }],
|
||||
),
|
||||
TestCase(
|
||||
vec![7, 14, 21, 28],
|
||||
vec![Base { n: 7 }, Pos { delta: 7, count: 3 }],
|
||||
),
|
||||
TestCase(
|
||||
vec![10, 9],
|
||||
vec![Base { n: 10 }, Neg { delta: 1, count: 1 }],
|
||||
),
|
||||
TestCase(
|
||||
vec![10, 9, 8, 7],
|
||||
vec![Base { n: 10 }, Neg { delta: 1, count: 3 }],
|
||||
),
|
||||
TestCase(
|
||||
vec![10, 8, 6, 4],
|
||||
vec![Base { n: 10 }, Neg { delta: 2, count: 3 }],
|
||||
),
|
||||
TestCase(
|
||||
vec![28, 21, 14, 7],
|
||||
vec![Base { n: 28 }, Neg { delta: 7, count: 3 }],
|
||||
),
|
||||
TestCase(
|
||||
vec![42, 42, 42, 42],
|
||||
vec![Base { n: 42 }, Const { count: 3 }],
|
||||
),
|
||||
TestCase(
|
||||
vec![1, 2, 3, 10, 20, 30, 40, 38, 36, 34, 0, 0, 0, 0],
|
||||
vec![
|
||||
Base { n: 1 },
|
||||
Pos { delta: 1, count: 2 },
|
||||
Pos { delta: 7, count: 1 },
|
||||
Pos {
|
||||
delta: 10,
|
||||
count: 3,
|
||||
},
|
||||
Neg { delta: 2, count: 3 },
|
||||
Neg {
|
||||
delta: 34,
|
||||
count: 1,
|
||||
},
|
||||
Const { count: 3 },
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
for t in &cases {
|
||||
assert_eq!(to_delta(&t.0), t.1);
|
||||
assert_eq!(from_delta(&t.1), t.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
@ -1,5 +0,0 @@
|
||||
pub mod toplevel;
|
||||
|
||||
mod delta_list;
|
||||
mod node_encode;
|
||||
mod vm;
|
@ -1,127 +0,0 @@
|
||||
use std::{io, io::Write};
|
||||
|
||||
use nom::{bytes::complete::*, number::complete::*, IResult};
|
||||
|
||||
use crate::pack::vm::*;
|
||||
|
||||
//-------------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PackError {
|
||||
ParseError,
|
||||
IOError,
|
||||
}
|
||||
|
||||
impl std::error::Error for PackError {}
|
||||
|
||||
pub type PResult<T> = Result<T, PackError>;
|
||||
|
||||
fn nom_to_pr<T>(r: IResult<&[u8], T>) -> PResult<(&[u8], T)> {
|
||||
match r {
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => Err(PackError::ParseError),
|
||||
}
|
||||
}
|
||||
|
||||
fn io_to_pr<T>(r: io::Result<T>) -> PResult<T> {
|
||||
match r {
|
||||
Ok(v) => Ok(v),
|
||||
Err(_) => Err(PackError::IOError),
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------
|
||||
|
||||
impl std::fmt::Display for PackError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
PackError::ParseError => write!(f, "parse error"),
|
||||
PackError::IOError => write!(f, "IO error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run64(i: &[u8], count: usize) -> IResult<&[u8], Vec<u64>> {
|
||||
let (i, ns) = nom::multi::many_m_n(count, count, le_u64)(i)?;
|
||||
Ok((i, ns))
|
||||
}
|
||||
|
||||
struct NodeSummary {
|
||||
is_leaf: bool,
|
||||
max_entries: usize,
|
||||
value_size: usize
|
||||
}
|
||||
|
||||
fn summarise_node(data: &[u8]) -> IResult<&[u8], NodeSummary> {
|
||||
let (i, _csum) = le_u32(data)?;
|
||||
let (i, flags) = le_u32(i)?;
|
||||
let (i, _blocknr) = le_u64(i)?;
|
||||
let (i, _nr_entries) = le_u32(i)?;
|
||||
let (i, max_entries) = le_u32(i)?;
|
||||
let (i, value_size) = le_u32(i)?;
|
||||
let (i, _padding) = le_u32(i)?;
|
||||
Ok((i, NodeSummary {
|
||||
is_leaf: flags == 2,
|
||||
max_entries: max_entries as usize,
|
||||
value_size: value_size as usize,
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn pack_btree_node<W: Write>(w: &mut W, data: &[u8]) -> PResult<()> {
|
||||
let (_, info) = nom_to_pr(summarise_node(data))?;
|
||||
|
||||
if info.is_leaf {
|
||||
if info.value_size == std::mem::size_of::<u64>() {
|
||||
let (i, hdr) = nom_to_pr(take(32usize)(data))?;
|
||||
let (i, keys) = nom_to_pr(run64(i, info.max_entries))?;
|
||||
let (tail, values) = nom_to_pr(run64(i, info.max_entries))?;
|
||||
|
||||
io_to_pr(pack_literal(w, hdr))?;
|
||||
io_to_pr(pack_u64s(w, &keys))?;
|
||||
io_to_pr(pack_shifted_u64s(w, &values))?;
|
||||
if !tail.is_empty() {
|
||||
io_to_pr(pack_literal(w, tail))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
} else {
|
||||
// We don't bother packing the values if they aren't u64
|
||||
let (i, hdr) = nom_to_pr(take(32usize)(data))?;
|
||||
let (tail, keys) = nom_to_pr(run64(i, info.max_entries))?;
|
||||
|
||||
io_to_pr(pack_literal(w, hdr))?;
|
||||
io_to_pr(pack_u64s(w, &keys))?;
|
||||
io_to_pr(pack_literal(w, tail))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
// Internal node, values are also u64s
|
||||
let (i, hdr) = nom_to_pr(take(32usize)(data))?;
|
||||
let (i, keys) = nom_to_pr(run64(i, info.max_entries))?;
|
||||
let (tail, values) = nom_to_pr(run64(i, info.max_entries))?;
|
||||
|
||||
io_to_pr(pack_literal(w, hdr))?;
|
||||
io_to_pr(pack_u64s(w, &keys))?;
|
||||
io_to_pr(pack_u64s(w, &values))?;
|
||||
if !tail.is_empty() {
|
||||
io_to_pr(pack_literal(w, tail))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pack_superblock<W: Write>(w: &mut W, bytes: &[u8]) -> PResult<()> {
|
||||
io_to_pr(pack_literal(w, bytes))
|
||||
}
|
||||
|
||||
pub fn pack_bitmap<W: Write>(w: &mut W, bytes: &[u8]) -> PResult<()> {
|
||||
io_to_pr(pack_literal(w, bytes))
|
||||
}
|
||||
|
||||
pub fn pack_index<W: Write>(w: &mut W, bytes: &[u8]) -> PResult<()> {
|
||||
io_to_pr(pack_literal(w, bytes))
|
||||
}
|
||||
|
||||
//-------------------------------------
|
@ -1,357 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
use flate2::{read::ZlibDecoder, write::ZlibEncoder, Compression};
|
||||
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::{
|
||||
error::Error,
|
||||
fs::OpenOptions,
|
||||
path::Path,
|
||||
io,
|
||||
io::prelude::*,
|
||||
io::Cursor,
|
||||
io::Write,
|
||||
ops::DerefMut,
|
||||
sync::{Arc, Mutex},
|
||||
thread::spawn,
|
||||
};
|
||||
|
||||
use rand::prelude::*;
|
||||
use std::sync::mpsc::{sync_channel, Receiver};
|
||||
|
||||
use crate::file_utils;
|
||||
use crate::pack::node_encode::*;
|
||||
|
||||
const BLOCK_SIZE: u64 = 4096;
|
||||
const MAGIC: u64 = 0xa537a0aa6309ef77;
|
||||
const PACK_VERSION: u64 = 3;
|
||||
const SUPERBLOCK_CSUM_XOR: u32 = 160774;
|
||||
const BITMAP_CSUM_XOR: u32 = 240779;
|
||||
const INDEX_CSUM_XOR: u32 = 160478;
|
||||
const BTREE_CSUM_XOR: u32 = 121107;
|
||||
|
||||
fn shuffle<T>(v: &mut Vec<T>) {
|
||||
let mut rng = rand::thread_rng();
|
||||
v.shuffle(&mut rng);
|
||||
}
|
||||
|
||||
// Each thread processes multiple contiguous runs of blocks, called
|
||||
// chunks. Chunks are shuffled so each thread gets chunks spread
|
||||
// across the dev in case there are large regions that don't contain
|
||||
// metadata.
|
||||
fn mk_chunk_vecs(nr_blocks: u64, nr_jobs: u64) -> Vec<Vec<(u64, u64)>> {
|
||||
use std::cmp::{max, min};
|
||||
|
||||
let chunk_size = min(4 * 1024u64, max(128u64, nr_blocks / (nr_jobs * 64)));
|
||||
let nr_chunks = nr_blocks / chunk_size;
|
||||
let mut chunks = Vec::with_capacity(nr_chunks as usize);
|
||||
for i in 0..nr_chunks {
|
||||
chunks.push((i * chunk_size, (i + 1) * chunk_size));
|
||||
}
|
||||
|
||||
// there may be a smaller chunk at the back of the file.
|
||||
if nr_chunks * chunk_size < nr_blocks {
|
||||
chunks.push((nr_chunks * chunk_size, nr_blocks));
|
||||
}
|
||||
|
||||
shuffle(&mut chunks);
|
||||
|
||||
let mut vs = Vec::with_capacity(nr_jobs as usize);
|
||||
for _ in 0..nr_jobs {
|
||||
vs.push(Vec::new());
|
||||
}
|
||||
|
||||
for c in 0..nr_chunks {
|
||||
vs[(c % nr_jobs) as usize].push(chunks[c as usize]);
|
||||
}
|
||||
|
||||
vs
|
||||
}
|
||||
|
||||
pub fn pack(input_file: &Path, output_file: &Path) -> Result<(), Box<dyn Error>> {
|
||||
let nr_blocks = get_nr_blocks(&input_file)?;
|
||||
let nr_jobs = std::cmp::max(1, std::cmp::min(num_cpus::get() as u64, nr_blocks / 128));
|
||||
let chunk_vecs = mk_chunk_vecs(nr_blocks, nr_jobs);
|
||||
|
||||
let input = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.custom_flags(libc::O_EXCL)
|
||||
.open(input_file)?;
|
||||
|
||||
let output = OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(output_file)?;
|
||||
|
||||
write_header(&output, nr_blocks)?;
|
||||
|
||||
let sync_input = Arc::new(Mutex::new(input));
|
||||
let sync_output = Arc::new(Mutex::new(output));
|
||||
|
||||
let mut threads = Vec::new();
|
||||
for job in 0..nr_jobs {
|
||||
let sync_input = Arc::clone(&sync_input);
|
||||
let sync_output = Arc::clone(&sync_output);
|
||||
let chunks = chunk_vecs[job as usize].clone();
|
||||
threads.push(spawn(move || crunch(sync_input, sync_output, chunks)));
|
||||
}
|
||||
|
||||
for t in threads {
|
||||
t.join().unwrap()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn crunch<R, W>(input: Arc<Mutex<R>>, output: Arc<Mutex<W>>, ranges: Vec<(u64, u64)>) -> Result<()>
|
||||
where
|
||||
R: Read + Seek,
|
||||
W: Write,
|
||||
{
|
||||
let mut written = 0u64;
|
||||
let mut z = ZlibEncoder::new(Vec::new(), Compression::default());
|
||||
for (lo, hi) in ranges {
|
||||
// We read multiple blocks at once to reduce contention
|
||||
// on input.
|
||||
let mut input = input.lock().unwrap();
|
||||
let big_data = read_blocks(input.deref_mut(), lo, hi - lo)?;
|
||||
drop(input);
|
||||
|
||||
for b in lo..hi {
|
||||
let block_start = ((b - lo) * BLOCK_SIZE) as usize;
|
||||
let data = &big_data[block_start..(block_start + BLOCK_SIZE as usize)];
|
||||
let kind = metadata_block_type(data);
|
||||
if kind != BT::UNKNOWN {
|
||||
z.write_u64::<LittleEndian>(b)?;
|
||||
pack_block(&mut z, kind, &data);
|
||||
|
||||
written += 1;
|
||||
if written == 1024 {
|
||||
let compressed = z.reset(Vec::new())?;
|
||||
|
||||
let mut output = output.lock().unwrap();
|
||||
output.write_u64::<LittleEndian>(compressed.len() as u64)?;
|
||||
output.write_all(&compressed)?;
|
||||
written = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if written > 0 {
|
||||
let compressed = z.finish()?;
|
||||
let mut output = output.lock().unwrap();
|
||||
output.write_u64::<LittleEndian>(compressed.len() as u64)?;
|
||||
output.write_all(&compressed)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_header<W>(mut w: W, nr_blocks: u64) -> io::Result<()>
|
||||
where
|
||||
W: byteorder::WriteBytesExt,
|
||||
{
|
||||
w.write_u64::<LittleEndian>(MAGIC)?;
|
||||
w.write_u64::<LittleEndian>(PACK_VERSION)?;
|
||||
w.write_u64::<LittleEndian>(4096)?;
|
||||
w.write_u64::<LittleEndian>(nr_blocks)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn read_header<R>(mut r: R) -> io::Result<u64>
|
||||
where
|
||||
R: byteorder::ReadBytesExt,
|
||||
{
|
||||
use std::process::exit;
|
||||
|
||||
let magic = r.read_u64::<LittleEndian>()?;
|
||||
if magic != MAGIC {
|
||||
eprintln!("Not a pack file.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let version = r.read_u64::<LittleEndian>()?;
|
||||
if version != PACK_VERSION {
|
||||
eprintln!("unsupported pack file version ({}).", PACK_VERSION);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
let block_size = r.read_u64::<LittleEndian>()?;
|
||||
if block_size != BLOCK_SIZE {
|
||||
eprintln!("block size is not {}", BLOCK_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
r.read_u64::<LittleEndian>()
|
||||
}
|
||||
|
||||
fn get_nr_blocks(path: &Path) -> io::Result<u64> {
|
||||
let len = file_utils::file_size(path)?;
|
||||
Ok(len / (BLOCK_SIZE as u64))
|
||||
}
|
||||
|
||||
fn read_blocks<R>(rdr: &mut R, b: u64, count: u64) -> io::Result<Vec<u8>>
|
||||
where
|
||||
R: io::Read + io::Seek,
|
||||
{
|
||||
let mut buf: Vec<u8> = vec![0; (BLOCK_SIZE * count) as usize];
|
||||
|
||||
rdr.seek(io::SeekFrom::Start(b * BLOCK_SIZE))?;
|
||||
rdr.read_exact(&mut buf)?;
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
fn checksum(buf: &[u8]) -> u32 {
|
||||
crc32c::crc32c(&buf[4..]) ^ 0xffffffff
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum BT {
|
||||
SUPERBLOCK,
|
||||
NODE,
|
||||
INDEX,
|
||||
BITMAP,
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
fn metadata_block_type(buf: &[u8]) -> BT {
|
||||
if buf.len() != BLOCK_SIZE as usize {
|
||||
return BT::UNKNOWN;
|
||||
}
|
||||
|
||||
// The checksum is always stored in the first u32 of the buffer.
|
||||
let mut rdr = Cursor::new(buf);
|
||||
let sum_on_disk = rdr.read_u32::<LittleEndian>().unwrap();
|
||||
let csum = checksum(buf);
|
||||
let btype = csum ^ sum_on_disk;
|
||||
|
||||
match btype {
|
||||
SUPERBLOCK_CSUM_XOR => BT::SUPERBLOCK,
|
||||
BTREE_CSUM_XOR => BT::NODE,
|
||||
BITMAP_CSUM_XOR => BT::BITMAP,
|
||||
INDEX_CSUM_XOR => BT::INDEX,
|
||||
_ => BT::UNKNOWN,
|
||||
}
|
||||
}
|
||||
|
||||
fn check<T>(r: &PResult<T>) {
|
||||
match r {
|
||||
Ok(_) => {}
|
||||
Err(PackError::ParseError) => panic!("parse error"),
|
||||
Err(PackError::IOError) => panic!("io error"),
|
||||
}
|
||||
}
|
||||
|
||||
fn pack_block<W: Write>(w: &mut W, kind: BT, buf: &[u8]) {
|
||||
match kind {
|
||||
BT::SUPERBLOCK => check(&pack_superblock(w, buf)),
|
||||
BT::NODE => check(&pack_btree_node(w, buf)),
|
||||
BT::INDEX => check(&pack_index(w, buf)),
|
||||
BT::BITMAP => check(&pack_bitmap(w, buf)),
|
||||
BT::UNKNOWN => panic!("asked to pack an unknown block type"),
|
||||
}
|
||||
}
|
||||
|
||||
fn write_zero_block<W>(w: &mut W, b: u64) -> io::Result<()>
|
||||
where
|
||||
W: Write + Seek,
|
||||
{
|
||||
let zeroes: Vec<u8> = vec![0; BLOCK_SIZE as usize];
|
||||
w.seek(io::SeekFrom::Start(b * BLOCK_SIZE))?;
|
||||
w.write_all(&zeroes)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_blocks<W>(w: &Arc<Mutex<W>>, blocks: &mut Vec<(u64, Vec<u8>)>) -> io::Result<()>
|
||||
where
|
||||
W: Write + Seek,
|
||||
{
|
||||
let mut w = w.lock().unwrap();
|
||||
while let Some((b, block)) = blocks.pop() {
|
||||
w.seek(io::SeekFrom::Start(b * BLOCK_SIZE))?;
|
||||
w.write_all(&block[0..])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_worker<W>(rx: Receiver<Vec<u8>>, w: Arc<Mutex<W>>) -> io::Result<()>
|
||||
where
|
||||
W: Write + Seek,
|
||||
{
|
||||
let mut blocks = Vec::new();
|
||||
|
||||
while let Ok(bytes) = rx.recv() {
|
||||
let mut z = ZlibDecoder::new(&bytes[0..]);
|
||||
|
||||
while let Ok(b) = z.read_u64::<LittleEndian>() {
|
||||
let block = crate::pack::vm::unpack(&mut z, BLOCK_SIZE as usize).unwrap();
|
||||
assert!(metadata_block_type(&block[0..]) != BT::UNKNOWN);
|
||||
blocks.push((b, block));
|
||||
|
||||
if blocks.len() >= 32 {
|
||||
write_blocks(&w, &mut blocks)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write_blocks(&w, &mut blocks)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unpack(input_file: &Path, output_file: &Path) -> Result<(), Box<dyn Error>> {
|
||||
let mut input = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(input_file)?;
|
||||
|
||||
let mut output = OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.open(output_file)?;
|
||||
|
||||
let nr_blocks = read_header(&input)?;
|
||||
|
||||
// zero the last block to size the file
|
||||
write_zero_block(&mut output, nr_blocks - 1)?;
|
||||
|
||||
// Run until we hit the end
|
||||
let output = Arc::new(Mutex::new(output));
|
||||
|
||||
// kick off the workers
|
||||
let nr_jobs = num_cpus::get();
|
||||
let mut senders = Vec::new();
|
||||
let mut threads = Vec::new();
|
||||
|
||||
for _ in 0..nr_jobs {
|
||||
let (tx, rx) = sync_channel(1);
|
||||
let output = Arc::clone(&output);
|
||||
senders.push(tx);
|
||||
threads.push(spawn(move || decode_worker(rx, output)));
|
||||
}
|
||||
|
||||
// Read z compressed chunk, and hand to worker thread.
|
||||
let mut next_worker = 0;
|
||||
while let Ok(len) = input.read_u64::<LittleEndian>() {
|
||||
let mut bytes = vec![0; len as usize];
|
||||
input.read_exact(&mut bytes)?;
|
||||
senders[next_worker].send(bytes).unwrap();
|
||||
next_worker = (next_worker + 1) % nr_jobs;
|
||||
}
|
||||
|
||||
for s in senders {
|
||||
drop(s);
|
||||
}
|
||||
|
||||
for t in threads {
|
||||
t.join().unwrap()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
490
src/pack/vm.rs
490
src/pack/vm.rs
@ -1,490 +0,0 @@
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::io;
|
||||
use std::io::{Cursor, Read, Write};
|
||||
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::pack::delta_list::*;
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
// Deltas are converted to instructions. A delta may not fit
|
||||
// into a single instruction.
|
||||
#[derive(Debug, FromPrimitive)]
|
||||
enum Tag {
|
||||
Set, // Operand width given in nibble
|
||||
|
||||
Pos, // Delta in nibble
|
||||
PosW, // Delta in operand, whose width is in nibble
|
||||
|
||||
Neg, // Delta in nibble
|
||||
NegW, // Delta in operand, whose width is in nibble
|
||||
|
||||
Const, // Count in nibble
|
||||
Const8, // count = (nibble << 8) | byte
|
||||
|
||||
// Controls how many times the next instruction is applied.
|
||||
// Not applicable to Const instructions which hold their own count.
|
||||
Count, // count stored in nibble
|
||||
Count8, // count = (nibble << 8) | byte
|
||||
|
||||
Lit, // len in nibble
|
||||
LitW,
|
||||
|
||||
ShiftedRun,
|
||||
}
|
||||
|
||||
fn pack_tag<W: Write>(w: &mut W, t: Tag, nibble: u8) -> io::Result<()> {
|
||||
assert!(nibble < 16);
|
||||
let mut b: u8 = t as u8;
|
||||
assert!(b < 16);
|
||||
b = (b << 4) | nibble;
|
||||
w.write_u8(b)
|
||||
}
|
||||
|
||||
fn pack_count<W>(w: &mut W, count: u64) -> io::Result<()>
|
||||
where
|
||||
W: Write,
|
||||
{
|
||||
if count == 1u64 {
|
||||
Ok(())
|
||||
} else if count < 16 {
|
||||
pack_tag(w, Tag::Count, count as u8)
|
||||
} else {
|
||||
assert!(count < 4096);
|
||||
let nibble = count >> 8;
|
||||
assert!(nibble < 16);
|
||||
let byte = count & 0xff;
|
||||
pack_tag(w, Tag::Count8, nibble as u8)?;
|
||||
w.write_u8(byte as u8)
|
||||
}
|
||||
}
|
||||
|
||||
fn pack_delta<W: Write>(w: &mut W, d: &Delta) -> io::Result<()> {
|
||||
use Tag::*;
|
||||
|
||||
match d {
|
||||
Delta::Base { n } => {
|
||||
if *n <= std::u8::MAX as u64 {
|
||||
pack_tag(w, Set, 1)?;
|
||||
w.write_u8(*n as u8)
|
||||
} else if *n <= std::u16::MAX as u64 {
|
||||
pack_tag(w, Set, 2)?;
|
||||
w.write_u16::<LittleEndian>(*n as u16)
|
||||
} else if *n <= u32::MAX as u64 {
|
||||
pack_tag(w, Set, 4)?;
|
||||
w.write_u32::<LittleEndian>(*n as u32)
|
||||
} else {
|
||||
pack_tag(w, Set, 8)?;
|
||||
w.write_u64::<LittleEndian>(*n)
|
||||
}
|
||||
}
|
||||
Delta::Pos { delta, count } => {
|
||||
pack_count(w, *count)?;
|
||||
if *delta < 16 {
|
||||
pack_tag(w, Tag::Pos, *delta as u8)
|
||||
} else if *delta <= u8::MAX as u64 {
|
||||
pack_tag(w, PosW, 1)?;
|
||||
w.write_u8(*delta as u8)
|
||||
} else if *delta <= u16::MAX as u64 {
|
||||
pack_tag(w, PosW, 2)?;
|
||||
w.write_u16::<LittleEndian>(*delta as u16)
|
||||
} else if *delta <= u32::MAX as u64 {
|
||||
pack_tag(w, PosW, 4)?;
|
||||
w.write_u32::<LittleEndian>(*delta as u32)
|
||||
} else {
|
||||
pack_tag(w, PosW, 8)?;
|
||||
w.write_u64::<LittleEndian>(*delta as u64)
|
||||
}
|
||||
}
|
||||
Delta::Neg { delta, count } => {
|
||||
pack_count(w, *count)?;
|
||||
|
||||
if *delta < 16 {
|
||||
pack_tag(w, Neg, *delta as u8)
|
||||
} else if *delta <= u8::MAX as u64 {
|
||||
pack_tag(w, NegW, 1)?;
|
||||
w.write_u8(*delta as u8)
|
||||
} else if *delta <= u16::MAX as u64 {
|
||||
pack_tag(w, NegW, 2)?;
|
||||
w.write_u16::<LittleEndian>(*delta as u16)
|
||||
} else if *delta <= u32::MAX as u64 {
|
||||
pack_tag(w, NegW, 4)?;
|
||||
w.write_u32::<LittleEndian>(*delta as u32)
|
||||
} else {
|
||||
pack_tag(w, NegW, 8)?;
|
||||
w.write_u64::<LittleEndian>(*delta as u64)
|
||||
}
|
||||
}
|
||||
Delta::Const { count } => {
|
||||
if *count < 16 {
|
||||
pack_tag(w, Tag::Const, *count as u8)
|
||||
} else {
|
||||
assert!(*count < 4096);
|
||||
let nibble = *count >> 8;
|
||||
assert!(nibble < 16);
|
||||
pack_tag(w, Tag::Const8, nibble as u8)?;
|
||||
w.write_u8((*count & 0xff) as u8)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pack_deltas<W: Write>(w: &mut W, ds: &[Delta]) -> io::Result<()> {
|
||||
for d in ds {
|
||||
pack_delta(w, d)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
pub fn pack_u64s<W: Write>(w: &mut W, ns: &[u64]) -> io::Result<()> {
|
||||
let ds = to_delta(ns);
|
||||
pack_deltas(w, &ds[0..])
|
||||
}
|
||||
|
||||
fn unshift_nrs(shift: usize, ns: &[u64]) -> (Vec<u64>, Vec<u64>) {
|
||||
let mut values = Vec::new();
|
||||
let mut shifts = Vec::new();
|
||||
|
||||
let mask = (1 << shift) - 1;
|
||||
for n in ns {
|
||||
values.push(n >> shift);
|
||||
shifts.push(n & mask);
|
||||
}
|
||||
|
||||
(values, shifts)
|
||||
}
|
||||
|
||||
pub fn pack_shifted_u64s<W: Write>(w: &mut W, ns: &[u64]) -> io::Result<()> {
|
||||
let len = ns.len();
|
||||
let nibble = len >> 8;
|
||||
assert!(nibble < 16);
|
||||
pack_tag(w, Tag::ShiftedRun, nibble as u8)?;
|
||||
w.write_u8((len & 0xff) as u8)?;
|
||||
let (high, low) = unshift_nrs(24, ns);
|
||||
pack_u64s(w, &high[0..])?;
|
||||
pack_u64s(w, &low[0..])
|
||||
}
|
||||
|
||||
pub fn pack_literal<W: Write>(w: &mut W, bs: &[u8]) -> io::Result<()> {
|
||||
use Tag::LitW;
|
||||
|
||||
let len = bs.len() as u64;
|
||||
if len < 16 as u64 {
|
||||
pack_tag(w, Tag::Lit, len as u8)?;
|
||||
} else if len <= u8::MAX as u64 {
|
||||
pack_tag(w, LitW, 1)?;
|
||||
w.write_u8(len as u8)?;
|
||||
} else if len <= u16::MAX as u64 {
|
||||
pack_tag(w, LitW, 2)?;
|
||||
w.write_u16::<LittleEndian>(len as u16)?;
|
||||
} else if len <= u32::MAX as u64 {
|
||||
pack_tag(w, LitW, 4)?;
|
||||
w.write_u32::<LittleEndian>(len as u32)?;
|
||||
} else {
|
||||
pack_tag(w, LitW, 8)?;
|
||||
w.write_u64::<LittleEndian>(len as u64)?;
|
||||
}
|
||||
w.write_all(bs)
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
fn unpack_with_width<R: Read>(r: &mut R, nibble: u8) -> io::Result<u64> {
|
||||
let v = match nibble {
|
||||
1 => r.read_u8()? as u64,
|
||||
2 => r.read_u16::<LittleEndian>()? as u64,
|
||||
4 => r.read_u32::<LittleEndian>()? as u64,
|
||||
8 => r.read_u64::<LittleEndian>()? as u64,
|
||||
_ => {
|
||||
panic!("SET with bad width");
|
||||
}
|
||||
};
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn unpack_u64s<R: Read>(r: &mut R, count: usize) -> io::Result<Vec<u64>> {
|
||||
let mut v = Vec::new();
|
||||
for _ in 0..count {
|
||||
let n = r.read_u64::<LittleEndian>()?;
|
||||
v.push(n);
|
||||
}
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
struct VM {
|
||||
base: u64,
|
||||
bytes_written: usize,
|
||||
}
|
||||
|
||||
impl VM {
|
||||
fn new() -> VM {
|
||||
VM {
|
||||
base: 0,
|
||||
bytes_written: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_u64<W: Write>(&mut self, w: &mut W, n: u64) -> io::Result<()> {
|
||||
w.write_u64::<LittleEndian>(n)?;
|
||||
self.bytes_written += 8;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn emit_base<W: Write>(&mut self, w: &mut W) -> io::Result<()> {
|
||||
self.emit_u64(w, self.base)
|
||||
}
|
||||
|
||||
fn emit_bytes<W: Write>(&mut self, w: &mut W, bytes: &[u8]) -> io::Result<()> {
|
||||
let len = bytes.len();
|
||||
w.write_all(bytes)?;
|
||||
self.bytes_written += len;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn unpack_instr<R: Read, W: Write>(
|
||||
&mut self,
|
||||
r: &mut R,
|
||||
w: &mut W,
|
||||
count: usize,
|
||||
) -> io::Result<()> {
|
||||
use Tag::*;
|
||||
|
||||
let b = r.read_u8()?;
|
||||
let kind: Tag = match Tag::from_u8(b >> 4) {
|
||||
Some(k) => k,
|
||||
None => {
|
||||
panic!("bad tag");
|
||||
}
|
||||
};
|
||||
let nibble = b & 0xf;
|
||||
|
||||
match kind {
|
||||
Set => {
|
||||
self.base = unpack_with_width(r, nibble)?;
|
||||
for _ in 0..count {
|
||||
self.emit_base(w)?;
|
||||
}
|
||||
}
|
||||
Pos => {
|
||||
for _ in 0..count {
|
||||
self.base += nibble as u64;
|
||||
self.emit_base(w)?;
|
||||
}
|
||||
}
|
||||
PosW => {
|
||||
let delta = unpack_with_width(r, nibble)?;
|
||||
for _ in 0..count {
|
||||
self.base += delta;
|
||||
self.emit_base(w)?;
|
||||
}
|
||||
}
|
||||
Neg => {
|
||||
for _ in 0..count {
|
||||
self.base -= nibble as u64;
|
||||
self.emit_base(w)?;
|
||||
}
|
||||
}
|
||||
NegW => {
|
||||
let delta = unpack_with_width(r, nibble)?;
|
||||
for _ in 0..count {
|
||||
self.base -= delta;
|
||||
self.emit_base(w)?;
|
||||
}
|
||||
}
|
||||
Const => {
|
||||
assert_eq!(count, 1);
|
||||
for _ in 0..nibble as usize {
|
||||
self.emit_base(w)?;
|
||||
}
|
||||
}
|
||||
Const8 => {
|
||||
assert_eq!(count, 1);
|
||||
let count = ((nibble as usize) << 8) | (r.read_u8()? as usize);
|
||||
for _ in 0..count {
|
||||
self.emit_base(w)?;
|
||||
}
|
||||
}
|
||||
Count => {
|
||||
self.unpack_instr(r, w, nibble as usize)?;
|
||||
}
|
||||
Count8 => {
|
||||
let count = ((nibble as usize) << 8) | (r.read_u8()? as usize);
|
||||
self.unpack_instr(r, w, count as usize)?;
|
||||
}
|
||||
Lit => {
|
||||
assert_eq!(count, 1);
|
||||
let len = nibble as usize;
|
||||
let mut bytes = vec![0; len];
|
||||
r.read_exact(&mut bytes[0..])?;
|
||||
self.emit_bytes(w, &bytes)?;
|
||||
}
|
||||
LitW => {
|
||||
assert_eq!(count, 1);
|
||||
let len = unpack_with_width(r, nibble)? as usize;
|
||||
let mut bytes = vec![0; len];
|
||||
r.read_exact(&mut bytes[0..])?;
|
||||
self.emit_bytes(w, &bytes)?;
|
||||
}
|
||||
ShiftedRun => {
|
||||
// FIXME: repeated unpack, pack, unpack
|
||||
let len = ((nibble as usize) << 8) | (r.read_u8()? as usize);
|
||||
let nr_bytes = (len as usize) * std::mem::size_of::<u64>() as usize;
|
||||
|
||||
let mut high_bytes: Vec<u8> = Vec::with_capacity(nr_bytes);
|
||||
let written = self.exec(r, &mut high_bytes, nr_bytes)?;
|
||||
self.bytes_written -= written; // hack
|
||||
let mut high_r = Cursor::new(high_bytes);
|
||||
let high = unpack_u64s(&mut high_r, len)?;
|
||||
|
||||
let mut low_bytes: Vec<u8> = Vec::with_capacity(nr_bytes);
|
||||
let written = self.exec(r, &mut low_bytes, nr_bytes)?;
|
||||
self.bytes_written -= written; // hack
|
||||
let mut low_r = Cursor::new(low_bytes);
|
||||
let low = unpack_u64s(&mut low_r, len)?;
|
||||
|
||||
let mask = (1 << 24) - 1;
|
||||
for i in 0..len {
|
||||
self.emit_u64(w, (high[i] << 24) | (low[i] & mask))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Runs until at least a number of bytes have been emitted. Returns nr emitted.
|
||||
fn exec<R: Read, W: Write>(
|
||||
&mut self,
|
||||
r: &mut R,
|
||||
w: &mut W,
|
||||
emit_bytes: usize,
|
||||
) -> io::Result<usize> {
|
||||
let begin = self.bytes_written;
|
||||
while (self.bytes_written - begin) < emit_bytes {
|
||||
self.unpack_instr(r, w, 1)?;
|
||||
}
|
||||
Ok(self.bytes_written - begin)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unpack<R: Read>(r: &mut R, count: usize) -> io::Result<Vec<u8>> {
|
||||
let mut w = Vec::with_capacity(4096);
|
||||
let mut cursor = Cursor::new(&mut w);
|
||||
|
||||
let mut vm = VM::new();
|
||||
let written = vm.exec(r, &mut cursor, count)?;
|
||||
|
||||
assert_eq!(w.len(), count);
|
||||
assert_eq!(written, count);
|
||||
Ok(w)
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_pack_literals() {
|
||||
struct TestCase(Vec<u8>);
|
||||
|
||||
let cases = [
|
||||
// This is a bad test case, because unpack will not exec
|
||||
// any instructions.
|
||||
TestCase(b"".to_vec()),
|
||||
TestCase(b"foo".to_vec()),
|
||||
TestCase(vec![42; 15]),
|
||||
TestCase(vec![42; 256]),
|
||||
TestCase(vec![42; 4096]),
|
||||
];
|
||||
|
||||
for t in &cases {
|
||||
let mut bs = Vec::with_capacity(4096);
|
||||
|
||||
let mut w = Cursor::new(&mut bs);
|
||||
pack_literal(&mut w, &t.0[0..]).unwrap();
|
||||
|
||||
let mut r = Cursor::new(&mut bs);
|
||||
let unpacked = unpack(&mut r, t.0.len()).unwrap();
|
||||
|
||||
assert_eq!(&t.0[0..], &unpacked[0..]);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_u64s_match(ns: &Vec<u64>, bytes: &[u8]) -> bool {
|
||||
let mut packed = Vec::with_capacity(ns.len() * 8);
|
||||
let mut w = Cursor::new(&mut packed);
|
||||
for n in ns {
|
||||
w.write_u64::<LittleEndian>(*n).unwrap();
|
||||
}
|
||||
packed == bytes
|
||||
}
|
||||
|
||||
fn check_pack_u64s(ns: &Vec<u64>) -> bool {
|
||||
println!("packing {:?}", &ns);
|
||||
let mut bs = Vec::with_capacity(4096);
|
||||
|
||||
let mut w = Cursor::new(&mut bs);
|
||||
pack_u64s(&mut w, &ns[0..]).unwrap();
|
||||
println!("unpacked len = {}, packed len = {}", ns.len() * 8, bs.len());
|
||||
|
||||
let mut r = Cursor::new(&mut bs);
|
||||
let unpacked = unpack(&mut r, ns.len() * 8).unwrap();
|
||||
|
||||
check_u64s_match(&ns, &unpacked[0..])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pack_u64s() {
|
||||
let cases = [
|
||||
vec![0],
|
||||
vec![1, 5, 9, 10],
|
||||
b"the quick brown fox jumps over the lazy dog"
|
||||
.iter()
|
||||
.map(|b| *b as u64)
|
||||
.collect(),
|
||||
];
|
||||
|
||||
for t in &cases {
|
||||
assert!(check_pack_u64s(&t));
|
||||
}
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_pack_u64s(mut ns: Vec<u64>) -> bool {
|
||||
ns.push(42); // We don't handle empty vecs
|
||||
check_pack_u64s(&ns)
|
||||
}
|
||||
|
||||
fn check_pack_shifted_u64s(ns: &Vec<(u64, u64)>) -> bool {
|
||||
let shifted: Vec<u64> = ns
|
||||
.iter()
|
||||
.map(|(h, l)| (h << 24) | (l & ((1 << 24) - 1)))
|
||||
.collect();
|
||||
|
||||
println!("packing {:?}", &ns);
|
||||
let mut bs = Vec::with_capacity(4096);
|
||||
|
||||
let mut w = Cursor::new(&mut bs);
|
||||
pack_shifted_u64s(&mut w, &shifted[0..]).unwrap();
|
||||
println!("unpacked len = {}, packed len = {}", ns.len() * 8, bs.len());
|
||||
|
||||
let mut r = Cursor::new(&mut bs);
|
||||
let unpacked = unpack(&mut r, ns.len() * 8).unwrap();
|
||||
|
||||
check_u64s_match(&shifted, &unpacked[0..])
|
||||
}
|
||||
|
||||
#[quickcheck]
|
||||
fn prop_pack_shifted_u64s(mut ns: Vec<(u64, u64)>) -> bool {
|
||||
ns.push((42, 42));
|
||||
check_pack_shifted_u64s(&ns)
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
@ -1,61 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use std::fs::OpenOptions;
|
||||
use std::path::Path;
|
||||
use std::io::{Seek, SeekFrom, Write, Read};
|
||||
//use std::os::unix::fs::OpenOptionsExt;
|
||||
|
||||
pub type Sector = u64;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Region {
|
||||
pub src: Sector,
|
||||
pub dest: Sector,
|
||||
pub len: Sector,
|
||||
}
|
||||
|
||||
|
||||
fn copy_step<W>(file: &mut W, src_byte: u64, dest_byte: u64, len: usize) -> Result<()>
|
||||
where
|
||||
W: Write + Seek + Read,
|
||||
{
|
||||
let mut buf = vec![0; len];
|
||||
file.seek(SeekFrom::Start(src_byte))?;
|
||||
file.read_exact(&mut buf)?;
|
||||
file.seek(SeekFrom::Start(dest_byte))?;
|
||||
file.write_all(&buf)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn copy_region<W>(file: &mut W, r: &Region) -> Result<()>
|
||||
where
|
||||
W: Write + Seek + Read,
|
||||
{
|
||||
const MAX_BYTES: Sector = 1024 * 1024 * 64;
|
||||
|
||||
let src_bytes = r.src * 512;
|
||||
let dest_bytes = r.dest * 512;
|
||||
let len_bytes = r.len * 512;
|
||||
let mut written = 0;
|
||||
while written != len_bytes {
|
||||
let step = u64::min(len_bytes - written, MAX_BYTES);
|
||||
copy_step(file, src_bytes + written, dest_bytes + written, step as usize)?;
|
||||
written += step;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn copy(path: &Path, regions: &[Region]) -> Result<()> {
|
||||
let mut input = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(true)
|
||||
//.custom_flags(libc::O_DIRECT)
|
||||
.open(path)?;
|
||||
|
||||
for r in regions {
|
||||
eprintln!("copying {:?}", r);
|
||||
copy_region(&mut input, r)?;
|
||||
}
|
||||
input.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
pub mod toplevel;
|
||||
|
||||
mod copier;
|
@ -1,528 +0,0 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use fixedbitset::FixedBitSet;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::shrink::copier::{self, Region};
|
||||
use crate::thin::xml::{self, Visit};
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Pass1 {
|
||||
// FIXME: Inefficient, use a range_set of some description
|
||||
allocated_blocks: FixedBitSet,
|
||||
|
||||
nr_blocks: u64,
|
||||
|
||||
/// High blocks are beyond the new, reduced end of the pool. These
|
||||
/// will need to be moved.
|
||||
nr_high_blocks: u64,
|
||||
block_size: Option<u64>,
|
||||
}
|
||||
|
||||
impl Pass1 {
|
||||
fn new(nr_blocks: u64) -> Pass1 {
|
||||
Pass1 {
|
||||
allocated_blocks: FixedBitSet::with_capacity(0),
|
||||
nr_blocks,
|
||||
nr_high_blocks: 0,
|
||||
block_size: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl xml::MetadataVisitor for Pass1 {
|
||||
fn superblock_b(&mut self, sb: &xml::Superblock) -> Result<Visit> {
|
||||
self.allocated_blocks.grow(sb.nr_data_blocks as usize);
|
||||
self.block_size = Some(sb.data_block_size as u64);
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn superblock_e(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_b(&mut self, _d: &xml::Device) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_e(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn map(&mut self, m: &xml::Map) -> Result<Visit> {
|
||||
for i in m.data_begin..(m.data_begin + m.len) {
|
||||
if i > self.nr_blocks {
|
||||
self.nr_high_blocks += 1;
|
||||
}
|
||||
self.allocated_blocks.insert(i as usize);
|
||||
}
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
// Writes remapped xml
|
||||
struct Pass2<W: Write> {
|
||||
writer: xml::XmlWriter<W>,
|
||||
nr_blocks: u64,
|
||||
remaps: Vec<(BlockRange, BlockRange)>,
|
||||
}
|
||||
|
||||
impl<W: Write> Pass2<W> {
|
||||
fn new(w: W, nr_blocks: u64, remaps: Vec<(BlockRange, BlockRange)>) -> Pass2<W> {
|
||||
Pass2 {
|
||||
writer: xml::XmlWriter::new(w),
|
||||
nr_blocks,
|
||||
remaps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: Write> xml::MetadataVisitor for Pass2<W> {
|
||||
fn superblock_b(&mut self, sb: &xml::Superblock) -> Result<Visit> {
|
||||
self.writer.superblock_b(sb)
|
||||
}
|
||||
|
||||
fn superblock_e(&mut self) -> Result<Visit> {
|
||||
self.writer.superblock_e()
|
||||
}
|
||||
|
||||
fn device_b(&mut self, d: &xml::Device) -> Result<Visit> {
|
||||
self.writer.device_b(d)
|
||||
}
|
||||
|
||||
fn device_e(&mut self) -> Result<Visit> {
|
||||
self.writer.device_e()
|
||||
}
|
||||
|
||||
fn map(&mut self, m: &xml::Map) -> Result<Visit> {
|
||||
if m.data_begin + m.len < self.nr_blocks {
|
||||
// no remapping needed.
|
||||
self.writer.map(m)?;
|
||||
} else {
|
||||
let r = m.data_begin..(m.data_begin + m.len);
|
||||
let remaps = remap(&r, &self.remaps);
|
||||
let mut written = 0;
|
||||
|
||||
for r in remaps {
|
||||
self.writer.map(&xml::Map {
|
||||
thin_begin: m.thin_begin + written,
|
||||
data_begin: r.start,
|
||||
time: m.time,
|
||||
len: range_len(&r),
|
||||
})?;
|
||||
written += range_len(&r);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<Visit> {
|
||||
self.writer.eof()
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
type BlockRange = std::ops::Range<u64>;
|
||||
|
||||
fn bits_to_ranges(bits: &FixedBitSet) -> Vec<BlockRange> {
|
||||
let mut ranges = Vec::new();
|
||||
let mut start = None;
|
||||
|
||||
for i in 0..bits.len() {
|
||||
match (bits[i], start) {
|
||||
(false, None) => {}
|
||||
(true, None) => {
|
||||
start = Some((i as u64, 1));
|
||||
}
|
||||
(false, Some((b, len))) => {
|
||||
ranges.push(b..(b + len));
|
||||
start = None;
|
||||
}
|
||||
(true, Some((b, len))) => {
|
||||
start = Some((b, len + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((b, len)) = start {
|
||||
ranges.push(b..(b + len));
|
||||
}
|
||||
|
||||
ranges
|
||||
}
|
||||
|
||||
// Splits the ranges into those below threshold, and those equal or
|
||||
// above threshold below threshold, and those equal or above threshold
|
||||
fn ranges_split(ranges: &[BlockRange], threshold: u64) -> (Vec<BlockRange>, Vec<BlockRange>) {
|
||||
use std::ops::Range;
|
||||
|
||||
let mut below = Vec::new();
|
||||
let mut above = Vec::new();
|
||||
for r in ranges {
|
||||
match r {
|
||||
Range { start, end } if *end <= threshold => below.push(*start..*end),
|
||||
Range { start, end } if *start < threshold => {
|
||||
below.push(*start..threshold);
|
||||
above.push(threshold..*end);
|
||||
}
|
||||
Range { start, end } => above.push(*start..*end),
|
||||
}
|
||||
}
|
||||
(below, above)
|
||||
}
|
||||
|
||||
fn negate_ranges(ranges: &[BlockRange], upper_limit: u64) -> Vec<BlockRange> {
|
||||
use std::ops::Range;
|
||||
|
||||
let mut result = Vec::new();
|
||||
let mut cursor = 0;
|
||||
|
||||
for r in ranges {
|
||||
match r {
|
||||
Range { start, end } if cursor < *start => {
|
||||
result.push(cursor..*start);
|
||||
cursor = *end;
|
||||
}
|
||||
Range { start: _, end } => {
|
||||
cursor = *end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cursor < upper_limit {
|
||||
result.push(cursor..upper_limit);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn range_len(r: &BlockRange) -> u64 {
|
||||
r.end - r.start
|
||||
}
|
||||
|
||||
fn ranges_total(rs: &[BlockRange]) -> u64 {
|
||||
rs.iter().fold(0, |sum, r| sum + range_len(r))
|
||||
}
|
||||
|
||||
// Assumes there is enough space to remap.
|
||||
fn build_remaps(ranges: Vec<BlockRange>, free: Vec<BlockRange>) -> Vec<(BlockRange, BlockRange)> {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
let mut remap = Vec::new();
|
||||
let mut range_iter = ranges.into_iter();
|
||||
let mut free_iter = free.into_iter();
|
||||
|
||||
let mut r_ = range_iter.next();
|
||||
let mut f_ = free_iter.next();
|
||||
|
||||
while let (Some(r), Some(f)) = (r_, f_) {
|
||||
let rlen = range_len(&r);
|
||||
let flen = range_len(&f);
|
||||
|
||||
match rlen.cmp(&flen) {
|
||||
Ordering::Less => {
|
||||
// range fits into the free chunk
|
||||
remap.push((r, f.start..(f.start + rlen)));
|
||||
f_ = Some((f.start + rlen)..f.end);
|
||||
r_ = range_iter.next();
|
||||
}
|
||||
Ordering::Equal => {
|
||||
remap.push((r, f));
|
||||
r_ = range_iter.next();
|
||||
f_ = free_iter.next();
|
||||
}
|
||||
Ordering::Greater => {
|
||||
remap.push((r.start..(r.start + flen), f));
|
||||
r_ = Some((r.start + flen)..r.end);
|
||||
f_ = free_iter.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remap
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_build_remaps() {
|
||||
struct Test {
|
||||
ranges: Vec<BlockRange>,
|
||||
free: Vec<BlockRange>,
|
||||
result: Vec<(BlockRange, BlockRange)>,
|
||||
}
|
||||
|
||||
let tests = vec![
|
||||
Test {
|
||||
ranges: vec![],
|
||||
free: vec![],
|
||||
result: vec![],
|
||||
},
|
||||
Test {
|
||||
ranges: vec![],
|
||||
free: vec![0..100],
|
||||
result: vec![],
|
||||
},
|
||||
Test {
|
||||
ranges: vec![1000..1002],
|
||||
free: vec![0..100],
|
||||
result: vec![(1000..1002, 0..2)],
|
||||
},
|
||||
Test {
|
||||
ranges: vec![1000..1002, 1100..1110],
|
||||
free: vec![0..100],
|
||||
result: vec![(1000..1002, 0..2), (1100..1110, 2..12)],
|
||||
},
|
||||
Test {
|
||||
ranges: vec![100..120],
|
||||
free: vec![0..5, 20..23, 30..50],
|
||||
result: vec![(100..105, 0..5), (105..108, 20..23), (108..120, 30..42)],
|
||||
},
|
||||
];
|
||||
|
||||
for t in tests {
|
||||
assert_eq!(build_remaps(t.ranges, t.free), t.result);
|
||||
}
|
||||
}
|
||||
|
||||
fn overlaps(r1: &BlockRange, r2: &BlockRange, index: usize) -> Option<usize> {
|
||||
if r1.start >= r2.end {
|
||||
return None;
|
||||
}
|
||||
|
||||
if r2.start >= r1.end {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(index)
|
||||
}
|
||||
|
||||
// Finds the index of the first entry that overlaps r.
|
||||
fn find_first(r: &BlockRange, remaps: &[(BlockRange, BlockRange)]) -> Option<usize> {
|
||||
if remaps.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
match remaps.binary_search_by_key(&r.start, |(from, _)| from.start) {
|
||||
Ok(n) => Some(n),
|
||||
Err(n) => {
|
||||
if n == 0 {
|
||||
let (from, _) = &remaps[n];
|
||||
overlaps(&r, &from, n)
|
||||
} else if n == remaps.len() {
|
||||
let (from, _) = &remaps[n - 1];
|
||||
overlaps(&r, from, n - 1)
|
||||
} else {
|
||||
// Need to check the previous entry
|
||||
let (from, _) = &remaps[n - 1];
|
||||
overlaps(&r, &from, n - 1).or_else(|| {
|
||||
let (from, _) = &remaps[n];
|
||||
overlaps(&r, &from, n)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_empty(r: &BlockRange) -> bool {
|
||||
r.start == r.end
|
||||
}
|
||||
|
||||
// remaps must be in sorted order by from.start.
|
||||
fn remap(r: &BlockRange, remaps: &[(BlockRange, BlockRange)]) -> Vec<BlockRange> {
|
||||
let mut remap = Vec::new();
|
||||
let mut r = r.start..r.end;
|
||||
|
||||
if let Some(index) = find_first(&r, &remaps) {
|
||||
let mut index = index;
|
||||
loop {
|
||||
let (from, to) = &remaps[index];
|
||||
|
||||
// There may be a prefix that doesn't overlap with 'from'
|
||||
if r.start < from.start {
|
||||
let len = u64::min(range_len(&r), from.start - r.start);
|
||||
remap.push(r.start..(r.start + len));
|
||||
r = (r.start + len)..r.end;
|
||||
|
||||
if is_empty(&r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let to = (to.start + (r.start - from.start))..to.end;
|
||||
let from = r.start..from.end;
|
||||
let rlen = range_len(&r);
|
||||
let flen = range_len(&from);
|
||||
|
||||
let len = u64::min(rlen, flen);
|
||||
remap.push(to.start..(to.start + len));
|
||||
|
||||
r = (r.start + len)..r.end;
|
||||
if is_empty(&r) {
|
||||
break;
|
||||
}
|
||||
|
||||
if len == flen {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if index == remaps.len() {
|
||||
remap.push(r.start..r.end);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
remap.push(r.start..r.end);
|
||||
}
|
||||
|
||||
remap
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn remap_test() {
|
||||
struct Test {
|
||||
remaps: Vec<(BlockRange, BlockRange)>,
|
||||
input: BlockRange,
|
||||
output: Vec<BlockRange>,
|
||||
}
|
||||
|
||||
let tests = [
|
||||
Test {
|
||||
remaps: vec![],
|
||||
input: 0..1,
|
||||
output: vec![0..1],
|
||||
},
|
||||
Test {
|
||||
remaps: vec![],
|
||||
input: 100..1000,
|
||||
output: vec![100..1000],
|
||||
},
|
||||
Test {
|
||||
remaps: vec![(10..20, 110..120)],
|
||||
input: 0..5,
|
||||
output: vec![0..5],
|
||||
},
|
||||
Test {
|
||||
remaps: vec![(10..20, 110..120)],
|
||||
input: 10..20,
|
||||
output: vec![110..120],
|
||||
},
|
||||
Test {
|
||||
remaps: vec![(10..20, 110..120)],
|
||||
input: 5..15,
|
||||
output: vec![5..10, 110..115],
|
||||
},
|
||||
Test {
|
||||
remaps: vec![(10..20, 110..120)],
|
||||
input: 5..25,
|
||||
output: vec![5..10, 110..120, 20..25],
|
||||
},
|
||||
Test {
|
||||
remaps: vec![(10..20, 110..120)],
|
||||
input: 15..25,
|
||||
output: vec![115..120, 20..25],
|
||||
},
|
||||
Test {
|
||||
remaps: vec![(10..20, 110..120)],
|
||||
input: 25..35,
|
||||
output: vec![25..35],
|
||||
},
|
||||
Test {
|
||||
remaps: vec![(10..20, 110..120), (30..40, 230..240)],
|
||||
input: 0..50,
|
||||
output: vec![0..10, 110..120, 20..30, 230..240, 40..50],
|
||||
},
|
||||
];
|
||||
|
||||
for t in &tests {
|
||||
let rs = remap(&t.input, &t.remaps);
|
||||
assert_eq!(rs, t.output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_copy_regions(remaps: &[(BlockRange, BlockRange)], block_size: u64) -> Vec<Region> {
|
||||
let mut rs = Vec::new();
|
||||
|
||||
for (from, to) in remaps {
|
||||
rs.push(Region {
|
||||
src: from.start * block_size,
|
||||
dest: to.start * block_size,
|
||||
len: range_len(&from) * block_size,
|
||||
});
|
||||
}
|
||||
|
||||
rs
|
||||
}
|
||||
|
||||
fn process_xml<MV: xml::MetadataVisitor>(input_path: &Path, pass: &mut MV) -> Result<()> {
|
||||
let input = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.custom_flags(libc::O_EXCL)
|
||||
.open(input_path)?;
|
||||
|
||||
xml::read(input, pass)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn shrink(
|
||||
input_path: &Path,
|
||||
output_path: &Path,
|
||||
data_path: &Path,
|
||||
nr_blocks: u64,
|
||||
do_copy: bool,
|
||||
) -> Result<()> {
|
||||
let mut pass1 = Pass1::new(nr_blocks);
|
||||
eprint!("Reading xml...");
|
||||
process_xml(input_path, &mut pass1)?;
|
||||
eprintln!("done");
|
||||
eprintln!("{} blocks need moving", pass1.nr_high_blocks);
|
||||
|
||||
let ranges = bits_to_ranges(&pass1.allocated_blocks);
|
||||
let (below, above) = ranges_split(&ranges, nr_blocks);
|
||||
|
||||
let free = negate_ranges(&below, nr_blocks);
|
||||
let free_blocks = ranges_total(&free);
|
||||
eprintln!("{} free blocks.", free_blocks);
|
||||
|
||||
if free_blocks < pass1.nr_high_blocks {
|
||||
return Err(anyhow!("Insufficient space"));
|
||||
}
|
||||
|
||||
let remaps = build_remaps(above, free);
|
||||
|
||||
if do_copy {
|
||||
let regions = build_copy_regions(&remaps, pass1.block_size.unwrap() as u64);
|
||||
copier::copy(data_path, ®ions)?;
|
||||
} else {
|
||||
eprintln!("skipping copy");
|
||||
}
|
||||
|
||||
let output = OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(output_path)?;
|
||||
let mut pass2 = Pass2::new(output, nr_blocks, remaps);
|
||||
eprint!("writing new xml...");
|
||||
process_xml(input_path, &mut pass2)?;
|
||||
eprintln!("done.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//---------------------------------------
|
@ -1 +0,0 @@
|
||||
pub mod xml;
|
413
src/thin/xml.rs
413
src/thin/xml.rs
@ -1,413 +0,0 @@
|
||||
use anyhow::Result;
|
||||
use std::{borrow::Cow, fmt::Display, io::prelude::*, io::BufReader, io::Write};
|
||||
|
||||
use quick_xml::events::attributes::Attribute;
|
||||
use quick_xml::events::{BytesEnd, BytesStart, Event};
|
||||
use quick_xml::{Reader, Writer};
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Superblock {
|
||||
pub uuid: String,
|
||||
pub time: u64,
|
||||
pub transaction: u64,
|
||||
pub flags: Option<u32>,
|
||||
pub version: Option<u32>,
|
||||
pub data_block_size: u32,
|
||||
pub nr_data_blocks: u64,
|
||||
pub metadata_snap: Option<u64>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Device {
|
||||
pub dev_id: u32,
|
||||
pub mapped_blocks: u64,
|
||||
pub transaction: u64,
|
||||
pub creation_time: u64,
|
||||
pub snap_time: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Map {
|
||||
pub thin_begin: u64,
|
||||
pub data_begin: u64,
|
||||
pub time: u32,
|
||||
pub len: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Visit {
|
||||
Continue,
|
||||
Stop,
|
||||
}
|
||||
|
||||
pub trait MetadataVisitor {
|
||||
fn superblock_b(&mut self, sb: &Superblock) -> Result<Visit>;
|
||||
fn superblock_e(&mut self) -> Result<Visit>;
|
||||
|
||||
fn device_b(&mut self, d: &Device) -> Result<Visit>;
|
||||
fn device_e(&mut self) -> Result<Visit>;
|
||||
|
||||
fn map(&mut self, m: &Map) -> Result<Visit>;
|
||||
|
||||
fn eof(&mut self) -> Result<Visit>;
|
||||
}
|
||||
|
||||
pub struct XmlWriter<W: Write> {
|
||||
w: Writer<W>,
|
||||
}
|
||||
|
||||
impl<W: Write> XmlWriter<W> {
|
||||
pub fn new(w: W) -> XmlWriter<W> {
|
||||
XmlWriter {
|
||||
w: Writer::new_with_indent(w, 0x20, 2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn mk_attr_<'a, T: Display>(n: T) -> Cow<'a, [u8]> {
|
||||
let str = format!("{}", n);
|
||||
Cow::Owned(str.into_bytes())
|
||||
}
|
||||
|
||||
fn mk_attr<T: Display>(key: &[u8], value: T) -> Attribute {
|
||||
Attribute {
|
||||
key,
|
||||
value: mk_attr_(value),
|
||||
}
|
||||
}
|
||||
|
||||
const XML_VERSION: u32 = 2;
|
||||
|
||||
impl<W: Write> MetadataVisitor for XmlWriter<W> {
|
||||
fn superblock_b(&mut self, sb: &Superblock) -> Result<Visit> {
|
||||
let tag = b"superblock";
|
||||
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||
elem.push_attribute(mk_attr(b"uuid", sb.uuid.clone()));
|
||||
elem.push_attribute(mk_attr(b"time", sb.time));
|
||||
elem.push_attribute(mk_attr(b"transaction", sb.transaction));
|
||||
if let Some(flags) = sb.flags {
|
||||
// FIXME: is this really a nr?
|
||||
elem.push_attribute(mk_attr(b"flags", flags));
|
||||
}
|
||||
|
||||
elem.push_attribute(mk_attr(b"version", XML_VERSION));
|
||||
elem.push_attribute(mk_attr(b"data_block_size", sb.data_block_size));
|
||||
elem.push_attribute(mk_attr(b"nr_data_blocks", sb.nr_data_blocks));
|
||||
|
||||
if let Some(snap) = sb.metadata_snap {
|
||||
elem.push_attribute(mk_attr(b"metadata_snap", snap));
|
||||
}
|
||||
|
||||
self.w.write_event(Event::Start(elem))?;
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn superblock_e(&mut self) -> Result<Visit> {
|
||||
self.w
|
||||
.write_event(Event::End(BytesEnd::borrowed(b"superblock")))?;
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_b(&mut self, d: &Device) -> Result<Visit> {
|
||||
let tag = b"device";
|
||||
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||
elem.push_attribute(mk_attr(b"dev_id", d.dev_id));
|
||||
elem.push_attribute(mk_attr(b"mapped_blocks", d.mapped_blocks));
|
||||
elem.push_attribute(mk_attr(b"transaction", d.transaction));
|
||||
elem.push_attribute(mk_attr(b"creation_time", d.creation_time));
|
||||
elem.push_attribute(mk_attr(b"snap_time", d.snap_time));
|
||||
self.w.write_event(Event::Start(elem))?;
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_e(&mut self) -> Result<Visit> {
|
||||
self.w
|
||||
.write_event(Event::End(BytesEnd::borrowed(b"device")))?;
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn map(&mut self, m: &Map) -> Result<Visit> {
|
||||
match m.len {
|
||||
1 => {
|
||||
let tag = b"single_mapping";
|
||||
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||
elem.push_attribute(mk_attr(b"origin_block", m.thin_begin));
|
||||
elem.push_attribute(mk_attr(b"data_block", m.data_begin));
|
||||
elem.push_attribute(mk_attr(b"time", m.time));
|
||||
self.w.write_event(Event::Empty(elem))?;
|
||||
}
|
||||
_ => {
|
||||
let tag = b"range_mapping";
|
||||
let mut elem = BytesStart::owned(tag.to_vec(), tag.len());
|
||||
elem.push_attribute(mk_attr(b"origin_begin", m.thin_begin));
|
||||
elem.push_attribute(mk_attr(b"data_begin", m.data_begin));
|
||||
elem.push_attribute(mk_attr(b"length", m.len));
|
||||
elem.push_attribute(mk_attr(b"time", m.time));
|
||||
self.w.write_event(Event::Empty(elem))?;
|
||||
}
|
||||
}
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<Visit> {
|
||||
let w = self.w.inner();
|
||||
w.flush()?;
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
// FIXME: nasty unwraps
|
||||
fn string_val(kv: &Attribute) -> String {
|
||||
let v = kv.unescaped_value().unwrap();
|
||||
let bytes = v.to_vec();
|
||||
String::from_utf8(bytes).unwrap()
|
||||
}
|
||||
|
||||
// FIXME: there's got to be a way of doing this without copying the string
|
||||
fn u64_val(kv: &Attribute) -> Result<u64> {
|
||||
let n = string_val(kv).parse::<u64>()?;
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn u32_val(kv: &Attribute) -> Result<u32> {
|
||||
let n = string_val(kv).parse::<u32>()?;
|
||||
Ok(n)
|
||||
}
|
||||
|
||||
fn bad_attr<T>(_tag: &str, _attr: &[u8]) -> Result<T> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn missing_attr<T>(_tag: &str, _attr: &str) -> Result<T> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn check_attr<T>(tag: &str, name: &str, maybe_v: Option<T>) -> Result<T> {
|
||||
match maybe_v {
|
||||
None => missing_attr(tag, name),
|
||||
Some(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_superblock(e: &BytesStart) -> Result<Superblock> {
|
||||
let mut uuid: Option<String> = None;
|
||||
let mut time: Option<u64> = None;
|
||||
let mut transaction: Option<u64> = None;
|
||||
let mut flags: Option<u32> = None;
|
||||
let mut version: Option<u32> = None;
|
||||
let mut data_block_size: Option<u32> = None;
|
||||
let mut nr_data_blocks: Option<u64> = None;
|
||||
let mut metadata_snap: Option<u64> = None;
|
||||
|
||||
for a in e.attributes() {
|
||||
let kv = a.unwrap();
|
||||
match kv.key {
|
||||
b"uuid" => uuid = Some(string_val(&kv)),
|
||||
b"time" => time = Some(u64_val(&kv)?),
|
||||
b"transaction" => transaction = Some(u64_val(&kv)?),
|
||||
b"flags" => flags = Some(u32_val(&kv)?),
|
||||
b"version" => version = Some(u32_val(&kv)?),
|
||||
b"data_block_size" => data_block_size = Some(u32_val(&kv)?),
|
||||
b"nr_data_blocks" => nr_data_blocks = Some(u64_val(&kv)?),
|
||||
b"metadata_snap" => metadata_snap = Some(u64_val(&kv)?),
|
||||
_ => return bad_attr("superblock", kv.key),
|
||||
}
|
||||
}
|
||||
|
||||
let tag = "superblock";
|
||||
|
||||
Ok(Superblock {
|
||||
uuid: check_attr(tag, "uuid", uuid)?,
|
||||
time: check_attr(tag, "time", time)?,
|
||||
transaction: check_attr(tag, "transaction", transaction)?,
|
||||
flags,
|
||||
version,
|
||||
data_block_size: check_attr(tag, "data_block_size", data_block_size)?,
|
||||
nr_data_blocks: check_attr(tag, "nr_data_blocks", nr_data_blocks)?,
|
||||
metadata_snap,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_device(e: &BytesStart) -> Result<Device> {
|
||||
let mut dev_id: Option<u32> = None;
|
||||
let mut mapped_blocks: Option<u64> = None;
|
||||
let mut transaction: Option<u64> = None;
|
||||
let mut creation_time: Option<u64> = None;
|
||||
let mut snap_time: Option<u64> = None;
|
||||
|
||||
for a in e.attributes() {
|
||||
let kv = a.unwrap();
|
||||
match kv.key {
|
||||
b"dev_id" => dev_id = Some(u32_val(&kv)?),
|
||||
b"mapped_blocks" => mapped_blocks = Some(u64_val(&kv)?),
|
||||
b"transaction" => transaction = Some(u64_val(&kv)?),
|
||||
b"creation_time" => creation_time = Some(u64_val(&kv)?),
|
||||
b"snap_time" => snap_time = Some(u64_val(&kv)?),
|
||||
_ => return bad_attr("device", kv.key),
|
||||
}
|
||||
}
|
||||
|
||||
let tag = "device";
|
||||
|
||||
Ok(Device {
|
||||
dev_id: check_attr(tag, "dev_id", dev_id)?,
|
||||
mapped_blocks: check_attr(tag, "mapped_blocks", mapped_blocks)?,
|
||||
transaction: check_attr(tag, "transaction", transaction)?,
|
||||
creation_time: check_attr(tag, "creation_time", creation_time)?,
|
||||
snap_time: check_attr(tag, "snap_time", snap_time)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_single_map(e: &BytesStart) -> Result<Map> {
|
||||
let mut thin_begin: Option<u64> = None;
|
||||
let mut data_begin: Option<u64> = None;
|
||||
let mut time: Option<u32> = None;
|
||||
|
||||
for a in e.attributes() {
|
||||
let kv = a.unwrap();
|
||||
match kv.key {
|
||||
b"origin_block" => thin_begin = Some(u64_val(&kv)?),
|
||||
b"data_block" => data_begin = Some(u64_val(&kv)?),
|
||||
b"time" => time = Some(u32_val(&kv)?),
|
||||
_ => return bad_attr("single_mapping", kv.key),
|
||||
}
|
||||
}
|
||||
|
||||
let tag = "single_mapping";
|
||||
|
||||
Ok(Map {
|
||||
thin_begin: check_attr(tag, "origin_block", thin_begin)?,
|
||||
data_begin: check_attr(tag, "data_block", data_begin)?,
|
||||
time: check_attr(tag, "time", time)?,
|
||||
len: 1,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_range_map(e: &BytesStart) -> Result<Map> {
|
||||
let mut thin_begin: Option<u64> = None;
|
||||
let mut data_begin: Option<u64> = None;
|
||||
let mut time: Option<u32> = None;
|
||||
let mut length: Option<u64> = None;
|
||||
|
||||
for a in e.attributes() {
|
||||
let kv = a.unwrap();
|
||||
match kv.key {
|
||||
b"origin_begin" => thin_begin = Some(u64_val(&kv)?),
|
||||
b"data_begin" => data_begin = Some(u64_val(&kv)?),
|
||||
b"time" => time = Some(u32_val(&kv)?),
|
||||
b"length" => length = Some(u64_val(&kv)?),
|
||||
_ => return bad_attr("range_mapping", kv.key),
|
||||
}
|
||||
}
|
||||
|
||||
let tag = "range_mapping";
|
||||
|
||||
Ok(Map {
|
||||
thin_begin: check_attr(tag, "origin_begin", thin_begin)?,
|
||||
data_begin: check_attr(tag, "data_begin", data_begin)?,
|
||||
time: check_attr(tag, "time", time)?,
|
||||
len: check_attr(tag, "length", length)?,
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_event<R, M>(reader: &mut Reader<R>, buf: &mut Vec<u8>, visitor: &mut M) -> Result<Visit>
|
||||
where
|
||||
R: Read + BufRead,
|
||||
M: MetadataVisitor,
|
||||
{
|
||||
match reader.read_event(buf) {
|
||||
Ok(Event::Start(ref e)) => match e.name() {
|
||||
b"superblock" => visitor.superblock_b(&parse_superblock(e)?),
|
||||
b"device" => visitor.device_b(&parse_device(e)?),
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::End(ref e)) => match e.name() {
|
||||
b"superblock" => visitor.superblock_e(),
|
||||
b"device" => visitor.device_e(),
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::Empty(ref e)) => match e.name() {
|
||||
b"single_mapping" => visitor.map(&parse_single_map(e)?),
|
||||
b"range_mapping" => visitor.map(&parse_range_map(e)?),
|
||||
_ => todo!(),
|
||||
},
|
||||
Ok(Event::Text(_)) => Ok(Visit::Continue),
|
||||
Ok(Event::Comment(_)) => Ok(Visit::Continue),
|
||||
Ok(Event::Eof) => {
|
||||
visitor.eof()?;
|
||||
Ok(Visit::Stop)
|
||||
}
|
||||
Ok(_) => todo!(),
|
||||
|
||||
// FIXME: don't panic!
|
||||
Err(e) => panic!("error parsing xml {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read<R, M>(input: R, visitor: &mut M) -> Result<()>
|
||||
where
|
||||
R: Read,
|
||||
M: MetadataVisitor,
|
||||
{
|
||||
let input = BufReader::new(input);
|
||||
let mut reader = Reader::from_reader(input);
|
||||
|
||||
reader.trim_text(true);
|
||||
let mut buf = Vec::new();
|
||||
|
||||
loop {
|
||||
match handle_event(&mut reader, &mut buf, visitor)? {
|
||||
Visit::Continue => {}
|
||||
Visit::Stop => break,
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
//---------------------------------------
|
||||
|
||||
struct SBVisitor {
|
||||
superblock: Option<Superblock>,
|
||||
}
|
||||
|
||||
impl MetadataVisitor for SBVisitor {
|
||||
fn superblock_b(&mut self, sb: &Superblock) -> Result<Visit> {
|
||||
self.superblock = Some(sb.clone());
|
||||
Ok(Visit::Stop)
|
||||
}
|
||||
|
||||
fn superblock_e(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn device_b(&mut self, _d: &Device) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
fn device_e(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn map(&mut self, _m: &Map) -> Result<Visit> {
|
||||
Ok(Visit::Continue)
|
||||
}
|
||||
|
||||
fn eof(&mut self) -> Result<Visit> {
|
||||
Ok(Visit::Stop)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_superblock<R>(input: R) -> Result<Superblock>
|
||||
where
|
||||
R: Read,
|
||||
{
|
||||
let mut v = SBVisitor {superblock: None};
|
||||
read(input, &mut v)?;
|
||||
Ok(v.superblock.unwrap())
|
||||
}
|
||||
|
||||
//---------------------------------------
|
Loading…
Reference in New Issue
Block a user