This commit is contained in:
pepe 2023-06-13 21:41:47 +00:00
parent 4d646c02f2
commit 17b8ef8bd9
46 changed files with 1 additions and 3138 deletions

567
Cargo.lock generated
View File

@ -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"

View File

@ -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"

View File

@ -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

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
../target/release/thin_metadata_pack

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
../target/release/thin_metadata_unpack

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -1 +0,0 @@
pdata_tools

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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)
}
}

View File

@ -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(())
}

View File

@ -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)
}
//---------------------------------------

View File

@ -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;

View File

@ -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);
}
}
}
//-------------------------------------------------

View File

@ -1,5 +0,0 @@
pub mod toplevel;
mod delta_list;
mod node_encode;
mod vm;

View File

@ -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))
}
//-------------------------------------

View File

@ -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(())
}

View File

@ -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)
}
}
//-------------------------------------------------

View File

@ -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(())
}

View File

@ -1,3 +0,0 @@
pub mod toplevel;
mod copier;

View File

@ -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, &regions)?;
} 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(())
}
//---------------------------------------

View File

@ -1 +0,0 @@
pub mod xml;

View File

@ -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())
}
//---------------------------------------