diff options
Diffstat (limited to 'third_party/rust/base64')
27 files changed, 7235 insertions, 0 deletions
diff --git a/third_party/rust/base64/.cargo-checksum.json b/third_party/rust/base64/.cargo-checksum.json new file mode 100644 index 0000000000..442dc3c00c --- /dev/null +++ b/third_party/rust/base64/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"8f96978e95bd43767d0a03ac6110a6e7808c8807e87a6dd1634412bd91341fa2","Cargo.toml":"9d58d2f4a65fe6456bc7e453e6826f43f7dd1df9f471cab13ca1d0796dba1969","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7","README.md":"2810098d290f3df719e6f41ffca38bb954d0fe62d4e56905a9a2436c4784bebf","RELEASE-NOTES.md":"ee3bbd1c58b6c4c86dd31734fcfbc2b03388c9a451137278947ef1e8851fb1eb","benches/benchmarks.rs":"4eedafc56a6e6477731af2d04c6b673bb320a46d7f02f8329ea3a4d6b55c5f25","examples/make_tables.rs":"85de384828f485540d12c0b45de57b6c1701276ad9e9075ec6669678abe7e9e5","icon_CLion.svg":"cffa044ba75cb998ee3306991dc4a3755ec2f39ab95ddd4b74bc21988389020f","src/chunked_encoder.rs":"fba5ea5f1204b9bf11291ec3483bcb23d330101eb2f6635e33cd63e4de13b653","src/decode.rs":"7bbbade0e9e2f8a1937afee8a62273f6c8c878ea2574cdc06c33b9672a7f3b8d","src/display.rs":"55f9cd05ba037b77689e2558fa174055fad5273959d1edce3a2289f94244fd5d","src/encode.rs":"390f875ae06647f732a06e0003aa9dfffb8a9c3a6b1acc20911cfedebc380b4b","src/lib.rs":"73ce99986d8126f47c3f5af28c9451305ef7563b6aedc1ead3187b8c3a851093","src/read/decoder.rs":"9a7b65e165f7aed6b007bf7436ac9ba9b03d3b03e9d5a1e16691874e21509ced","src/read/decoder_tests.rs":"aacb7624c33ed6b90e068ff9af6095c839b4088060b4c406c08dce25ce837f6d","src/read/mod.rs":"e0b714eda02d16b1ffa6f78fd09b2f963e01c881b1f7c17b39db4e904be5e746","src/tables.rs":"af437939bdad23f99e0877dcf1319f3f47611dca1365e6f81b6d440d8dcc538a","src/tests.rs":"dbe000d13ab7a5d7a91ebed59d6bf6cce58f7b47c473b3ce6ec219d5044ceb57","src/write/encoder.rs":"f99b967f4347c44af8d0b8cb7ad29228a1b168a13738d7298dbe07b7411de30a","src/write/encoder_tests.rs":"7192e321a1c61afa0de5ecb3c07757777ea4bc2844789cdb93852467977d5969","src/write/mod.rs":"124b223f3effcb1d85671a4173a635cdfae480341b032217c77876647344176b","tests/decode.rs":"2b63c9e7f9dd48153e77206882684d3f99288bcfa2addda71b255e5d966c8b00","tests/encode.rs":"5efb6904c36c6f899a05078e5c9be756fc58af1ee9940edfa8dea1ee53675364","tests/helpers.rs":"a76015e4a4e8f98213bdbaa592cd9574ccdc95a28e1b1f835a2753e09fa6037f","tests/tests.rs":"05753e5f1d4a6c75015a5342f9b5dc3073c00bdfe0a829a962f8723321c75549"},"package":"7d5ca2cd0adc3f48f9e9ea5a6bbdf9ccc0bfade884847e484d452414c7ccffb3"}
\ No newline at end of file diff --git a/third_party/rust/base64/Cargo.lock b/third_party/rust/base64/Cargo.lock new file mode 100644 index 0000000000..d06dee6ea5 --- /dev/null +++ b/third_party/rust/base64/Cargo.lock @@ -0,0 +1,760 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "atty" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "autocfg" +version = "0.1.4" +source = "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 = "base64" +version = "0.12.0" +dependencies = [ + "criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "doc-comment 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bitflags" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "bstr" +version = "0.2.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)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-automata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bumpalo" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "cast" +version = "0.2.2" +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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.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.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "criterion" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "atty 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", + "criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "criterion-plot" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.1" +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)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bstr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "csv-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "doc-comment" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "either" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "itertools" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "itoa" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "js-sys" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "libc" +version = "0.2.65" +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 = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "memchr" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "memoffset" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num-traits" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "num_cpus" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "oorandom" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "plotters" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "1.0.9" +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 = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "quote" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-automata" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "regex-syntax" +version = "0.6.16" +source = "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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "same-file" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +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 = "serde" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "serde_derive" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "serde_json" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "0.15.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "syn" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tinytemplate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-width" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +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 = "walkdir" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.59" +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)", + "wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.59" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "web-sys" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "winapi" +version = "0.3.7" +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-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.7 (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 atty 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ecaaea69f52b3b18633611ec0007d188517d0366f47ff703d400fa6879d6f8d5" +"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" +"checksum autocfg 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" +"checksum bstr 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6cc0572e02f76cb335f309b19e0a0d585b4f62788f7d26de2a13a836a637385f" +"checksum bumpalo 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f359dc14ff8911330a51ef78022d376f25ed00248912803b58f00cb1c27f742" +"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" +"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +"checksum criterion 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1" +"checksum criterion-plot 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545" +"checksum crossbeam-deque 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" +"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" +"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +"checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" +"checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" +"checksum doc-comment 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "807e5847c39ad6a11eac66de492ed1406f76a260eb8656e8740cad9eabc69c27" +"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" +"checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" +"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum js-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1cb931d43e71f560c81badb0191596562bafad2be06a3f9025b845c847c60df5" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +"checksum libc 0.2.65 (registry+https://github.com/rust-lang/crates.io-index)" = "1a31a0627fdf1f6a39ec0dd577e101440b7db22672c0901fe00a9a6fbb5c24e8" +"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" +"checksum maybe-uninit 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" +"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +"checksum num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bcef43580c035376c0705c42792c294b66974abbfd2789b511784023f71f3273" +"checksum oorandom 11.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405" +"checksum plotters 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb" +"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" +"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" +"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" +"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +"checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" +"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +"checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +"checksum rayon 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +"checksum rayon-core 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +"checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +"checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" +"checksum regex-automata 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3ed09217220c272b29ef237a974ad58515bde75f194e3ffa7e6d0bf0f3b01f86" +"checksum regex-syntax 0.6.16 (registry+https://github.com/rust-lang/crates.io-index)" = "1132f845907680735a84409c3bebc64d1364a5683ffbce899550cd09d5eaefc1" +"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" +"checksum same-file 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8f20c4be53a8a1ff4c1f1b2bd14570d2f634628709752f0702ecdd2b3f9a5267" +"checksum scopeguard 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +"checksum serde 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "076a696fdea89c19d3baed462576b8f6d663064414b5c793642da8dfeb99475b" +"checksum serde_derive 1.0.94 (registry+https://github.com/rust-lang/crates.io-index)" = "ef45eb79d6463b22f5f9e16d283798b7c0175ba6050bc25c1a946c122727fe7b" +"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +"checksum syn 0.15.39 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d960b829a55e56db167e861ddb43602c003c7be0bee1d345021703fac2fb7c" +"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" +"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +"checksum tinytemplate 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4574b75faccaacddb9b284faecdf0b544b80b6b294f3d062d325c5726a209c20" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +"checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +"checksum wasm-bindgen 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "3557c397ab5a8e347d434782bcd31fc1483d927a6826804cec05cc792ee2519d" +"checksum wasm-bindgen-backend 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "e0da9c9a19850d3af6df1cb9574970b566d617ecfaf36eb0b706b6f3ef9bd2f8" +"checksum wasm-bindgen-macro 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "0f6fde1d36e75a714b5fe0cffbb78978f222ea6baebb726af13c78869fdb4205" +"checksum wasm-bindgen-macro-support 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "25bda4168030a6412ea8a047e27238cadf56f0e53516e1e83fec0a8b7c786f6d" +"checksum wasm-bindgen-shared 0.2.59 (registry+https://github.com/rust-lang/crates.io-index)" = "fc9f36ad51f25b0219a3d4d13b90eb44cd075dff8b6280cca015775d7acaddd8" +"checksum web-sys 0.3.36 (registry+https://github.com/rust-lang/crates.io-index)" = "721c6263e2c66fd44501cc5efbfa2b7dfa775d13e4ea38c46299646ed1f9c70a" +"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" +"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +"checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" +"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/third_party/rust/base64/Cargo.toml b/third_party/rust/base64/Cargo.toml new file mode 100644 index 0000000000..c92b54a3e3 --- /dev/null +++ b/third_party/rust/base64/Cargo.toml @@ -0,0 +1,43 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies +# +# If you believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "base64" +version = "0.12.0" +authors = ["Alice Maz <alice@alicemaz.com>", "Marshall Pierce <marshall@mpierce.org>"] +description = "encodes and decodes base64 as bytes or utf8" +documentation = "https://docs.rs/base64" +readme = "README.md" +keywords = ["base64", "utf8", "encode", "decode", "no_std"] +categories = ["encoding"] +license = "MIT/Apache-2.0" +repository = "https://github.com/marshallpierce/rust-base64" +[profile.bench] +debug = true + +[[bench]] +name = "benchmarks" +harness = false +[dev-dependencies.criterion] +version = "0.3" + +[dev-dependencies.doc-comment] +version = "0.3" + +[dev-dependencies.rand] +version = "0.6.1" + +[features] +alloc = [] +default = ["std"] +std = [] diff --git a/third_party/rust/base64/LICENSE-APACHE b/third_party/rust/base64/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/base64/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/base64/LICENSE-MIT b/third_party/rust/base64/LICENSE-MIT new file mode 100644 index 0000000000..7bc10f80a0 --- /dev/null +++ b/third_party/rust/base64/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Alice Maz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/rust/base64/README.md b/third_party/rust/base64/README.md new file mode 100644 index 0000000000..7815966c30 --- /dev/null +++ b/third_party/rust/base64/README.md @@ -0,0 +1,114 @@ +[base64](https://crates.io/crates/base64) +=== + +[![](https://img.shields.io/crates/v/base64.svg)](https://crates.io/crates/base64) [![Docs](https://docs.rs/base64/badge.svg)](https://docs.rs/base64) [![Build](https://travis-ci.org/marshallpierce/rust-base64.svg?branch=master)](https://travis-ci.org/marshallpierce/rust-base64) [![codecov](https://codecov.io/gh/marshallpierce/rust-base64/branch/master/graph/badge.svg)](https://codecov.io/gh/marshallpierce/rust-base64) [![unsafe forbidden](https://img.shields.io/badge/unsafe-forbidden-success.svg)](https://github.com/rust-secure-code/safety-dance/) + +<a href="https://www.jetbrains.com/?from=rust-base64"><img src="/icon_CLion.svg" height="40px"/></a> + +Made with CLion. Thanks to JetBrains for supporting open source! + +It's base64. What more could anyone want? + +This library's goals are to be *correct* and *fast*. It's thoroughly tested and widely used. It exposes functionality at multiple levels of abstraction so you can choose the level of convenience vs performance that you want, e.g. `decode_config_slice` decodes into an existing `&mut [u8]` and is pretty fast (2.6GiB/s for a 3 KiB input), whereas `decode_config` allocates a new `Vec<u8>` and returns it, which might be more convenient in some cases, but is slower (although still fast enough for almost any purpose) at 2.1 GiB/s. + +Example +--- + +```rust +extern crate base64; + +use base64::{encode, decode}; + +fn main() { + let a = b"hello world"; + let b = "aGVsbG8gd29ybGQ="; + + assert_eq!(encode(a), b); + assert_eq!(a, &decode(b).unwrap()[..]); +} +``` + +See the [docs](https://docs.rs/base64) for all the details. + +Rust version compatibility +--- + +The minimum required Rust version is 1.34.0. + +Developing +--- + +Benchmarks are in `benches/`. Running them requires nightly rust, but `rustup` makes it easy: + +```bash +rustup run nightly cargo bench +``` + +Decoding is aided by some pre-calculated tables, which are generated by: + +```bash +cargo run --example make_tables > src/tables.rs.tmp && mv src/tables.rs.tmp src/tables.rs +``` + +no_std +--- + +This crate supports no_std. By default the crate targets std via the `std` feature. You can deactivate the `default-features` to target core instead. In that case you lose out on all the functionality revolving around `std::io`, `std::error::Error` and heap allocations. There is an additional `alloc` feature that you can activate to bring back the support for heap allocations. + +Profiling +--- + +On Linux, you can use [perf](https://perf.wiki.kernel.org/index.php/Main_Page) for profiling. Then compile the benchmarks with `rustup nightly run cargo bench --no-run`. + +Run the benchmark binary with `perf` (shown here filtering to one particular benchmark, which will make the results easier to read). `perf` is only available to the root user on most systems as it fiddles with event counters in your CPU, so use `sudo`. We need to run the actual benchmark binary, hence the path into `target`. You can see the actual full path with `rustup run nightly cargo bench -v`; it will print out the commands it runs. If you use the exact path that `bench` outputs, make sure you get the one that's for the benchmarks, not the tests. You may also want to `cargo clean` so you have only one `benchmarks-` binary (they tend to accumulate). + +```bash +sudo perf record target/release/deps/benchmarks-* --bench decode_10mib_reuse +``` + +Then analyze the results, again with perf: + +```bash +sudo perf annotate -l +``` + +You'll see a bunch of interleaved rust source and assembly like this. The section with `lib.rs:327` is telling us that 4.02% of samples saw the `movzbl` aka bit shift as the active instruction. However, this percentage is not as exact as it seems due to a phenomenon called *skid*. Basically, a consequence of how fancy modern CPUs are is that this sort of instruction profiling is inherently inaccurate, especially in branch-heavy code. + +```text + lib.rs:322 0.70 : 10698: mov %rdi,%rax + 2.82 : 1069b: shr $0x38,%rax + : if morsel == decode_tables::INVALID_VALUE { + : bad_byte_index = input_index; + : break; + : }; + : accum = (morsel as u64) << 58; + lib.rs:327 4.02 : 1069f: movzbl (%r9,%rax,1),%r15d + : // fast loop of 8 bytes at a time + : while input_index < length_of_full_chunks { + : let mut accum: u64; + : + : let input_chunk = BigEndian::read_u64(&input_bytes[input_index..(input_index + 8)]); + : morsel = decode_table[(input_chunk >> 56) as usize]; + lib.rs:322 3.68 : 106a4: cmp $0xff,%r15 + : if morsel == decode_tables::INVALID_VALUE { + 0.00 : 106ab: je 1090e <base64::decode_config_buf::hbf68a45fefa299c1+0x46e> +``` + + +Fuzzing +--- + +This uses [cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz). See `fuzz/fuzzers` for the available fuzzing scripts. To run, use an invocation like these: + +```bash +cargo +nightly fuzz run roundtrip +cargo +nightly fuzz run roundtrip_no_pad +cargo +nightly fuzz run roundtrip_random_config -- -max_len=10240 +cargo +nightly fuzz run decode_random +``` + + +License +--- + +This project is dual-licensed under MIT and Apache 2.0. diff --git a/third_party/rust/base64/RELEASE-NOTES.md b/third_party/rust/base64/RELEASE-NOTES.md new file mode 100644 index 0000000000..a6fbdf2887 --- /dev/null +++ b/third_party/rust/base64/RELEASE-NOTES.md @@ -0,0 +1,89 @@ +# 0.12.0 + +- A `Read` implementation (`DecoderReader`) to let users transparently decoded data from a b64 input source +- IMAP's modified b64 alphabet +- Relaxed type restrictions to just `AsRef<[ut8]>` for main `encode*`/`decode*` functions +- A minor performance improvement in encoding + +# 0.11.0 +- Minimum rust version 1.34.0 +- `no_std` is now supported via the two new features `alloc` and `std`. + +# 0.10.1 + +- Minimum rust version 1.27.2 +- Fix bug in streaming encoding ([#90](https://github.com/marshallpierce/rust-base64/pull/90)): if the underlying writer didn't write all the bytes given to it, the remaining bytes would not be retried later. See the docs on `EncoderWriter::write`. +- Make it configurable whether or not to return an error when decoding detects excess trailing bits. + +# 0.10.0 + +- Remove line wrapping. Line wrapping was never a great conceptual fit in this library, and other features (streaming encoding, etc) either couldn't support it or could support only special cases of it with a great increase in complexity. Line wrapping has been pulled out into a [line-wrap](https://crates.io/crates/line-wrap) crate, so it's still available if you need it. + - `Base64Display` creation no longer uses a `Result` because it can't fail, which means its helper methods for common + configs that `unwrap()` for you are no longer needed +- Add a streaming encoder `Write` impl to transparently base64 as you write. +- Remove the remaining `unsafe` code. +- Remove whitespace stripping to simplify `no_std` support. No out of the box configs use it, and it's trivial to do yourself if needed: `filter(|b| !b" \n\t\r\x0b\x0c".contains(b)`. +- Detect invalid trailing symbols when decoding and return an error rather than silently ignoring them. + +# 0.9.3 + +- Update safemem + +# 0.9.2 + +- Derive `Clone` for `DecodeError`. + +# 0.9.1 + +- Add support for `crypt(3)`'s base64 variant. + +# 0.9.0 + +- `decode_config_slice` function for no-allocation decoding, analogous to `encode_config_slice` +- Decode performance optimization + +# 0.8.0 + +- `encode_config_slice` function for no-allocation encoding + +# 0.7.0 + +- `STANDARD_NO_PAD` config +- `Base64Display` heap-free wrapper for use in format strings, etc + +# 0.6.0 + +- Decode performance improvements +- Use `unsafe` in fewer places +- Added fuzzers + +# 0.5.2 + +- Avoid usize overflow when calculating length +- Better line wrapping performance + +# 0.5.1 + +- Temporarily disable line wrapping +- Add Apache 2.0 license + +# 0.5.0 + +- MIME support, including configurable line endings and line wrapping +- Removed `decode_ws` +- Renamed `Base64Error` to `DecodeError` + +# 0.4.1 + +- Allow decoding a `AsRef<[u8]>` instead of just a `&str` + +# 0.4.0 + +- Configurable padding +- Encode performance improvements + +# 0.3.0 + +- Added encode/decode functions that do not allocate their own storage +- Decode performance improvements +- Extraneous padding bytes are no longer ignored. Now, an error will be returned. diff --git a/third_party/rust/base64/benches/benchmarks.rs b/third_party/rust/base64/benches/benchmarks.rs new file mode 100644 index 0000000000..07f887212a --- /dev/null +++ b/third_party/rust/base64/benches/benchmarks.rs @@ -0,0 +1,179 @@ +extern crate base64; +#[macro_use] +extern crate criterion; +extern crate rand; + +use base64::display; +use base64::{ + decode, decode_config_buf, decode_config_slice, encode, encode_config_buf, encode_config_slice, + write, Config, +}; + +use criterion::{black_box, Bencher, Criterion, ParameterizedBenchmark, Throughput}; +use rand::{FromEntropy, Rng}; +use std::io::{self, Read, Write}; + +const TEST_CONFIG: Config = base64::STANDARD; + +fn do_decode_bench(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size * 3 / 4); + fill(&mut v); + let encoded = encode(&v); + + b.iter(|| { + let orig = decode(&encoded); + black_box(&orig); + }); +} + +fn do_decode_bench_reuse_buf(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size * 3 / 4); + fill(&mut v); + let encoded = encode(&v); + + let mut buf = Vec::new(); + b.iter(|| { + decode_config_buf(&encoded, TEST_CONFIG, &mut buf).unwrap(); + black_box(&buf); + buf.clear(); + }); +} + +fn do_decode_bench_slice(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size * 3 / 4); + fill(&mut v); + let encoded = encode(&v); + + let mut buf = Vec::new(); + buf.resize(size, 0); + b.iter(|| { + decode_config_slice(&encoded, TEST_CONFIG, &mut buf).unwrap(); + black_box(&buf); + }); +} + +fn do_decode_bench_stream(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size * 3 / 4); + fill(&mut v); + let encoded = encode(&v); + + let mut buf = Vec::new(); + buf.resize(size, 0); + buf.truncate(0); + + b.iter(|| { + let mut cursor = io::Cursor::new(&encoded[..]); + let mut decoder = base64::read::DecoderReader::new(&mut cursor, TEST_CONFIG); + decoder.read_to_end(&mut buf).unwrap(); + buf.clear(); + black_box(&buf); + }); +} + +fn do_encode_bench(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size); + fill(&mut v); + b.iter(|| { + let e = encode(&v); + black_box(&e); + }); +} + +fn do_encode_bench_display(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size); + fill(&mut v); + b.iter(|| { + let e = format!("{}", display::Base64Display::with_config(&v, TEST_CONFIG)); + black_box(&e); + }); +} + +fn do_encode_bench_reuse_buf(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size); + fill(&mut v); + let mut buf = String::new(); + b.iter(|| { + encode_config_buf(&v, TEST_CONFIG, &mut buf); + buf.clear(); + }); +} + +fn do_encode_bench_slice(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size); + fill(&mut v); + let mut buf = Vec::new(); + // conservative estimate of encoded size + buf.resize(v.len() * 2, 0); + b.iter(|| { + encode_config_slice(&v, TEST_CONFIG, &mut buf); + }); +} + +fn do_encode_bench_stream(b: &mut Bencher, &size: &usize) { + let mut v: Vec<u8> = Vec::with_capacity(size); + fill(&mut v); + let mut buf = Vec::new(); + + buf.reserve(size * 2); + b.iter(|| { + buf.clear(); + let mut stream_enc = write::EncoderWriter::new(&mut buf, TEST_CONFIG); + stream_enc.write_all(&v).unwrap(); + stream_enc.flush().unwrap(); + }); +} + +fn fill(v: &mut Vec<u8>) { + let cap = v.capacity(); + // weak randomness is plenty; we just want to not be completely friendly to the branch predictor + let mut r = rand::rngs::SmallRng::from_entropy(); + while v.len() < cap { + v.push(r.gen::<u8>()); + } +} + +const BYTE_SIZES: [usize; 5] = [3, 50, 100, 500, 3 * 1024]; + +// Benchmarks over these byte sizes take longer so we will run fewer samples to +// keep the benchmark runtime reasonable. +const LARGE_BYTE_SIZES: [usize; 3] = [3 * 1024 * 1024, 10 * 1024 * 1024, 30 * 1024 * 1024]; + +fn encode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize> { + ParameterizedBenchmark::new("encode", do_encode_bench, byte_sizes.iter().cloned()) + .warm_up_time(std::time::Duration::from_millis(500)) + .measurement_time(std::time::Duration::from_secs(3)) + .throughput(|s| Throughput::Bytes(*s as u64)) + .with_function("encode_display", do_encode_bench_display) + .with_function("encode_reuse_buf", do_encode_bench_reuse_buf) + .with_function("encode_slice", do_encode_bench_slice) + .with_function("encode_reuse_buf_stream", do_encode_bench_stream) +} + +fn decode_benchmarks(byte_sizes: &[usize]) -> ParameterizedBenchmark<usize> { + ParameterizedBenchmark::new("decode", do_decode_bench, byte_sizes.iter().cloned()) + .warm_up_time(std::time::Duration::from_millis(500)) + .measurement_time(std::time::Duration::from_secs(3)) + .throughput(|s| Throughput::Bytes(*s as u64)) + .with_function("decode_reuse_buf", do_decode_bench_reuse_buf) + .with_function("decode_slice", do_decode_bench_slice) + .with_function("decode_stream", do_decode_bench_stream) +} + +fn bench(c: &mut Criterion) { + c.bench("bench_small_input", encode_benchmarks(&BYTE_SIZES[..])); + + c.bench( + "bench_large_input", + encode_benchmarks(&LARGE_BYTE_SIZES[..]).sample_size(10), + ); + + c.bench("bench_small_input", decode_benchmarks(&BYTE_SIZES[..])); + + c.bench( + "bench_large_input", + decode_benchmarks(&LARGE_BYTE_SIZES[..]).sample_size(10), + ); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/third_party/rust/base64/examples/make_tables.rs b/third_party/rust/base64/examples/make_tables.rs new file mode 100644 index 0000000000..07e9635775 --- /dev/null +++ b/third_party/rust/base64/examples/make_tables.rs @@ -0,0 +1,130 @@ +use std::collections::HashMap; +use std::iter::Iterator; + +fn main() { + println!("pub const INVALID_VALUE: u8 = 255;"); + + // A-Z + let standard_alphabet: Vec<u8> = (0x41..0x5B) + // a-z + .chain(0x61..0x7B) + // 0-9 + .chain(0x30..0x3A) + // + + .chain(0x2B..0x2C) + // / + .chain(0x2F..0x30) + .collect(); + print_encode_table(&standard_alphabet, "STANDARD_ENCODE", 0); + print_decode_table(&standard_alphabet, "STANDARD_DECODE", 0); + + // A-Z + let url_alphabet: Vec<u8> = (0x41..0x5B) + // a-z + .chain(0x61..0x7B) + // 0-9 + .chain(0x30..0x3A) + // - + .chain(0x2D..0x2E) + // _ + .chain(0x5F..0x60) + .collect(); + print_encode_table(&url_alphabet, "URL_SAFE_ENCODE", 0); + print_decode_table(&url_alphabet, "URL_SAFE_DECODE", 0); + + // ./0123456789 + let crypt_alphabet: Vec<u8> = (b'.'..(b'9' + 1)) + // A-Z + .chain(b'A'..(b'Z' + 1)) + // a-z + .chain(b'a'..(b'z' + 1)) + .collect(); + print_encode_table(&crypt_alphabet, "CRYPT_ENCODE", 0); + print_decode_table(&crypt_alphabet, "CRYPT_DECODE", 0); + + // A-Z + let imap_alphabet: Vec<u8> = (0x41..0x5B) + // a-z + .chain(0x61..0x7B) + // 0-9 + .chain(0x30..0x3A) + // + + .chain(0x2B..0x2C) + // , + .chain(0x2C..0x2D) + .collect(); + print_encode_table(&imap_alphabet, "IMAP_MUTF7_ENCODE", 0); + print_decode_table(&imap_alphabet, "IMAP_MUTF7_DECODE", 0); +} + +fn print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) { + println!("#[rustfmt::skip]"); + println!( + "{:width$}pub const {}: &[u8; 64] = &[", + "", + const_name, + width = indent_depth + ); + + for (i, b) in alphabet.iter().enumerate() { + println!( + "{:width$}{}, // input {} (0x{:X}) => '{}' (0x{:X})", + "", + b, + i, + i, + String::from_utf8(vec![*b as u8]).unwrap(), + b, + width = indent_depth + 4 + ); + } + + println!("{:width$}];", "", width = indent_depth); +} + +fn print_decode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) { + // map of alphabet bytes to 6-bit morsels + let mut input_to_morsel = HashMap::<u8, u8>::new(); + + // standard base64 alphabet bytes, in order + for (morsel, ascii_byte) in alphabet.iter().enumerate() { + // truncation cast is fine here + let _ = input_to_morsel.insert(*ascii_byte, morsel as u8); + } + + println!("#[rustfmt::skip]"); + println!( + "{:width$}pub const {}: &[u8; 256] = &[", + "", + const_name, + width = indent_depth + ); + for ascii_byte in 0..256 { + let (value, comment) = match input_to_morsel.get(&(ascii_byte as u8)) { + None => ( + "INVALID_VALUE".to_string(), + format!("input {} (0x{:X})", ascii_byte, ascii_byte), + ), + Some(v) => ( + format!("{}", *v), + format!( + "input {} (0x{:X} char '{}') => {} (0x{:X})", + ascii_byte, + ascii_byte, + String::from_utf8(vec![ascii_byte as u8]).unwrap(), + *v, + *v + ), + ), + }; + + println!( + "{:width$}{}, // {}", + "", + value, + comment, + width = indent_depth + 4 + ); + } + println!("{:width$}];", "", width = indent_depth); +} diff --git a/third_party/rust/base64/icon_CLion.svg b/third_party/rust/base64/icon_CLion.svg new file mode 100644 index 0000000000..e9edb0445e --- /dev/null +++ b/third_party/rust/base64/icon_CLion.svg @@ -0,0 +1,34 @@ +<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 128 128"> + <defs> + <linearGradient id="linear-gradient" x1="40.69" y1="-676.56" x2="83.48" y2="-676.56" gradientTransform="matrix(1, 0, 0, -1, 0, -648.86)" gradientUnits="userSpaceOnUse"> + <stop offset="0" stop-color="#ed358c"/> + <stop offset="0.16" stop-color="#e9388c"/> + <stop offset="0.3" stop-color="#de418c"/> + <stop offset="0.43" stop-color="#cc508c"/> + <stop offset="0.57" stop-color="#b2658d"/> + <stop offset="0.7" stop-color="#90808d"/> + <stop offset="0.83" stop-color="#67a18e"/> + <stop offset="0.95" stop-color="#37c78f"/> + <stop offset="1" stop-color="#22d88f"/> + </linearGradient> + <linearGradient id="linear-gradient-2" x1="32.58" y1="-665.27" x2="13.76" y2="-791.59" gradientTransform="matrix(1, 0, 0, -1, 0, -648.86)" gradientUnits="userSpaceOnUse"> + <stop offset="0.09" stop-color="#22d88f"/> + <stop offset="0.9" stop-color="#029de0"/> + </linearGradient> + <linearGradient id="linear-gradient-3" x1="116.68" y1="-660.66" x2="-12.09" y2="-796.66" xlink:href="#linear-gradient-2"/> + <linearGradient id="linear-gradient-4" x1="73.35" y1="-739.1" x2="122.29" y2="-746.06" xlink:href="#linear-gradient-2"/> + </defs> + <title>icon_CLion</title> + <g> + <polygon points="49.2 51.8 40.6 55.4 48.4 0 77.8 16.2 49.2 51.8" fill="url(#linear-gradient)"/> + <polygon points="44.6 76.8 48.8 0 11.8 23.2 0 94 44.6 76.8" fill="url(#linear-gradient-2)"/> + <polygon points="125.4 38.4 109 4.8 77.8 16.2 55 41.4 0 94 41.6 124.4 93.6 77.2 125.4 38.4" fill="url(#linear-gradient-3)"/> + <polygon points="53.8 54.6 46.6 98.4 75.8 121 107.8 128 128 82.4 53.8 54.6" fill="url(#linear-gradient-4)"/> + </g> + <g> + <rect x="24" y="24" width="80" height="80"/> + <rect x="31.6" y="89" width="30" height="5" fill="#fff"/> + <path d="M31,51.2h0A16.83,16.83,0,0,1,48.2,34c6.2,0,10,2,13,5.2l-4.6,5.4c-2.6-2.4-5.2-3.8-8.4-3.8-5.6,0-9.6,4.6-9.6,10.4h0c0,5.6,4,10.4,9.6,10.4,3.8,0,6.2-1.6,8.8-3.8l4.6,4.6c-3.4,3.6-7.2,6-13.6,6A17,17,0,0,1,31,51.2" fill="#fff"/> + <path d="M66.6,34.4H74v27H88.4v6.2H66.6V34.4Z" fill="#fff"/> + </g> +</svg> diff --git a/third_party/rust/base64/src/chunked_encoder.rs b/third_party/rust/base64/src/chunked_encoder.rs new file mode 100644 index 0000000000..bd45ec9e2d --- /dev/null +++ b/third_party/rust/base64/src/chunked_encoder.rs @@ -0,0 +1,247 @@ +use crate::{ + encode::{add_padding, encode_to_slice}, + Config, +}; +#[cfg(any(feature = "alloc", feature = "std", test))] +use alloc::string::String; +use core::cmp; +#[cfg(any(feature = "alloc", feature = "std", test))] +use core::str; + +/// The output mechanism for ChunkedEncoder's encoded bytes. +pub trait Sink { + type Error; + + /// Handle a chunk of encoded base64 data (as UTF-8 bytes) + fn write_encoded_bytes(&mut self, encoded: &[u8]) -> Result<(), Self::Error>; +} + +const BUF_SIZE: usize = 1024; + +/// A base64 encoder that emits encoded bytes in chunks without heap allocation. +pub struct ChunkedEncoder { + config: Config, + max_input_chunk_len: usize, +} + +impl ChunkedEncoder { + pub fn new(config: Config) -> ChunkedEncoder { + ChunkedEncoder { + config, + max_input_chunk_len: max_input_length(BUF_SIZE, config), + } + } + + pub fn encode<S: Sink>(&self, bytes: &[u8], sink: &mut S) -> Result<(), S::Error> { + let mut encode_buf: [u8; BUF_SIZE] = [0; BUF_SIZE]; + let encode_table = self.config.char_set.encode_table(); + + let mut input_index = 0; + + while input_index < bytes.len() { + // either the full input chunk size, or it's the last iteration + let input_chunk_len = cmp::min(self.max_input_chunk_len, bytes.len() - input_index); + + let chunk = &bytes[input_index..(input_index + input_chunk_len)]; + + let mut b64_bytes_written = encode_to_slice(chunk, &mut encode_buf, encode_table); + + input_index += input_chunk_len; + let more_input_left = input_index < bytes.len(); + + if self.config.pad && !more_input_left { + // no more input, add padding if needed. Buffer will have room because + // max_input_length leaves room for it. + b64_bytes_written += add_padding(bytes.len(), &mut encode_buf[b64_bytes_written..]); + } + + sink.write_encoded_bytes(&encode_buf[0..b64_bytes_written])?; + } + + Ok(()) + } +} + +/// Calculate the longest input that can be encoded for the given output buffer size. +/// +/// If the config requires padding, two bytes of buffer space will be set aside so that the last +/// chunk of input can be encoded safely. +/// +/// The input length will always be a multiple of 3 so that no encoding state has to be carried over +/// between chunks. +fn max_input_length(encoded_buf_len: usize, config: Config) -> usize { + let effective_buf_len = if config.pad { + // make room for padding + encoded_buf_len + .checked_sub(2) + .expect("Don't use a tiny buffer") + } else { + encoded_buf_len + }; + + // No padding, so just normal base64 expansion. + (effective_buf_len / 4) * 3 +} + +// A really simple sink that just appends to a string +#[cfg(any(feature = "alloc", feature = "std", test))] +pub(crate) struct StringSink<'a> { + string: &'a mut String, +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +impl<'a> StringSink<'a> { + pub(crate) fn new(s: &mut String) -> StringSink { + StringSink { string: s } + } +} + +#[cfg(any(feature = "alloc", feature = "std", test))] +impl<'a> Sink for StringSink<'a> { + type Error = (); + + fn write_encoded_bytes(&mut self, s: &[u8]) -> Result<(), Self::Error> { + self.string.push_str(str::from_utf8(s).unwrap()); + + Ok(()) + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + use crate::{encode_config_buf, tests::random_config, CharacterSet, STANDARD}; + + use rand::{ + distributions::{Distribution, Uniform}, + FromEntropy, Rng, + }; + + #[test] + fn chunked_encode_empty() { + assert_eq!("", chunked_encode_str(&[], STANDARD)); + } + + #[test] + fn chunked_encode_intermediate_fast_loop() { + // > 8 bytes input, will enter the pretty fast loop + assert_eq!( + "Zm9vYmFyYmF6cXV4", + chunked_encode_str(b"foobarbazqux", STANDARD) + ); + } + + #[test] + fn chunked_encode_fast_loop() { + // > 32 bytes input, will enter the uber fast loop + assert_eq!( + "Zm9vYmFyYmF6cXV4cXV1eGNvcmdlZ3JhdWx0Z2FycGx5eg==", + chunked_encode_str(b"foobarbazquxquuxcorgegraultgarplyz", STANDARD) + ); + } + + #[test] + fn chunked_encode_slow_loop_only() { + // < 8 bytes input, slow loop only + assert_eq!("Zm9vYmFy", chunked_encode_str(b"foobar", STANDARD)); + } + + #[test] + fn chunked_encode_matches_normal_encode_random_string_sink() { + let helper = StringSinkTestHelper; + chunked_encode_matches_normal_encode_random(&helper); + } + + #[test] + fn max_input_length_no_pad() { + let config = config_with_pad(false); + assert_eq!(768, max_input_length(1024, config)); + } + + #[test] + fn max_input_length_with_pad_decrements_one_triple() { + let config = config_with_pad(true); + assert_eq!(765, max_input_length(1024, config)); + } + + #[test] + fn max_input_length_with_pad_one_byte_short() { + let config = config_with_pad(true); + assert_eq!(765, max_input_length(1025, config)); + } + + #[test] + fn max_input_length_with_pad_fits_exactly() { + let config = config_with_pad(true); + assert_eq!(768, max_input_length(1026, config)); + } + + #[test] + fn max_input_length_cant_use_extra_single_encoded_byte() { + let config = Config::new(crate::CharacterSet::Standard, false); + assert_eq!(300, max_input_length(401, config)); + } + + pub fn chunked_encode_matches_normal_encode_random<S: SinkTestHelper>(sink_test_helper: &S) { + let mut input_buf: Vec<u8> = Vec::new(); + let mut output_buf = String::new(); + let mut rng = rand::rngs::SmallRng::from_entropy(); + let input_len_range = Uniform::new(1, 10_000); + + for _ in 0..5_000 { + input_buf.clear(); + output_buf.clear(); + + let buf_len = input_len_range.sample(&mut rng); + for _ in 0..buf_len { + input_buf.push(rng.gen()); + } + + let config = random_config(&mut rng); + + let chunk_encoded_string = sink_test_helper.encode_to_string(config, &input_buf); + encode_config_buf(&input_buf, config, &mut output_buf); + + assert_eq!( + output_buf, chunk_encoded_string, + "input len={}, config: pad={}", + buf_len, config.pad + ); + } + } + + fn chunked_encode_str(bytes: &[u8], config: Config) -> String { + let mut s = String::new(); + { + let mut sink = StringSink::new(&mut s); + let encoder = ChunkedEncoder::new(config); + encoder.encode(bytes, &mut sink).unwrap(); + } + + return s; + } + + fn config_with_pad(pad: bool) -> Config { + Config::new(CharacterSet::Standard, pad) + } + + // An abstraction around sinks so that we can have tests that easily to any sink implementation + pub trait SinkTestHelper { + fn encode_to_string(&self, config: Config, bytes: &[u8]) -> String; + } + + struct StringSinkTestHelper; + + impl SinkTestHelper for StringSinkTestHelper { + fn encode_to_string(&self, config: Config, bytes: &[u8]) -> String { + let encoder = ChunkedEncoder::new(config); + let mut s = String::new(); + { + let mut sink = StringSink::new(&mut s); + encoder.encode(bytes, &mut sink).unwrap(); + } + + s + } + } +} diff --git a/third_party/rust/base64/src/decode.rs b/third_party/rust/base64/src/decode.rs new file mode 100644 index 0000000000..716fce737e --- /dev/null +++ b/third_party/rust/base64/src/decode.rs @@ -0,0 +1,867 @@ +use crate::{tables, Config}; + +#[cfg(any(feature = "alloc", feature = "std", test))] +use crate::STANDARD; +#[cfg(any(feature = "alloc", feature = "std", test))] +use alloc::vec::Vec; +use core::fmt; +#[cfg(any(feature = "std", test))] +use std::error; + +// decode logic operates on chunks of 8 input bytes without padding +const INPUT_CHUNK_LEN: usize = 8; +const DECODED_CHUNK_LEN: usize = 6; +// we read a u64 and write a u64, but a u64 of input only yields 6 bytes of output, so the last +// 2 bytes of any output u64 should not be counted as written to (but must be available in a +// slice). +const DECODED_CHUNK_SUFFIX: usize = 2; + +// how many u64's of input to handle at a time +const CHUNKS_PER_FAST_LOOP_BLOCK: usize = 4; +const INPUT_BLOCK_LEN: usize = CHUNKS_PER_FAST_LOOP_BLOCK * INPUT_CHUNK_LEN; +// includes the trailing 2 bytes for the final u64 write +const DECODED_BLOCK_LEN: usize = + CHUNKS_PER_FAST_LOOP_BLOCK * DECODED_CHUNK_LEN + DECODED_CHUNK_SUFFIX; + +/// Errors that can occur while decoding. +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum DecodeError { + /// An invalid byte was found in the input. The offset and offending byte are provided. + InvalidByte(usize, u8), + /// The length of the input is invalid. + InvalidLength, + /// The last non-padding input symbol's encoded 6 bits have nonzero bits that will be discarded. + /// This is indicative of corrupted or truncated Base64. + /// Unlike InvalidByte, which reports symbols that aren't in the alphabet, this error is for + /// symbols that are in the alphabet but represent nonsensical encodings. + InvalidLastSymbol(usize, u8), +} + +impl fmt::Display for DecodeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DecodeError::InvalidByte(index, byte) => { + write!(f, "Invalid byte {}, offset {}.", byte, index) + } + DecodeError::InvalidLength => write!(f, "Encoded text cannot have a 6-bit remainder."), + DecodeError::InvalidLastSymbol(index, byte) => { + write!(f, "Invalid last symbol {}, offset {}.", byte, index) + } + } + } +} + +#[cfg(any(feature = "std", test))] +impl error::Error for DecodeError { + fn description(&self) -> &str { + match *self { + DecodeError::InvalidByte(_, _) => "invalid byte", + DecodeError::InvalidLength => "invalid length", + DecodeError::InvalidLastSymbol(_, _) => "invalid last symbol", + } + } + + fn cause(&self) -> Option<&dyn error::Error> { + None + } +} + +///Decode from string reference as octets. +///Returns a Result containing a Vec<u8>. +///Convenience `decode_config(input, base64::STANDARD);`. +/// +///# Example +/// +///```rust +///extern crate base64; +/// +///fn main() { +/// let bytes = base64::decode("aGVsbG8gd29ybGQ=").unwrap(); +/// println!("{:?}", bytes); +///} +///``` +#[cfg(any(feature = "alloc", feature = "std", test))] +pub fn decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> { + decode_config(input, STANDARD) +} + +///Decode from string reference as octets. +///Returns a Result containing a Vec<u8>. +/// +///# Example +/// +///```rust +///extern crate base64; +/// +///fn main() { +/// let bytes = base64::decode_config("aGVsbG8gd29ybGR+Cg==", base64::STANDARD).unwrap(); +/// println!("{:?}", bytes); +/// +/// let bytes_url = base64::decode_config("aGVsbG8gaW50ZXJuZXR-Cg==", base64::URL_SAFE).unwrap(); +/// println!("{:?}", bytes_url); +///} +///``` +#[cfg(any(feature = "alloc", feature = "std", test))] +pub fn decode_config<T: AsRef<[u8]>>(input: T, config: Config) -> Result<Vec<u8>, DecodeError> { + let mut buffer = Vec::<u8>::with_capacity(input.as_ref().len() * 4 / 3); + + decode_config_buf(input, config, &mut buffer).map(|_| buffer) +} + +///Decode from string reference as octets. +///Writes into the supplied buffer to avoid allocation. +///Returns a Result containing an empty tuple, aka (). +/// +///# Example +/// +///```rust +///extern crate base64; +/// +///fn main() { +/// let mut buffer = Vec::<u8>::new(); +/// base64::decode_config_buf("aGVsbG8gd29ybGR+Cg==", base64::STANDARD, &mut buffer).unwrap(); +/// println!("{:?}", buffer); +/// +/// buffer.clear(); +/// +/// base64::decode_config_buf("aGVsbG8gaW50ZXJuZXR-Cg==", base64::URL_SAFE, &mut buffer) +/// .unwrap(); +/// println!("{:?}", buffer); +///} +///``` +#[cfg(any(feature = "alloc", feature = "std", test))] +pub fn decode_config_buf<T: AsRef<[u8]>>( + input: T, + config: Config, + buffer: &mut Vec<u8>, +) -> Result<(), DecodeError> { + let input_bytes = input.as_ref(); + + let starting_output_len = buffer.len(); + + let num_chunks = num_chunks(input_bytes); + let decoded_len_estimate = num_chunks + .checked_mul(DECODED_CHUNK_LEN) + .and_then(|p| p.checked_add(starting_output_len)) + .expect("Overflow when calculating output buffer length"); + buffer.resize(decoded_len_estimate, 0); + + let bytes_written; + { + let buffer_slice = &mut buffer.as_mut_slice()[starting_output_len..]; + bytes_written = decode_helper(input_bytes, num_chunks, config, buffer_slice)?; + } + + buffer.truncate(starting_output_len + bytes_written); + + Ok(()) +} + +/// Decode the input into the provided output slice. +/// +/// This will not write any bytes past exactly what is decoded (no stray garbage bytes at the end). +/// +/// If you don't know ahead of time what the decoded length should be, size your buffer with a +/// conservative estimate for the decoded length of an input: 3 bytes of output for every 4 bytes of +/// input, rounded up, or in other words `(input_len + 3) / 4 * 3`. +/// +/// If the slice is not large enough, this will panic. +pub fn decode_config_slice<T: AsRef<[u8]>>( + input: T, + config: Config, + output: &mut [u8], +) -> Result<usize, DecodeError> { + let input_bytes = input.as_ref(); + + decode_helper(input_bytes, num_chunks(input_bytes), config, output) +} + +/// Return the number of input chunks (including a possibly partial final chunk) in the input +fn num_chunks(input: &[u8]) -> usize { + input + .len() + .checked_add(INPUT_CHUNK_LEN - 1) + .expect("Overflow when calculating number of chunks in input") + / INPUT_CHUNK_LEN +} + +/// Helper to avoid duplicating num_chunks calculation, which is costly on short inputs. +/// Returns the number of bytes written, or an error. +// We're on the fragile edge of compiler heuristics here. If this is not inlined, slow. If this is +// inlined(always), a different slow. plain ol' inline makes the benchmarks happiest at the moment, +// but this is fragile and the best setting changes with only minor code modifications. +#[inline] +fn decode_helper( + input: &[u8], + num_chunks: usize, + config: Config, + output: &mut [u8], +) -> Result<usize, DecodeError> { + let char_set = config.char_set; + let decode_table = char_set.decode_table(); + + let remainder_len = input.len() % INPUT_CHUNK_LEN; + + // Because the fast decode loop writes in groups of 8 bytes (unrolled to + // CHUNKS_PER_FAST_LOOP_BLOCK times 8 bytes, where possible) and outputs 8 bytes at a time (of + // which only 6 are valid data), we need to be sure that we stop using the fast decode loop + // soon enough that there will always be 2 more bytes of valid data written after that loop. + let trailing_bytes_to_skip = match remainder_len { + // if input is a multiple of the chunk size, ignore the last chunk as it may have padding, + // and the fast decode logic cannot handle padding + 0 => INPUT_CHUNK_LEN, + // 1 and 5 trailing bytes are illegal: can't decode 6 bits of input into a byte + 1 | 5 => return Err(DecodeError::InvalidLength), + // This will decode to one output byte, which isn't enough to overwrite the 2 extra bytes + // written by the fast decode loop. So, we have to ignore both these 2 bytes and the + // previous chunk. + 2 => INPUT_CHUNK_LEN + 2, + // If this is 3 unpadded chars, then it would actually decode to 2 bytes. However, if this + // is an erroneous 2 chars + 1 pad char that would decode to 1 byte, then it should fail + // with an error, not panic from going past the bounds of the output slice, so we let it + // use stage 3 + 4. + 3 => INPUT_CHUNK_LEN + 3, + // This can also decode to one output byte because it may be 2 input chars + 2 padding + // chars, which would decode to 1 byte. + 4 => INPUT_CHUNK_LEN + 4, + // Everything else is a legal decode len (given that we don't require padding), and will + // decode to at least 2 bytes of output. + _ => remainder_len, + }; + + // rounded up to include partial chunks + let mut remaining_chunks = num_chunks; + + let mut input_index = 0; + let mut output_index = 0; + + { + let length_of_fast_decode_chunks = input.len().saturating_sub(trailing_bytes_to_skip); + + // Fast loop, stage 1 + // manual unroll to CHUNKS_PER_FAST_LOOP_BLOCK of u64s to amortize slice bounds checks + if let Some(max_start_index) = length_of_fast_decode_chunks.checked_sub(INPUT_BLOCK_LEN) { + while input_index <= max_start_index { + let input_slice = &input[input_index..(input_index + INPUT_BLOCK_LEN)]; + let output_slice = &mut output[output_index..(output_index + DECODED_BLOCK_LEN)]; + + decode_chunk( + &input_slice[0..], + input_index, + decode_table, + &mut output_slice[0..], + )?; + decode_chunk( + &input_slice[8..], + input_index + 8, + decode_table, + &mut output_slice[6..], + )?; + decode_chunk( + &input_slice[16..], + input_index + 16, + decode_table, + &mut output_slice[12..], + )?; + decode_chunk( + &input_slice[24..], + input_index + 24, + decode_table, + &mut output_slice[18..], + )?; + + input_index += INPUT_BLOCK_LEN; + output_index += DECODED_BLOCK_LEN - DECODED_CHUNK_SUFFIX; + remaining_chunks -= CHUNKS_PER_FAST_LOOP_BLOCK; + } + } + + // Fast loop, stage 2 (aka still pretty fast loop) + // 8 bytes at a time for whatever we didn't do in stage 1. + if let Some(max_start_index) = length_of_fast_decode_chunks.checked_sub(INPUT_CHUNK_LEN) { + while input_index < max_start_index { + decode_chunk( + &input[input_index..(input_index + INPUT_CHUNK_LEN)], + input_index, + decode_table, + &mut output + [output_index..(output_index + DECODED_CHUNK_LEN + DECODED_CHUNK_SUFFIX)], + )?; + + output_index += DECODED_CHUNK_LEN; + input_index += INPUT_CHUNK_LEN; + remaining_chunks -= 1; + } + } + } + + // Stage 3 + // If input length was such that a chunk had to be deferred until after the fast loop + // because decoding it would have produced 2 trailing bytes that wouldn't then be + // overwritten, we decode that chunk here. This way is slower but doesn't write the 2 + // trailing bytes. + // However, we still need to avoid the last chunk (partial or complete) because it could + // have padding, so we always do 1 fewer to avoid the last chunk. + for _ in 1..remaining_chunks { + decode_chunk_precise( + &input[input_index..], + input_index, + decode_table, + &mut output[output_index..(output_index + DECODED_CHUNK_LEN)], + )?; + + input_index += INPUT_CHUNK_LEN; + output_index += DECODED_CHUNK_LEN; + } + + // always have one more (possibly partial) block of 8 input + debug_assert!(input.len() - input_index > 1 || input.is_empty()); + debug_assert!(input.len() - input_index <= 8); + + // Stage 4 + // Finally, decode any leftovers that aren't a complete input block of 8 bytes. + // Use a u64 as a stack-resident 8 byte buffer. + let mut leftover_bits: u64 = 0; + let mut morsels_in_leftover = 0; + let mut padding_bytes = 0; + let mut first_padding_index: usize = 0; + let mut last_symbol = 0_u8; + let start_of_leftovers = input_index; + for (i, b) in input[start_of_leftovers..].iter().enumerate() { + // '=' padding + if *b == 0x3D { + // There can be bad padding in a few ways: + // 1 - Padding with non-padding characters after it + // 2 - Padding after zero or one non-padding characters before it + // in the current quad. + // 3 - More than two characters of padding. If 3 or 4 padding chars + // are in the same quad, that implies it will be caught by #2. + // If it spreads from one quad to another, it will be caught by + // #2 in the second quad. + + if i % 4 < 2 { + // Check for case #2. + let bad_padding_index = start_of_leftovers + + if padding_bytes > 0 { + // If we've already seen padding, report the first padding index. + // This is to be consistent with the faster logic above: it will report an + // error on the first padding character (since it doesn't expect to see + // anything but actual encoded data). + first_padding_index + } else { + // haven't seen padding before, just use where we are now + i + }; + return Err(DecodeError::InvalidByte(bad_padding_index, *b)); + } + + if padding_bytes == 0 { + first_padding_index = i; + } + + padding_bytes += 1; + continue; + } + + // Check for case #1. + // To make '=' handling consistent with the main loop, don't allow + // non-suffix '=' in trailing chunk either. Report error as first + // erroneous padding. + if padding_bytes > 0 { + return Err(DecodeError::InvalidByte( + start_of_leftovers + first_padding_index, + 0x3D, + )); + } + last_symbol = *b; + + // can use up to 8 * 6 = 48 bits of the u64, if last chunk has no padding. + // To minimize shifts, pack the leftovers from left to right. + let shift = 64 - (morsels_in_leftover + 1) * 6; + // tables are all 256 elements, lookup with a u8 index always succeeds + let morsel = decode_table[*b as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte(start_of_leftovers + i, *b)); + } + + leftover_bits |= (morsel as u64) << shift; + morsels_in_leftover += 1; + } + + let leftover_bits_ready_to_append = match morsels_in_leftover { + 0 => 0, + 2 => 8, + 3 => 16, + 4 => 24, + 6 => 32, + 7 => 40, + 8 => 48, + _ => unreachable!( + "Impossible: must only have 0 to 8 input bytes in last chunk, with no invalid lengths" + ), + }; + + // if there are bits set outside the bits we care about, last symbol encodes trailing bits that + // will not be included in the output + let mask = !0 >> leftover_bits_ready_to_append; + if !config.decode_allow_trailing_bits && (leftover_bits & mask) != 0 { + // last morsel is at `morsels_in_leftover` - 1 + return Err(DecodeError::InvalidLastSymbol( + start_of_leftovers + morsels_in_leftover - 1, + last_symbol, + )); + } + + let mut leftover_bits_appended_to_buf = 0; + while leftover_bits_appended_to_buf < leftover_bits_ready_to_append { + // `as` simply truncates the higher bits, which is what we want here + let selected_bits = (leftover_bits >> (56 - leftover_bits_appended_to_buf)) as u8; + output[output_index] = selected_bits; + output_index += 1; + + leftover_bits_appended_to_buf += 8; + } + + Ok(output_index) +} + +#[inline] +fn write_u64(output: &mut [u8], value: u64) { + output[..8].copy_from_slice(&value.to_be_bytes()); +} + +/// Decode 8 bytes of input into 6 bytes of output. 8 bytes of output will be written, but only the +/// first 6 of those contain meaningful data. +/// +/// `input` is the bytes to decode, of which the first 8 bytes will be processed. +/// `index_at_start_of_input` is the offset in the overall input (used for reporting errors +/// accurately) +/// `decode_table` is the lookup table for the particular base64 alphabet. +/// `output` will have its first 8 bytes overwritten, of which only the first 6 are valid decoded +/// data. +// yes, really inline (worth 30-50% speedup) +#[inline(always)] +fn decode_chunk( + input: &[u8], + index_at_start_of_input: usize, + decode_table: &[u8; 256], + output: &mut [u8], +) -> Result<(), DecodeError> { + let mut accum: u64; + + let morsel = decode_table[input[0] as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte(index_at_start_of_input, input[0])); + } + accum = (morsel as u64) << 58; + + let morsel = decode_table[input[1] as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte( + index_at_start_of_input + 1, + input[1], + )); + } + accum |= (morsel as u64) << 52; + + let morsel = decode_table[input[2] as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte( + index_at_start_of_input + 2, + input[2], + )); + } + accum |= (morsel as u64) << 46; + + let morsel = decode_table[input[3] as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte( + index_at_start_of_input + 3, + input[3], + )); + } + accum |= (morsel as u64) << 40; + + let morsel = decode_table[input[4] as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte( + index_at_start_of_input + 4, + input[4], + )); + } + accum |= (morsel as u64) << 34; + + let morsel = decode_table[input[5] as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte( + index_at_start_of_input + 5, + input[5], + )); + } + accum |= (morsel as u64) << 28; + + let morsel = decode_table[input[6] as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte( + index_at_start_of_input + 6, + input[6], + )); + } + accum |= (morsel as u64) << 22; + + let morsel = decode_table[input[7] as usize]; + if morsel == tables::INVALID_VALUE { + return Err(DecodeError::InvalidByte( + index_at_start_of_input + 7, + input[7], + )); + } + accum |= (morsel as u64) << 16; + + write_u64(output, accum); + + Ok(()) +} + +/// Decode an 8-byte chunk, but only write the 6 bytes actually decoded instead of including 2 +/// trailing garbage bytes. +#[inline] +fn decode_chunk_precise( + input: &[u8], + index_at_start_of_input: usize, + decode_table: &[u8; 256], + output: &mut [u8], +) -> Result<(), DecodeError> { + let mut tmp_buf = [0_u8; 8]; + + decode_chunk( + input, + index_at_start_of_input, + decode_table, + &mut tmp_buf[..], + )?; + + output[0..6].copy_from_slice(&tmp_buf[0..6]); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + encode::encode_config_buf, + encode::encode_config_slice, + tests::{assert_encode_sanity, random_config}, + }; + + use rand::{ + distributions::{Distribution, Uniform}, + FromEntropy, Rng, + }; + + #[test] + fn decode_chunk_precise_writes_only_6_bytes() { + let input = b"Zm9vYmFy"; // "foobar" + let mut output = [0_u8, 1, 2, 3, 4, 5, 6, 7]; + decode_chunk_precise(&input[..], 0, tables::STANDARD_DECODE, &mut output).unwrap(); + assert_eq!(&vec![b'f', b'o', b'o', b'b', b'a', b'r', 6, 7], &output); + } + + #[test] + fn decode_chunk_writes_8_bytes() { + let input = b"Zm9vYmFy"; // "foobar" + let mut output = [0_u8, 1, 2, 3, 4, 5, 6, 7]; + decode_chunk(&input[..], 0, tables::STANDARD_DECODE, &mut output).unwrap(); + assert_eq!(&vec![b'f', b'o', b'o', b'b', b'a', b'r', 0, 0], &output); + } + + #[test] + fn decode_into_nonempty_vec_doesnt_clobber_existing_prefix() { + let mut orig_data = Vec::new(); + let mut encoded_data = String::new(); + let mut decoded_with_prefix = Vec::new(); + let mut decoded_without_prefix = Vec::new(); + let mut prefix = Vec::new(); + + let prefix_len_range = Uniform::new(0, 1000); + let input_len_range = Uniform::new(0, 1000); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..10_000 { + orig_data.clear(); + encoded_data.clear(); + decoded_with_prefix.clear(); + decoded_without_prefix.clear(); + prefix.clear(); + + let input_len = input_len_range.sample(&mut rng); + + for _ in 0..input_len { + orig_data.push(rng.gen()); + } + + let config = random_config(&mut rng); + encode_config_buf(&orig_data, config, &mut encoded_data); + assert_encode_sanity(&encoded_data, config, input_len); + + let prefix_len = prefix_len_range.sample(&mut rng); + + // fill the buf with a prefix + for _ in 0..prefix_len { + prefix.push(rng.gen()); + } + + decoded_with_prefix.resize(prefix_len, 0); + decoded_with_prefix.copy_from_slice(&prefix); + + // decode into the non-empty buf + decode_config_buf(&encoded_data, config, &mut decoded_with_prefix).unwrap(); + // also decode into the empty buf + decode_config_buf(&encoded_data, config, &mut decoded_without_prefix).unwrap(); + + assert_eq!( + prefix_len + decoded_without_prefix.len(), + decoded_with_prefix.len() + ); + assert_eq!(orig_data, decoded_without_prefix); + + // append plain decode onto prefix + prefix.append(&mut decoded_without_prefix); + + assert_eq!(prefix, decoded_with_prefix); + } + } + + #[test] + fn decode_into_slice_doesnt_clobber_existing_prefix_or_suffix() { + let mut orig_data = Vec::new(); + let mut encoded_data = String::new(); + let mut decode_buf = Vec::new(); + let mut decode_buf_copy: Vec<u8> = Vec::new(); + + let input_len_range = Uniform::new(0, 1000); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..10_000 { + orig_data.clear(); + encoded_data.clear(); + decode_buf.clear(); + decode_buf_copy.clear(); + + let input_len = input_len_range.sample(&mut rng); + + for _ in 0..input_len { + orig_data.push(rng.gen()); + } + + let config = random_config(&mut rng); + encode_config_buf(&orig_data, config, &mut encoded_data); + assert_encode_sanity(&encoded_data, config, input_len); + + // fill the buffer with random garbage, long enough to have some room before and after + for _ in 0..5000 { + decode_buf.push(rng.gen()); + } + + // keep a copy for later comparison + decode_buf_copy.extend(decode_buf.iter()); + + let offset = 1000; + + // decode into the non-empty buf + let decode_bytes_written = + decode_config_slice(&encoded_data, config, &mut decode_buf[offset..]).unwrap(); + + assert_eq!(orig_data.len(), decode_bytes_written); + assert_eq!( + orig_data, + &decode_buf[offset..(offset + decode_bytes_written)] + ); + assert_eq!(&decode_buf_copy[0..offset], &decode_buf[0..offset]); + assert_eq!( + &decode_buf_copy[offset + decode_bytes_written..], + &decode_buf[offset + decode_bytes_written..] + ); + } + } + + #[test] + fn decode_into_slice_fits_in_precisely_sized_slice() { + let mut orig_data = Vec::new(); + let mut encoded_data = String::new(); + let mut decode_buf = Vec::new(); + + let input_len_range = Uniform::new(0, 1000); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..10_000 { + orig_data.clear(); + encoded_data.clear(); + decode_buf.clear(); + + let input_len = input_len_range.sample(&mut rng); + + for _ in 0..input_len { + orig_data.push(rng.gen()); + } + + let config = random_config(&mut rng); + encode_config_buf(&orig_data, config, &mut encoded_data); + assert_encode_sanity(&encoded_data, config, input_len); + + decode_buf.resize(input_len, 0); + + // decode into the non-empty buf + let decode_bytes_written = + decode_config_slice(&encoded_data, config, &mut decode_buf[..]).unwrap(); + + assert_eq!(orig_data.len(), decode_bytes_written); + assert_eq!(orig_data, decode_buf); + } + } + + #[test] + fn detect_invalid_last_symbol_two_bytes() { + let decode = + |input, forgiving| decode_config(input, STANDARD.decode_allow_trailing_bits(forgiving)); + + // example from https://github.com/marshallpierce/rust-base64/issues/75 + assert!(decode("iYU=", false).is_ok()); + // trailing 01 + assert_eq!( + Err(DecodeError::InvalidLastSymbol(2, b'V')), + decode("iYV=", false) + ); + assert_eq!(Ok(vec![137, 133]), decode("iYV=", true)); + // trailing 10 + assert_eq!( + Err(DecodeError::InvalidLastSymbol(2, b'W')), + decode("iYW=", false) + ); + assert_eq!(Ok(vec![137, 133]), decode("iYV=", true)); + // trailing 11 + assert_eq!( + Err(DecodeError::InvalidLastSymbol(2, b'X')), + decode("iYX=", false) + ); + assert_eq!(Ok(vec![137, 133]), decode("iYV=", true)); + + // also works when there are 2 quads in the last block + assert_eq!( + Err(DecodeError::InvalidLastSymbol(6, b'X')), + decode("AAAAiYX=", false) + ); + assert_eq!(Ok(vec![0, 0, 0, 137, 133]), decode("AAAAiYX=", true)); + } + + #[test] + fn detect_invalid_last_symbol_one_byte() { + // 0xFF -> "/w==", so all letters > w, 0-9, and '+', '/' should get InvalidLastSymbol + + assert!(decode("/w==").is_ok()); + // trailing 01 + assert_eq!(Err(DecodeError::InvalidLastSymbol(1, b'x')), decode("/x==")); + assert_eq!(Err(DecodeError::InvalidLastSymbol(1, b'z')), decode("/z==")); + assert_eq!(Err(DecodeError::InvalidLastSymbol(1, b'0')), decode("/0==")); + assert_eq!(Err(DecodeError::InvalidLastSymbol(1, b'9')), decode("/9==")); + assert_eq!(Err(DecodeError::InvalidLastSymbol(1, b'+')), decode("/+==")); + assert_eq!(Err(DecodeError::InvalidLastSymbol(1, b'/')), decode("//==")); + + // also works when there are 2 quads in the last block + assert_eq!( + Err(DecodeError::InvalidLastSymbol(5, b'x')), + decode("AAAA/x==") + ); + } + + #[test] + fn detect_invalid_last_symbol_every_possible_three_symbols() { + let mut base64_to_bytes = ::std::collections::HashMap::new(); + + let mut bytes = [0_u8; 2]; + for b1 in 0_u16..256 { + bytes[0] = b1 as u8; + for b2 in 0_u16..256 { + bytes[1] = b2 as u8; + let mut b64 = vec![0_u8; 4]; + assert_eq!(4, encode_config_slice(&bytes, STANDARD, &mut b64[..])); + let mut v = ::std::vec::Vec::with_capacity(2); + v.extend_from_slice(&bytes[..]); + + assert!(base64_to_bytes.insert(b64, v).is_none()); + } + } + + // every possible combination of symbols must either decode to 2 bytes or get InvalidLastSymbol + + let mut symbols = [0_u8; 4]; + for &s1 in STANDARD.char_set.encode_table().iter() { + symbols[0] = s1; + for &s2 in STANDARD.char_set.encode_table().iter() { + symbols[1] = s2; + for &s3 in STANDARD.char_set.encode_table().iter() { + symbols[2] = s3; + symbols[3] = b'='; + + match base64_to_bytes.get(&symbols[..]) { + Some(bytes) => { + assert_eq!(Ok(bytes.to_vec()), decode_config(&symbols, STANDARD)) + } + None => assert_eq!( + Err(DecodeError::InvalidLastSymbol(2, s3)), + decode_config(&symbols[..], STANDARD) + ), + } + } + } + } + } + + #[test] + fn detect_invalid_last_symbol_every_possible_two_symbols() { + let mut base64_to_bytes = ::std::collections::HashMap::new(); + + for b in 0_u16..256 { + let mut b64 = vec![0_u8; 4]; + assert_eq!(4, encode_config_slice(&[b as u8], STANDARD, &mut b64[..])); + let mut v = ::std::vec::Vec::with_capacity(1); + v.push(b as u8); + + assert!(base64_to_bytes.insert(b64, v).is_none()); + } + + // every possible combination of symbols must either decode to 1 byte or get InvalidLastSymbol + + let mut symbols = [0_u8; 4]; + for &s1 in STANDARD.char_set.encode_table().iter() { + symbols[0] = s1; + for &s2 in STANDARD.char_set.encode_table().iter() { + symbols[1] = s2; + symbols[2] = b'='; + symbols[3] = b'='; + + match base64_to_bytes.get(&symbols[..]) { + Some(bytes) => { + assert_eq!(Ok(bytes.to_vec()), decode_config(&symbols, STANDARD)) + } + None => assert_eq!( + Err(DecodeError::InvalidLastSymbol(1, s2)), + decode_config(&symbols[..], STANDARD) + ), + } + } + } + } + + #[test] + fn decode_imap() { + assert_eq!( + decode_config(b"+,,+", crate::IMAP_MUTF7), + decode_config(b"+//+", crate::STANDARD_NO_PAD) + ); + } +} diff --git a/third_party/rust/base64/src/display.rs b/third_party/rust/base64/src/display.rs new file mode 100644 index 0000000000..cc70aac225 --- /dev/null +++ b/third_party/rust/base64/src/display.rs @@ -0,0 +1,88 @@ +//! Enables base64'd output anywhere you might use a `Display` implementation, like a format string. +//! +//! ``` +//! use base64::display::Base64Display; +//! +//! let data = vec![0x0, 0x1, 0x2, 0x3]; +//! let wrapper = Base64Display::with_config(&data, base64::STANDARD); +//! +//! assert_eq!("base64: AAECAw==", format!("base64: {}", wrapper)); +//! ``` + +use super::chunked_encoder::ChunkedEncoder; +use super::Config; +use core::fmt::{Display, Formatter}; +use core::{fmt, str}; + +/// A convenience wrapper for base64'ing bytes into a format string without heap allocation. +pub struct Base64Display<'a> { + bytes: &'a [u8], + chunked_encoder: ChunkedEncoder, +} + +impl<'a> Base64Display<'a> { + /// Create a `Base64Display` with the provided config. + pub fn with_config(bytes: &[u8], config: Config) -> Base64Display { + Base64Display { + bytes, + chunked_encoder: ChunkedEncoder::new(config), + } + } +} + +impl<'a> Display for Base64Display<'a> { + fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { + let mut sink = FormatterSink { f: formatter }; + self.chunked_encoder.encode(self.bytes, &mut sink) + } +} + +struct FormatterSink<'a, 'b: 'a> { + f: &'a mut Formatter<'b>, +} + +impl<'a, 'b: 'a> super::chunked_encoder::Sink for FormatterSink<'a, 'b> { + type Error = fmt::Error; + + fn write_encoded_bytes(&mut self, encoded: &[u8]) -> Result<(), Self::Error> { + // Avoid unsafe. If max performance is needed, write your own display wrapper that uses + // unsafe here to gain about 10-15%. + self.f + .write_str(str::from_utf8(encoded).expect("base64 data was not utf8")) + } +} + +#[cfg(test)] +mod tests { + use super::super::chunked_encoder::tests::{ + chunked_encode_matches_normal_encode_random, SinkTestHelper, + }; + use super::super::*; + use super::*; + + #[test] + fn basic_display() { + assert_eq!( + "~$Zm9vYmFy#*", + format!("~${}#*", Base64Display::with_config(b"foobar", STANDARD)) + ); + assert_eq!( + "~$Zm9vYmFyZg==#*", + format!("~${}#*", Base64Display::with_config(b"foobarf", STANDARD)) + ); + } + + #[test] + fn display_encode_matches_normal_encode() { + let helper = DisplaySinkTestHelper; + chunked_encode_matches_normal_encode_random(&helper); + } + + struct DisplaySinkTestHelper; + + impl SinkTestHelper for DisplaySinkTestHelper { + fn encode_to_string(&self, config: Config, bytes: &[u8]) -> String { + format!("{}", Base64Display::with_config(bytes, config)) + } + } +} diff --git a/third_party/rust/base64/src/encode.rs b/third_party/rust/base64/src/encode.rs new file mode 100644 index 0000000000..9a09a19d49 --- /dev/null +++ b/third_party/rust/base64/src/encode.rs @@ -0,0 +1,675 @@ +use crate::Config; +#[cfg(any(feature = "alloc", feature = "std", test))] +use crate::{chunked_encoder, STANDARD}; +#[cfg(any(feature = "alloc", feature = "std", test))] +use alloc::{string::String, vec}; +use core::convert::TryInto; + +///Encode arbitrary octets as base64. +///Returns a String. +///Convenience for `encode_config(input, base64::STANDARD);`. +/// +///# Example +/// +///```rust +///extern crate base64; +/// +///fn main() { +/// let b64 = base64::encode(b"hello world"); +/// println!("{}", b64); +///} +///``` +#[cfg(any(feature = "alloc", feature = "std", test))] +pub fn encode<T: AsRef<[u8]>>(input: T) -> String { + encode_config(input, STANDARD) +} + +///Encode arbitrary octets as base64. +///Returns a String. +/// +///# Example +/// +///```rust +///extern crate base64; +/// +///fn main() { +/// let b64 = base64::encode_config(b"hello world~", base64::STANDARD); +/// println!("{}", b64); +/// +/// let b64_url = base64::encode_config(b"hello internet~", base64::URL_SAFE); +/// println!("{}", b64_url); +///} +///``` +#[cfg(any(feature = "alloc", feature = "std", test))] +pub fn encode_config<T: AsRef<[u8]>>(input: T, config: Config) -> String { + let mut buf = match encoded_size(input.as_ref().len(), config) { + Some(n) => vec![0; n], + None => panic!("integer overflow when calculating buffer size"), + }; + + encode_with_padding(input.as_ref(), config, buf.len(), &mut buf[..]); + + String::from_utf8(buf).expect("Invalid UTF8") +} + +///Encode arbitrary octets as base64. +///Writes into the supplied output buffer, which will grow the buffer if needed. +/// +///# Example +/// +///```rust +///extern crate base64; +/// +///fn main() { +/// let mut buf = String::new(); +/// base64::encode_config_buf(b"hello world~", base64::STANDARD, &mut buf); +/// println!("{}", buf); +/// +/// buf.clear(); +/// base64::encode_config_buf(b"hello internet~", base64::URL_SAFE, &mut buf); +/// println!("{}", buf); +///} +///``` +#[cfg(any(feature = "alloc", feature = "std", test))] +pub fn encode_config_buf<T: AsRef<[u8]>>(input: T, config: Config, buf: &mut String) { + let input_bytes = input.as_ref(); + + { + let mut sink = chunked_encoder::StringSink::new(buf); + let encoder = chunked_encoder::ChunkedEncoder::new(config); + + encoder + .encode(input_bytes, &mut sink) + .expect("Writing to a String shouldn't fail") + } +} + +/// Encode arbitrary octets as base64. +/// Writes into the supplied output buffer. +/// +/// This is useful if you wish to avoid allocation entirely (e.g. encoding into a stack-resident +/// or statically-allocated buffer). +/// +/// # Panics +/// +/// If `output` is too small to hold the encoded version of `input`, a panic will result. +/// +/// # Example +/// +/// ```rust +/// extern crate base64; +/// +/// fn main() { +/// let s = b"hello internet!"; +/// let mut buf = Vec::new(); +/// // make sure we'll have a slice big enough for base64 + padding +/// buf.resize(s.len() * 4 / 3 + 4, 0); +/// +/// let bytes_written = base64::encode_config_slice(s, +/// base64::STANDARD, &mut buf); +/// +/// // shorten our vec down to just what was written +/// buf.resize(bytes_written, 0); +/// +/// assert_eq!(s, base64::decode(&buf).unwrap().as_slice()); +/// } +/// ``` +pub fn encode_config_slice<T: AsRef<[u8]>>(input: T, config: Config, output: &mut [u8]) -> usize { + let input_bytes = input.as_ref(); + + let encoded_size = encoded_size(input_bytes.len(), config) + .expect("usize overflow when calculating buffer size"); + + let mut b64_output = &mut output[0..encoded_size]; + + encode_with_padding(&input_bytes, config, encoded_size, &mut b64_output); + + encoded_size +} + +/// B64-encode and pad (if configured). +/// +/// This helper exists to avoid recalculating encoded_size, which is relatively expensive on short +/// inputs. +/// +/// `encoded_size` is the encoded size calculated for `input`. +/// +/// `output` must be of size `encoded_size`. +/// +/// All bytes in `output` will be written to since it is exactly the size of the output. +fn encode_with_padding(input: &[u8], config: Config, encoded_size: usize, output: &mut [u8]) { + debug_assert_eq!(encoded_size, output.len()); + + let b64_bytes_written = encode_to_slice(input, output, config.char_set.encode_table()); + + let padding_bytes = if config.pad { + add_padding(input.len(), &mut output[b64_bytes_written..]) + } else { + 0 + }; + + let encoded_bytes = b64_bytes_written + .checked_add(padding_bytes) + .expect("usize overflow when calculating b64 length"); + + debug_assert_eq!(encoded_size, encoded_bytes); +} + +#[inline] +fn read_u64(s: &[u8]) -> u64 { + u64::from_be_bytes(s[..8].try_into().unwrap()) +} + +/// Encode input bytes to utf8 base64 bytes. Does not pad. +/// `output` must be long enough to hold the encoded `input` without padding. +/// Returns the number of bytes written. +#[inline] +pub fn encode_to_slice(input: &[u8], output: &mut [u8], encode_table: &[u8; 64]) -> usize { + let mut input_index: usize = 0; + + const BLOCKS_PER_FAST_LOOP: usize = 4; + const LOW_SIX_BITS: u64 = 0x3F; + + // we read 8 bytes at a time (u64) but only actually consume 6 of those bytes. Thus, we need + // 2 trailing bytes to be available to read.. + let last_fast_index = input.len().saturating_sub(BLOCKS_PER_FAST_LOOP * 6 + 2); + let mut output_index = 0; + + if last_fast_index > 0 { + while input_index <= last_fast_index { + // Major performance wins from letting the optimizer do the bounds check once, mostly + // on the output side + let input_chunk = &input[input_index..(input_index + (BLOCKS_PER_FAST_LOOP * 6 + 2))]; + let output_chunk = &mut output[output_index..(output_index + BLOCKS_PER_FAST_LOOP * 8)]; + + // Hand-unrolling for 32 vs 16 or 8 bytes produces yields performance about equivalent + // to unsafe pointer code on a Xeon E5-1650v3. 64 byte unrolling was slightly better for + // large inputs but significantly worse for 50-byte input, unsurprisingly. I suspect + // that it's a not uncommon use case to encode smallish chunks of data (e.g. a 64-byte + // SHA-512 digest), so it would be nice if that fit in the unrolled loop at least once. + // Plus, single-digit percentage performance differences might well be quite different + // on different hardware. + + let input_u64 = read_u64(&input_chunk[0..]); + + output_chunk[0] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize]; + output_chunk[1] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize]; + output_chunk[2] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize]; + output_chunk[3] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize]; + output_chunk[4] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize]; + output_chunk[5] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize]; + output_chunk[6] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize]; + output_chunk[7] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize]; + + let input_u64 = read_u64(&input_chunk[6..]); + + output_chunk[8] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize]; + output_chunk[9] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize]; + output_chunk[10] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize]; + output_chunk[11] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize]; + output_chunk[12] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize]; + output_chunk[13] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize]; + output_chunk[14] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize]; + output_chunk[15] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize]; + + let input_u64 = read_u64(&input_chunk[12..]); + + output_chunk[16] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize]; + output_chunk[17] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize]; + output_chunk[18] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize]; + output_chunk[19] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize]; + output_chunk[20] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize]; + output_chunk[21] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize]; + output_chunk[22] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize]; + output_chunk[23] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize]; + + let input_u64 = read_u64(&input_chunk[18..]); + + output_chunk[24] = encode_table[((input_u64 >> 58) & LOW_SIX_BITS) as usize]; + output_chunk[25] = encode_table[((input_u64 >> 52) & LOW_SIX_BITS) as usize]; + output_chunk[26] = encode_table[((input_u64 >> 46) & LOW_SIX_BITS) as usize]; + output_chunk[27] = encode_table[((input_u64 >> 40) & LOW_SIX_BITS) as usize]; + output_chunk[28] = encode_table[((input_u64 >> 34) & LOW_SIX_BITS) as usize]; + output_chunk[29] = encode_table[((input_u64 >> 28) & LOW_SIX_BITS) as usize]; + output_chunk[30] = encode_table[((input_u64 >> 22) & LOW_SIX_BITS) as usize]; + output_chunk[31] = encode_table[((input_u64 >> 16) & LOW_SIX_BITS) as usize]; + + output_index += BLOCKS_PER_FAST_LOOP * 8; + input_index += BLOCKS_PER_FAST_LOOP * 6; + } + } + + // Encode what's left after the fast loop. + + const LOW_SIX_BITS_U8: u8 = 0x3F; + + let rem = input.len() % 3; + let start_of_rem = input.len() - rem; + + // start at the first index not handled by fast loop, which may be 0. + + while input_index < start_of_rem { + let input_chunk = &input[input_index..(input_index + 3)]; + let output_chunk = &mut output[output_index..(output_index + 4)]; + + output_chunk[0] = encode_table[(input_chunk[0] >> 2) as usize]; + output_chunk[1] = + encode_table[((input_chunk[0] << 4 | input_chunk[1] >> 4) & LOW_SIX_BITS_U8) as usize]; + output_chunk[2] = + encode_table[((input_chunk[1] << 2 | input_chunk[2] >> 6) & LOW_SIX_BITS_U8) as usize]; + output_chunk[3] = encode_table[(input_chunk[2] & LOW_SIX_BITS_U8) as usize]; + + input_index += 3; + output_index += 4; + } + + if rem == 2 { + output[output_index] = encode_table[(input[start_of_rem] >> 2) as usize]; + output[output_index + 1] = encode_table[((input[start_of_rem] << 4 + | input[start_of_rem + 1] >> 4) + & LOW_SIX_BITS_U8) as usize]; + output[output_index + 2] = + encode_table[((input[start_of_rem + 1] << 2) & LOW_SIX_BITS_U8) as usize]; + output_index += 3; + } else if rem == 1 { + output[output_index] = encode_table[(input[start_of_rem] >> 2) as usize]; + output[output_index + 1] = + encode_table[((input[start_of_rem] << 4) & LOW_SIX_BITS_U8) as usize]; + output_index += 2; + } + + output_index +} + +/// calculate the base64 encoded string size, including padding if appropriate +pub fn encoded_size(bytes_len: usize, config: Config) -> Option<usize> { + let rem = bytes_len % 3; + + let complete_input_chunks = bytes_len / 3; + let complete_chunk_output = complete_input_chunks.checked_mul(4); + + if rem > 0 { + if config.pad { + complete_chunk_output.and_then(|c| c.checked_add(4)) + } else { + let encoded_rem = match rem { + 1 => 2, + 2 => 3, + _ => unreachable!("Impossible remainder"), + }; + complete_chunk_output.and_then(|c| c.checked_add(encoded_rem)) + } + } else { + complete_chunk_output + } +} + +/// Write padding characters. +/// `output` is the slice where padding should be written, of length at least 2. +/// +/// Returns the number of padding bytes written. +pub fn add_padding(input_len: usize, output: &mut [u8]) -> usize { + let rem = input_len % 3; + let mut bytes_written = 0; + for _ in 0..((3 - rem) % 3) { + output[bytes_written] = b'='; + bytes_written += 1; + } + + bytes_written +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{ + decode::decode_config_buf, + tests::{assert_encode_sanity, random_config}, + Config, STANDARD, URL_SAFE_NO_PAD, + }; + + use rand::{ + distributions::{Distribution, Uniform}, + FromEntropy, Rng, + }; + use std; + use std::str; + + #[test] + fn encoded_size_correct_standard() { + assert_encoded_length(0, 0, STANDARD); + + assert_encoded_length(1, 4, STANDARD); + assert_encoded_length(2, 4, STANDARD); + assert_encoded_length(3, 4, STANDARD); + + assert_encoded_length(4, 8, STANDARD); + assert_encoded_length(5, 8, STANDARD); + assert_encoded_length(6, 8, STANDARD); + + assert_encoded_length(7, 12, STANDARD); + assert_encoded_length(8, 12, STANDARD); + assert_encoded_length(9, 12, STANDARD); + + assert_encoded_length(54, 72, STANDARD); + + assert_encoded_length(55, 76, STANDARD); + assert_encoded_length(56, 76, STANDARD); + assert_encoded_length(57, 76, STANDARD); + + assert_encoded_length(58, 80, STANDARD); + } + + #[test] + fn encoded_size_correct_no_pad() { + assert_encoded_length(0, 0, URL_SAFE_NO_PAD); + + assert_encoded_length(1, 2, URL_SAFE_NO_PAD); + assert_encoded_length(2, 3, URL_SAFE_NO_PAD); + assert_encoded_length(3, 4, URL_SAFE_NO_PAD); + + assert_encoded_length(4, 6, URL_SAFE_NO_PAD); + assert_encoded_length(5, 7, URL_SAFE_NO_PAD); + assert_encoded_length(6, 8, URL_SAFE_NO_PAD); + + assert_encoded_length(7, 10, URL_SAFE_NO_PAD); + assert_encoded_length(8, 11, URL_SAFE_NO_PAD); + assert_encoded_length(9, 12, URL_SAFE_NO_PAD); + + assert_encoded_length(54, 72, URL_SAFE_NO_PAD); + + assert_encoded_length(55, 74, URL_SAFE_NO_PAD); + assert_encoded_length(56, 75, URL_SAFE_NO_PAD); + assert_encoded_length(57, 76, URL_SAFE_NO_PAD); + + assert_encoded_length(58, 78, URL_SAFE_NO_PAD); + } + + #[test] + fn encoded_size_overflow() { + assert_eq!(None, encoded_size(std::usize::MAX, STANDARD)); + } + + #[test] + fn encode_config_buf_into_nonempty_buffer_doesnt_clobber_prefix() { + let mut orig_data = Vec::new(); + let mut prefix = String::new(); + let mut encoded_data_no_prefix = String::new(); + let mut encoded_data_with_prefix = String::new(); + let mut decoded = Vec::new(); + + let prefix_len_range = Uniform::new(0, 1000); + let input_len_range = Uniform::new(0, 1000); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..10_000 { + orig_data.clear(); + prefix.clear(); + encoded_data_no_prefix.clear(); + encoded_data_with_prefix.clear(); + decoded.clear(); + + let input_len = input_len_range.sample(&mut rng); + + for _ in 0..input_len { + orig_data.push(rng.gen()); + } + + let prefix_len = prefix_len_range.sample(&mut rng); + for _ in 0..prefix_len { + // getting convenient random single-byte printable chars that aren't base64 is + // annoying + prefix.push('#'); + } + encoded_data_with_prefix.push_str(&prefix); + + let config = random_config(&mut rng); + encode_config_buf(&orig_data, config, &mut encoded_data_no_prefix); + encode_config_buf(&orig_data, config, &mut encoded_data_with_prefix); + + assert_eq!( + encoded_data_no_prefix.len() + prefix_len, + encoded_data_with_prefix.len() + ); + assert_encode_sanity(&encoded_data_no_prefix, config, input_len); + assert_encode_sanity(&encoded_data_with_prefix[prefix_len..], config, input_len); + + // append plain encode onto prefix + prefix.push_str(&mut encoded_data_no_prefix); + + assert_eq!(prefix, encoded_data_with_prefix); + + decode_config_buf(&encoded_data_no_prefix, config, &mut decoded).unwrap(); + assert_eq!(orig_data, decoded); + } + } + + #[test] + fn encode_config_slice_into_nonempty_buffer_doesnt_clobber_suffix() { + let mut orig_data = Vec::new(); + let mut encoded_data = Vec::new(); + let mut encoded_data_original_state = Vec::new(); + let mut decoded = Vec::new(); + + let input_len_range = Uniform::new(0, 1000); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..10_000 { + orig_data.clear(); + encoded_data.clear(); + encoded_data_original_state.clear(); + decoded.clear(); + + let input_len = input_len_range.sample(&mut rng); + + for _ in 0..input_len { + orig_data.push(rng.gen()); + } + + // plenty of existing garbage in the encoded buffer + for _ in 0..10 * input_len { + encoded_data.push(rng.gen()); + } + + encoded_data_original_state.extend_from_slice(&encoded_data); + + let config = random_config(&mut rng); + + let encoded_size = encoded_size(input_len, config).unwrap(); + + assert_eq!( + encoded_size, + encode_config_slice(&orig_data, config, &mut encoded_data) + ); + + assert_encode_sanity( + std::str::from_utf8(&encoded_data[0..encoded_size]).unwrap(), + config, + input_len, + ); + + assert_eq!( + &encoded_data[encoded_size..], + &encoded_data_original_state[encoded_size..] + ); + + decode_config_buf(&encoded_data[0..encoded_size], config, &mut decoded).unwrap(); + assert_eq!(orig_data, decoded); + } + } + + #[test] + fn encode_config_slice_fits_into_precisely_sized_slice() { + let mut orig_data = Vec::new(); + let mut encoded_data = Vec::new(); + let mut decoded = Vec::new(); + + let input_len_range = Uniform::new(0, 1000); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..10_000 { + orig_data.clear(); + encoded_data.clear(); + decoded.clear(); + + let input_len = input_len_range.sample(&mut rng); + + for _ in 0..input_len { + orig_data.push(rng.gen()); + } + + let config = random_config(&mut rng); + + let encoded_size = encoded_size(input_len, config).unwrap(); + + encoded_data.resize(encoded_size, 0); + + assert_eq!( + encoded_size, + encode_config_slice(&orig_data, config, &mut encoded_data) + ); + + assert_encode_sanity( + std::str::from_utf8(&encoded_data[0..encoded_size]).unwrap(), + config, + input_len, + ); + + decode_config_buf(&encoded_data[0..encoded_size], config, &mut decoded).unwrap(); + assert_eq!(orig_data, decoded); + } + } + + #[test] + fn encode_to_slice_random_valid_utf8() { + let mut input = Vec::new(); + let mut output = Vec::new(); + + let input_len_range = Uniform::new(0, 1000); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..10_000 { + input.clear(); + output.clear(); + + let input_len = input_len_range.sample(&mut rng); + + for _ in 0..input_len { + input.push(rng.gen()); + } + + let config = random_config(&mut rng); + + // fill up the output buffer with garbage + let encoded_size = encoded_size(input_len, config).unwrap(); + for _ in 0..encoded_size { + output.push(rng.gen()); + } + + let orig_output_buf = output.to_vec(); + + let bytes_written = + encode_to_slice(&input, &mut output, config.char_set.encode_table()); + + // make sure the part beyond bytes_written is the same garbage it was before + assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]); + + // make sure the encoded bytes are UTF-8 + let _ = str::from_utf8(&output[0..bytes_written]).unwrap(); + } + } + + #[test] + fn encode_with_padding_random_valid_utf8() { + let mut input = Vec::new(); + let mut output = Vec::new(); + + let input_len_range = Uniform::new(0, 1000); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..10_000 { + input.clear(); + output.clear(); + + let input_len = input_len_range.sample(&mut rng); + + for _ in 0..input_len { + input.push(rng.gen()); + } + + let config = random_config(&mut rng); + + // fill up the output buffer with garbage + let encoded_size = encoded_size(input_len, config).unwrap(); + for _ in 0..encoded_size + 1000 { + output.push(rng.gen()); + } + + let orig_output_buf = output.to_vec(); + + encode_with_padding(&input, config, encoded_size, &mut output[0..encoded_size]); + + // make sure the part beyond b64 is the same garbage it was before + assert_eq!(orig_output_buf[encoded_size..], output[encoded_size..]); + + // make sure the encoded bytes are UTF-8 + let _ = str::from_utf8(&output[0..encoded_size]).unwrap(); + } + } + + #[test] + fn add_padding_random_valid_utf8() { + let mut output = Vec::new(); + + let mut rng = rand::rngs::SmallRng::from_entropy(); + + // cover our bases for length % 3 + for input_len in 0..10 { + output.clear(); + + // fill output with random + for _ in 0..10 { + output.push(rng.gen()); + } + + let orig_output_buf = output.to_vec(); + + let bytes_written = add_padding(input_len, &mut output); + + // make sure the part beyond bytes_written is the same garbage it was before + assert_eq!(orig_output_buf[bytes_written..], output[bytes_written..]); + + // make sure the encoded bytes are UTF-8 + let _ = str::from_utf8(&output[0..bytes_written]).unwrap(); + } + } + + fn assert_encoded_length(input_len: usize, encoded_len: usize, config: Config) { + assert_eq!(encoded_len, encoded_size(input_len, config).unwrap()); + + let mut bytes: Vec<u8> = Vec::new(); + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..input_len { + bytes.push(rng.gen()); + } + + let encoded = encode_config(&bytes, config); + assert_encode_sanity(&encoded, config, input_len); + + assert_eq!(encoded_len, encoded.len()); + } + + #[test] + fn encode_imap() { + assert_eq!( + encode_config(b"\xFB\xFF", crate::IMAP_MUTF7), + encode_config(b"\xFB\xFF", crate::STANDARD_NO_PAD).replace("/", ",") + ); + } +} diff --git a/third_party/rust/base64/src/lib.rs b/third_party/rust/base64/src/lib.rs new file mode 100644 index 0000000000..dc92cc6ffd --- /dev/null +++ b/third_party/rust/base64/src/lib.rs @@ -0,0 +1,226 @@ +//! # Configs +//! +//! There isn't just one type of Base64; that would be too simple. You need to choose a character +//! set (standard, URL-safe, etc) and padding suffix (yes/no). +//! The `Config` struct encapsulates this info. There are some common configs included: `STANDARD`, +//! `URL_SAFE`, etc. You can also make your own `Config` if needed. +//! +//! The functions that don't have `config` in the name (e.g. `encode()` and `decode()`) use the +//! `STANDARD` config . +//! +//! The functions that write to a slice (the ones that end in `_slice`) are generally the fastest +//! because they don't need to resize anything. If it fits in your workflow and you care about +//! performance, keep using the same buffer (growing as need be) and use the `_slice` methods for +//! the best performance. +//! +//! # Encoding +//! +//! Several different encoding functions are available to you depending on your desire for +//! convenience vs performance. +//! +//! | Function | Output | Allocates | +//! | ----------------------- | ---------------------------- | ------------------------------ | +//! | `encode` | Returns a new `String` | Always | +//! | `encode_config` | Returns a new `String` | Always | +//! | `encode_config_buf` | Appends to provided `String` | Only if `String` needs to grow | +//! | `encode_config_slice` | Writes to provided `&[u8]` | Never | +//! +//! All of the encoding functions that take a `Config` will pad as per the config. +//! +//! # Decoding +//! +//! Just as for encoding, there are different decoding functions available. +//! +//! | Function | Output | Allocates | +//! | ----------------------- | ----------------------------- | ------------------------------ | +//! | `decode` | Returns a new `Vec<u8>` | Always | +//! | `decode_config` | Returns a new `Vec<u8>` | Always | +//! | `decode_config_buf` | Appends to provided `Vec<u8>` | Only if `Vec` needs to grow | +//! | `decode_config_slice` | Writes to provided `&[u8]` | Never | +//! +//! Unlike encoding, where all possible input is valid, decoding can fail (see `DecodeError`). +//! +//! Input can be invalid because it has invalid characters or invalid padding. (No padding at all is +//! valid, but excess padding is not.) Whitespace in the input is invalid. +//! +//! # `Read` and `Write` +//! +//! To map a `Read` of b64 bytes to the decoded bytes, wrap a reader (file, network socket, etc) +//! with `base64::read::DecoderReader`. To write raw bytes and have them b64 encoded on the fly, +//! wrap a writer with `base64::write::EncoderWriter`. There is some performance overhead (15% or +//! so) because of the necessary buffer shuffling -- still fast enough that almost nobody cares. +//! Also, these implementations do not heap allocate. +//! +//! # Panics +//! +//! If length calculations result in overflowing `usize`, a panic will result. +//! +//! The `_slice` flavors of encode or decode will panic if the provided output slice is too small, + +#![cfg_attr(feature = "cargo-clippy", allow(clippy::cast_lossless))] +#![deny( + missing_docs, + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_results, + variant_size_differences, + warnings +)] +#![forbid(unsafe_code)] +#![cfg_attr(not(any(feature = "std", test)), no_std)] + +#[cfg(all(feature = "alloc", not(any(feature = "std", test))))] +extern crate alloc; +#[cfg(any(feature = "std", test))] +extern crate std as alloc; + +#[cfg(test)] +#[macro_use] +extern crate doc_comment; + +#[cfg(test)] +doctest!("../README.md"); + +mod chunked_encoder; +pub mod display; +#[cfg(any(feature = "std", test))] +pub mod read; +mod tables; +#[cfg(any(feature = "std", test))] +pub mod write; + +mod encode; +pub use crate::encode::encode_config_slice; +#[cfg(any(feature = "alloc", feature = "std", test))] +pub use crate::encode::{encode, encode_config, encode_config_buf}; + +mod decode; +#[cfg(any(feature = "alloc", feature = "std", test))] +pub use crate::decode::{decode, decode_config, decode_config_buf}; +pub use crate::decode::{decode_config_slice, DecodeError}; + +#[cfg(test)] +mod tests; + +/// Available encoding character sets +#[derive(Clone, Copy, Debug)] +pub enum CharacterSet { + /// The standard character set (uses `+` and `/`). + /// + /// See [RFC 3548](https://tools.ietf.org/html/rfc3548#section-3). + Standard, + /// The URL safe character set (uses `-` and `_`). + /// + /// See [RFC 3548](https://tools.ietf.org/html/rfc3548#section-4). + UrlSafe, + /// The `crypt(3)` character set (uses `./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz`). + /// + /// Not standardized, but folk wisdom on the net asserts that this alphabet is what crypt uses. + Crypt, + /// The character set used in IMAP-modified UTF-7 (uses `+` and `,`). + /// + /// See [RFC 3501](https://tools.ietf.org/html/rfc3501#section-5.1.3) + ImapMutf7, +} + +impl CharacterSet { + fn encode_table(self) -> &'static [u8; 64] { + match self { + CharacterSet::Standard => tables::STANDARD_ENCODE, + CharacterSet::UrlSafe => tables::URL_SAFE_ENCODE, + CharacterSet::Crypt => tables::CRYPT_ENCODE, + CharacterSet::ImapMutf7 => tables::IMAP_MUTF7_ENCODE, + } + } + + fn decode_table(self) -> &'static [u8; 256] { + match self { + CharacterSet::Standard => tables::STANDARD_DECODE, + CharacterSet::UrlSafe => tables::URL_SAFE_DECODE, + CharacterSet::Crypt => tables::CRYPT_DECODE, + CharacterSet::ImapMutf7 => tables::IMAP_MUTF7_DECODE, + } + } +} + +/// Contains configuration parameters for base64 encoding +#[derive(Clone, Copy, Debug)] +pub struct Config { + /// Character set to use + char_set: CharacterSet, + /// True to pad output with `=` characters + pad: bool, + /// True to ignore excess nonzero bits in the last few symbols, otherwise an error is returned. + decode_allow_trailing_bits: bool, +} + +impl Config { + /// Create a new `Config`. + pub fn new(char_set: CharacterSet, pad: bool) -> Config { + Config { + char_set, + pad, + decode_allow_trailing_bits: false, + } + } + + /// Sets whether to pad output with `=` characters. + pub fn pad(self, pad: bool) -> Config { + Config { pad, ..self } + } + + /// Sets whether to emit errors for nonzero trailing bits. + /// + /// This is useful when implementing + /// [forgiving-base64 decode](https://infra.spec.whatwg.org/#forgiving-base64-decode). + pub fn decode_allow_trailing_bits(self, allow: bool) -> Config { + Config { + decode_allow_trailing_bits: allow, + ..self + } + } +} + +/// Standard character set with padding. +pub const STANDARD: Config = Config { + char_set: CharacterSet::Standard, + pad: true, + decode_allow_trailing_bits: false, +}; + +/// Standard character set without padding. +pub const STANDARD_NO_PAD: Config = Config { + char_set: CharacterSet::Standard, + pad: false, + decode_allow_trailing_bits: false, +}; + +/// URL-safe character set with padding +pub const URL_SAFE: Config = Config { + char_set: CharacterSet::UrlSafe, + pad: true, + decode_allow_trailing_bits: false, +}; + +/// URL-safe character set without padding +pub const URL_SAFE_NO_PAD: Config = Config { + char_set: CharacterSet::UrlSafe, + pad: false, + decode_allow_trailing_bits: false, +}; + +/// As per `crypt(3)` requirements +pub const CRYPT: Config = Config { + char_set: CharacterSet::Crypt, + pad: false, + decode_allow_trailing_bits: false, +}; + +/// IMAP modified UTF-7 requirements +pub const IMAP_MUTF7: Config = Config { + char_set: CharacterSet::ImapMutf7, + pad: false, + decode_allow_trailing_bits: false, +}; diff --git a/third_party/rust/base64/src/read/decoder.rs b/third_party/rust/base64/src/read/decoder.rs new file mode 100644 index 0000000000..7a9c4cd292 --- /dev/null +++ b/third_party/rust/base64/src/read/decoder.rs @@ -0,0 +1,282 @@ +use crate::{decode_config_slice, Config, DecodeError}; +use std::io::Read; +use std::{cmp, fmt, io}; + +// This should be large, but it has to fit on the stack. +pub(crate) const BUF_SIZE: usize = 1024; + +// 4 bytes of base64 data encode 3 bytes of raw data (modulo padding). +const BASE64_CHUNK_SIZE: usize = 4; +const DECODED_CHUNK_SIZE: usize = 3; + +/// A `Read` implementation that decodes base64 data read from an underlying reader. +/// +/// # Examples +/// +/// ``` +/// use std::io::Read; +/// use std::io::Cursor; +/// +/// // use a cursor as the simplest possible `Read` -- in real code this is probably a file, etc. +/// let mut wrapped_reader = Cursor::new(b"YXNkZg=="); +/// let mut decoder = base64::read::DecoderReader::new( +/// &mut wrapped_reader, base64::STANDARD); +/// +/// // handle errors as you normally would +/// let mut result = Vec::new(); +/// decoder.read_to_end(&mut result).unwrap(); +/// +/// assert_eq!(b"asdf", &result[..]); +/// +/// ``` +pub struct DecoderReader<'a, R: 'a + io::Read> { + config: Config, + /// Where b64 data is read from + r: &'a mut R, + + // Holds b64 data read from the delegate reader. + b64_buffer: [u8; BUF_SIZE], + // The start of the pending buffered data in b64_buffer. + b64_offset: usize, + // The amount of buffered b64 data. + b64_len: usize, + // Since the caller may provide us with a buffer of size 1 or 2 that's too small to copy a + // decoded chunk in to, we have to be able to hang on to a few decoded bytes. + // Technically we only need to hold 2 bytes but then we'd need a separate temporary buffer to + // decode 3 bytes into and then juggle copying one byte into the provided read buf and the rest + // into here, which seems like a lot of complexity for 1 extra byte of storage. + decoded_buffer: [u8; 3], + // index of start of decoded data + decoded_offset: usize, + // length of decoded data + decoded_len: usize, + // used to provide accurate offsets in errors + total_b64_decoded: usize, +} + +impl<'a, R: io::Read> fmt::Debug for DecoderReader<'a, R> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("DecoderReader") + .field("config", &self.config) + .field("b64_offset", &self.b64_offset) + .field("b64_len", &self.b64_len) + .field("decoded_buffer", &self.decoded_buffer) + .field("decoded_offset", &self.decoded_offset) + .field("decoded_len", &self.decoded_len) + .field("total_b64_decoded", &self.total_b64_decoded) + .finish() + } +} + +impl<'a, R: io::Read> DecoderReader<'a, R> { + /// Create a new decoder that will read from the provided reader `r`. + pub fn new(r: &'a mut R, config: Config) -> Self { + DecoderReader { + config, + r, + b64_buffer: [0; BUF_SIZE], + b64_offset: 0, + b64_len: 0, + decoded_buffer: [0; DECODED_CHUNK_SIZE], + decoded_offset: 0, + decoded_len: 0, + total_b64_decoded: 0, + } + } + + /// Write as much as possible of the decoded buffer into the target buffer. + /// Must only be called when there is something to write and space to write into. + /// Returns a Result with the number of (decoded) bytes copied. + fn flush_decoded_buf(&mut self, buf: &mut [u8]) -> io::Result<usize> { + debug_assert!(self.decoded_len > 0); + debug_assert!(buf.len() > 0); + + let copy_len = cmp::min(self.decoded_len, buf.len()); + debug_assert!(copy_len > 0); + debug_assert!(copy_len <= self.decoded_len); + + buf[..copy_len].copy_from_slice( + &self.decoded_buffer[self.decoded_offset..self.decoded_offset + copy_len], + ); + + self.decoded_offset += copy_len; + self.decoded_len -= copy_len; + + debug_assert!(self.decoded_len < DECODED_CHUNK_SIZE); + + Ok(copy_len) + } + + /// Read into the remaining space in the buffer after the current contents. + /// Must only be called when there is space to read into in the buffer. + /// Returns the number of bytes read. + fn read_from_delegate(&mut self) -> io::Result<usize> { + debug_assert!(self.b64_offset + self.b64_len < BUF_SIZE); + + let read = self + .r + .read(&mut self.b64_buffer[self.b64_offset + self.b64_len..])?; + self.b64_len += read; + + debug_assert!(self.b64_offset + self.b64_len <= BUF_SIZE); + + return Ok(read); + } + + /// Decode the requested number of bytes from the b64 buffer into the provided buffer. It's the + /// caller's responsibility to choose the number of b64 bytes to decode correctly. + /// + /// Returns a Result with the number of decoded bytes written to `buf`. + fn decode_to_buf(&mut self, num_bytes: usize, buf: &mut [u8]) -> io::Result<usize> { + debug_assert!(self.b64_len >= num_bytes); + debug_assert!(self.b64_offset + self.b64_len <= BUF_SIZE); + debug_assert!(buf.len() > 0); + + let decoded = decode_config_slice( + &self.b64_buffer[self.b64_offset..self.b64_offset + num_bytes], + self.config, + &mut buf[..], + ) + .map_err(|e| match e { + DecodeError::InvalidByte(offset, byte) => { + DecodeError::InvalidByte(self.total_b64_decoded + offset, byte) + } + DecodeError::InvalidLength => DecodeError::InvalidLength, + DecodeError::InvalidLastSymbol(offset, byte) => { + DecodeError::InvalidLastSymbol(self.total_b64_decoded + offset, byte) + } + }) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + self.total_b64_decoded += num_bytes; + self.b64_offset += num_bytes; + self.b64_len -= num_bytes; + + debug_assert!(self.b64_offset + self.b64_len <= BUF_SIZE); + + Ok(decoded) + } +} + +impl<'a, R: Read> Read for DecoderReader<'a, R> { + /// Decode input from the wrapped reader. + /// + /// Under non-error circumstances, this returns `Ok` with the value being the number of bytes + /// written in `buf`. + /// + /// Where possible, this function buffers base64 to minimize the number of read() calls to the + /// delegate reader. + /// + /// # Errors + /// + /// Any errors emitted by the delegate reader are returned. Decoding errors due to invalid + /// base64 are also possible, and will have `io::ErrorKind::InvalidData`. + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + if buf.len() == 0 { + return Ok(0); + } + + // offset == BUF_SIZE when we copied it all last time + debug_assert!(self.b64_offset <= BUF_SIZE); + debug_assert!(self.b64_offset + self.b64_len <= BUF_SIZE); + debug_assert!(if self.b64_offset == BUF_SIZE { + self.b64_len == 0 + } else { + self.b64_len <= BUF_SIZE + }); + + debug_assert!(if self.decoded_len == 0 { + // can be = when we were able to copy the complete chunk + self.decoded_offset <= DECODED_CHUNK_SIZE + } else { + self.decoded_offset < DECODED_CHUNK_SIZE + }); + + // We shouldn't ever decode into here when we can't immediately write at least one byte into + // the provided buf, so the effective length should only be 3 momentarily between when we + // decode and when we copy into the target buffer. + debug_assert!(self.decoded_len < DECODED_CHUNK_SIZE); + debug_assert!(self.decoded_len + self.decoded_offset <= DECODED_CHUNK_SIZE); + + if self.decoded_len > 0 { + // we have a few leftover decoded bytes; flush that rather than pull in more b64 + self.flush_decoded_buf(buf) + } else { + let mut at_eof = false; + while self.b64_len < BASE64_CHUNK_SIZE { + // Work around lack of copy_within, which is only present in 1.37 + // Copy any bytes we have to the start of the buffer. + // We know we have < 1 chunk, so we can use a tiny tmp buffer. + let mut memmove_buf = [0_u8; BASE64_CHUNK_SIZE]; + memmove_buf[..self.b64_len].copy_from_slice( + &self.b64_buffer[self.b64_offset..self.b64_offset + self.b64_len], + ); + self.b64_buffer[0..self.b64_len].copy_from_slice(&memmove_buf[..self.b64_len]); + self.b64_offset = 0; + + // then fill in more data + let read = self.read_from_delegate()?; + if read == 0 { + // we never pass in an empty buf, so 0 => we've hit EOF + at_eof = true; + break; + } + } + + if self.b64_len == 0 { + debug_assert!(at_eof); + // we must be at EOF, and we have no data left to decode + return Ok(0); + }; + + debug_assert!(if at_eof { + // if we are at eof, we may not have a complete chunk + self.b64_len > 0 + } else { + // otherwise, we must have at least one chunk + self.b64_len >= BASE64_CHUNK_SIZE + }); + + debug_assert_eq!(0, self.decoded_len); + + if buf.len() < DECODED_CHUNK_SIZE { + // caller requested an annoyingly short read + // have to write to a tmp buf first to avoid double mutable borrow + let mut decoded_chunk = [0_u8; DECODED_CHUNK_SIZE]; + // if we are at eof, could have less than BASE64_CHUNK_SIZE, in which case we have + // to assume that these last few tokens are, in fact, valid (i.e. must be 2-4 b64 + // tokens, not 1, since 1 token can't decode to 1 byte). + let to_decode = cmp::min(self.b64_len, BASE64_CHUNK_SIZE); + + let decoded = self.decode_to_buf(to_decode, &mut decoded_chunk[..])?; + self.decoded_buffer[..decoded].copy_from_slice(&decoded_chunk[..decoded]); + + self.decoded_offset = 0; + self.decoded_len = decoded; + + // can be less than 3 on last block due to padding + debug_assert!(decoded <= 3); + + self.flush_decoded_buf(buf) + } else { + let b64_bytes_that_can_decode_into_buf = (buf.len() / DECODED_CHUNK_SIZE) + .checked_mul(BASE64_CHUNK_SIZE) + .expect("too many chunks"); + debug_assert!(b64_bytes_that_can_decode_into_buf >= BASE64_CHUNK_SIZE); + + let b64_bytes_available_to_decode = if at_eof { + self.b64_len + } else { + // only use complete chunks + self.b64_len - self.b64_len % 4 + }; + + let actual_decode_len = cmp::min( + b64_bytes_that_can_decode_into_buf, + b64_bytes_available_to_decode, + ); + self.decode_to_buf(actual_decode_len, buf) + } + } + } +} diff --git a/third_party/rust/base64/src/read/decoder_tests.rs b/third_party/rust/base64/src/read/decoder_tests.rs new file mode 100644 index 0000000000..265d423a6a --- /dev/null +++ b/third_party/rust/base64/src/read/decoder_tests.rs @@ -0,0 +1,335 @@ +use std::io::{self, Read}; + +use rand::{Rng, RngCore}; +use std::{cmp, iter}; + +use super::decoder::{DecoderReader, BUF_SIZE}; +use crate::encode::encode_config_buf; +use crate::tests::random_config; +use crate::{decode_config_buf, DecodeError, STANDARD}; + +#[test] +fn simple() { + let tests: &[(&[u8], &[u8])] = &[ + (&b"0"[..], &b"MA=="[..]), + (b"01", b"MDE="), + (b"012", b"MDEy"), + (b"0123", b"MDEyMw=="), + (b"01234", b"MDEyMzQ="), + (b"012345", b"MDEyMzQ1"), + (b"0123456", b"MDEyMzQ1Ng=="), + (b"01234567", b"MDEyMzQ1Njc="), + (b"012345678", b"MDEyMzQ1Njc4"), + (b"0123456789", b"MDEyMzQ1Njc4OQ=="), + ][..]; + + for (text_expected, base64data) in tests.iter() { + // Read n bytes at a time. + for n in 1..base64data.len() + 1 { + let mut wrapped_reader = io::Cursor::new(base64data); + let mut decoder = DecoderReader::new(&mut wrapped_reader, STANDARD); + + // handle errors as you normally would + let mut text_got = Vec::new(); + let mut buffer = vec![0u8; n]; + while let Ok(read) = decoder.read(&mut buffer[..]) { + if read == 0 { + break; + } + text_got.extend_from_slice(&buffer[..read]); + } + + assert_eq!( + text_got, + *text_expected, + "\nGot: {}\nExpected: {}", + String::from_utf8_lossy(&text_got[..]), + String::from_utf8_lossy(text_expected) + ); + } + } +} + +// Make sure we error out on trailing junk. +#[test] +fn trailing_junk() { + let tests: &[&[u8]] = &[&b"MDEyMzQ1Njc4*!@#$%^&"[..], b"MDEyMzQ1Njc4OQ== "][..]; + + for base64data in tests.iter() { + // Read n bytes at a time. + for n in 1..base64data.len() + 1 { + let mut wrapped_reader = io::Cursor::new(base64data); + let mut decoder = DecoderReader::new(&mut wrapped_reader, STANDARD); + + // handle errors as you normally would + let mut buffer = vec![0u8; n]; + let mut saw_error = false; + loop { + match decoder.read(&mut buffer[..]) { + Err(_) => { + saw_error = true; + break; + } + Ok(read) if read == 0 => break, + Ok(_) => (), + } + } + + assert!(saw_error); + } + } +} + +#[test] +fn handles_short_read_from_delegate() { + let mut rng = rand::thread_rng(); + let mut bytes = Vec::new(); + let mut b64 = String::new(); + let mut decoded = Vec::new(); + + for _ in 0..10_000 { + bytes.clear(); + b64.clear(); + decoded.clear(); + + let size = rng.gen_range(0, 10 * BUF_SIZE); + bytes.extend(iter::repeat(0).take(size)); + bytes.truncate(size); + rng.fill_bytes(&mut bytes[..size]); + assert_eq!(size, bytes.len()); + + let config = random_config(&mut rng); + encode_config_buf(&bytes[..], config, &mut b64); + + let mut wrapped_reader = io::Cursor::new(b64.as_bytes()); + let mut short_reader = RandomShortRead { + delegate: &mut wrapped_reader, + rng: &mut rng, + }; + + let mut decoder = DecoderReader::new(&mut short_reader, config); + + let decoded_len = decoder.read_to_end(&mut decoded).unwrap(); + assert_eq!(size, decoded_len); + assert_eq!(&bytes[..], &decoded[..]); + } +} + +#[test] +fn read_in_short_increments() { + let mut rng = rand::thread_rng(); + let mut bytes = Vec::new(); + let mut b64 = String::new(); + let mut decoded = Vec::new(); + + for _ in 0..10_000 { + bytes.clear(); + b64.clear(); + decoded.clear(); + + let size = rng.gen_range(0, 10 * BUF_SIZE); + bytes.extend(iter::repeat(0).take(size)); + // leave room to play around with larger buffers + decoded.extend(iter::repeat(0).take(size * 3)); + + rng.fill_bytes(&mut bytes[..]); + assert_eq!(size, bytes.len()); + + let config = random_config(&mut rng); + + encode_config_buf(&bytes[..], config, &mut b64); + + let mut wrapped_reader = io::Cursor::new(&b64[..]); + let mut decoder = DecoderReader::new(&mut wrapped_reader, config); + + consume_with_short_reads_and_validate(&mut rng, &bytes[..], &mut decoded, &mut decoder); + } +} + +#[test] +fn read_in_short_increments_with_short_delegate_reads() { + let mut rng = rand::thread_rng(); + let mut bytes = Vec::new(); + let mut b64 = String::new(); + let mut decoded = Vec::new(); + + for _ in 0..10_000 { + bytes.clear(); + b64.clear(); + decoded.clear(); + + let size = rng.gen_range(0, 10 * BUF_SIZE); + bytes.extend(iter::repeat(0).take(size)); + // leave room to play around with larger buffers + decoded.extend(iter::repeat(0).take(size * 3)); + + rng.fill_bytes(&mut bytes[..]); + assert_eq!(size, bytes.len()); + + let config = random_config(&mut rng); + + encode_config_buf(&bytes[..], config, &mut b64); + + let mut base_reader = io::Cursor::new(&b64[..]); + let mut decoder = DecoderReader::new(&mut base_reader, config); + let mut short_reader = RandomShortRead { + delegate: &mut decoder, + rng: &mut rand::thread_rng(), + }; + + consume_with_short_reads_and_validate(&mut rng, &bytes[..], &mut decoded, &mut short_reader) + } +} + +#[test] +fn reports_invalid_last_symbol_correctly() { + let mut rng = rand::thread_rng(); + let mut bytes = Vec::new(); + let mut b64 = String::new(); + let mut b64_bytes = Vec::new(); + let mut decoded = Vec::new(); + let mut bulk_decoded = Vec::new(); + + for _ in 0..1_000 { + bytes.clear(); + b64.clear(); + b64_bytes.clear(); + + let size = rng.gen_range(1, 10 * BUF_SIZE); + bytes.extend(iter::repeat(0).take(size)); + decoded.extend(iter::repeat(0).take(size)); + rng.fill_bytes(&mut bytes[..]); + assert_eq!(size, bytes.len()); + + let mut config = random_config(&mut rng); + // changing padding will cause invalid padding errors when we twiddle the last byte + config.pad = false; + + encode_config_buf(&bytes[..], config, &mut b64); + b64_bytes.extend(b64.bytes()); + assert_eq!(b64_bytes.len(), b64.len()); + + // change the last character to every possible symbol. Should behave the same as bulk + // decoding whether invalid or valid. + for &s1 in config.char_set.encode_table().iter() { + decoded.clear(); + bulk_decoded.clear(); + + // replace the last + *b64_bytes.last_mut().unwrap() = s1; + let bulk_res = decode_config_buf(&b64_bytes[..], config, &mut bulk_decoded); + + let mut wrapped_reader = io::Cursor::new(&b64_bytes[..]); + let mut decoder = DecoderReader::new(&mut wrapped_reader, config); + + let stream_res = decoder.read_to_end(&mut decoded).map(|_| ()).map_err(|e| { + e.into_inner() + .and_then(|e| e.downcast::<DecodeError>().ok()) + }); + + assert_eq!(bulk_res.map_err(|e| Some(Box::new(e))), stream_res); + } + } +} + +#[test] +fn reports_invalid_byte_correctly() { + let mut rng = rand::thread_rng(); + let mut bytes = Vec::new(); + let mut b64 = String::new(); + let mut decoded = Vec::new(); + + for _ in 0..10_000 { + bytes.clear(); + b64.clear(); + decoded.clear(); + + let size = rng.gen_range(1, 10 * BUF_SIZE); + bytes.extend(iter::repeat(0).take(size)); + rng.fill_bytes(&mut bytes[..size]); + assert_eq!(size, bytes.len()); + + let config = random_config(&mut rng); + encode_config_buf(&bytes[..], config, &mut b64); + // replace one byte, somewhere, with '*', which is invalid + let bad_byte_pos = rng.gen_range(0, &b64.len()); + let mut b64_bytes = b64.bytes().collect::<Vec<u8>>(); + b64_bytes[bad_byte_pos] = b'*'; + + let mut wrapped_reader = io::Cursor::new(b64_bytes.clone()); + let mut decoder = DecoderReader::new(&mut wrapped_reader, config); + + // some gymnastics to avoid double-moving the io::Error, which is not Copy + let read_decode_err = decoder + .read_to_end(&mut decoded) + .map_err(|e| { + let kind = e.kind(); + let inner = e + .into_inner() + .and_then(|e| e.downcast::<DecodeError>().ok()); + inner.map(|i| (*i, kind)) + }) + .err() + .and_then(|o| o); + + let mut bulk_buf = Vec::new(); + let bulk_decode_err = decode_config_buf(&b64_bytes[..], config, &mut bulk_buf).err(); + + // it's tricky to predict where the invalid data's offset will be since if it's in the last + // chunk it will be reported at the first padding location because it's treated as invalid + // padding. So, we just check that it's the same as it is for decoding all at once. + assert_eq!( + bulk_decode_err.map(|e| (e, io::ErrorKind::InvalidData)), + read_decode_err + ); + } +} + +fn consume_with_short_reads_and_validate<R: Read>( + rng: &mut rand::rngs::ThreadRng, + expected_bytes: &[u8], + decoded: &mut Vec<u8>, + short_reader: &mut R, +) -> () { + let mut total_read = 0_usize; + loop { + assert!( + total_read <= expected_bytes.len(), + "tr {} size {}", + total_read, + expected_bytes.len() + ); + if total_read == expected_bytes.len() { + assert_eq!(expected_bytes, &decoded[..total_read]); + // should be done + assert_eq!(0, short_reader.read(&mut decoded[..]).unwrap()); + // didn't write anything + assert_eq!(expected_bytes, &decoded[..total_read]); + + break; + } + let decode_len = rng.gen_range(1, cmp::max(2, expected_bytes.len() * 2)); + + let read = short_reader + .read(&mut decoded[total_read..total_read + decode_len]) + .unwrap(); + total_read += read; + } +} + +/// Limits how many bytes a reader will provide in each read call. +/// Useful for shaking out code that may work fine only with typical input sources that always fill +/// the buffer. +struct RandomShortRead<'a, 'b, R: io::Read, N: rand::Rng> { + delegate: &'b mut R, + rng: &'a mut N, +} + +impl<'a, 'b, R: io::Read, N: rand::Rng> io::Read for RandomShortRead<'a, 'b, R, N> { + fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> { + // avoid 0 since it means EOF for non-empty buffers + let effective_len = cmp::min(self.rng.gen_range(1, 20), buf.len()); + + self.delegate.read(&mut buf[..effective_len]) + } +} diff --git a/third_party/rust/base64/src/read/mod.rs b/third_party/rust/base64/src/read/mod.rs new file mode 100644 index 0000000000..856064481c --- /dev/null +++ b/third_party/rust/base64/src/read/mod.rs @@ -0,0 +1,6 @@ +//! Implementations of `io::Read` to transparently decode base64. +mod decoder; +pub use self::decoder::DecoderReader; + +#[cfg(test)] +mod decoder_tests; diff --git a/third_party/rust/base64/src/tables.rs b/third_party/rust/base64/src/tables.rs new file mode 100644 index 0000000000..54afd2033c --- /dev/null +++ b/third_party/rust/base64/src/tables.rs @@ -0,0 +1,1305 @@ +pub const INVALID_VALUE: u8 = 255; +#[rustfmt::skip] +pub const STANDARD_ENCODE: &[u8; 64] = &[ + 65, // input 0 (0x0) => 'A' (0x41) + 66, // input 1 (0x1) => 'B' (0x42) + 67, // input 2 (0x2) => 'C' (0x43) + 68, // input 3 (0x3) => 'D' (0x44) + 69, // input 4 (0x4) => 'E' (0x45) + 70, // input 5 (0x5) => 'F' (0x46) + 71, // input 6 (0x6) => 'G' (0x47) + 72, // input 7 (0x7) => 'H' (0x48) + 73, // input 8 (0x8) => 'I' (0x49) + 74, // input 9 (0x9) => 'J' (0x4A) + 75, // input 10 (0xA) => 'K' (0x4B) + 76, // input 11 (0xB) => 'L' (0x4C) + 77, // input 12 (0xC) => 'M' (0x4D) + 78, // input 13 (0xD) => 'N' (0x4E) + 79, // input 14 (0xE) => 'O' (0x4F) + 80, // input 15 (0xF) => 'P' (0x50) + 81, // input 16 (0x10) => 'Q' (0x51) + 82, // input 17 (0x11) => 'R' (0x52) + 83, // input 18 (0x12) => 'S' (0x53) + 84, // input 19 (0x13) => 'T' (0x54) + 85, // input 20 (0x14) => 'U' (0x55) + 86, // input 21 (0x15) => 'V' (0x56) + 87, // input 22 (0x16) => 'W' (0x57) + 88, // input 23 (0x17) => 'X' (0x58) + 89, // input 24 (0x18) => 'Y' (0x59) + 90, // input 25 (0x19) => 'Z' (0x5A) + 97, // input 26 (0x1A) => 'a' (0x61) + 98, // input 27 (0x1B) => 'b' (0x62) + 99, // input 28 (0x1C) => 'c' (0x63) + 100, // input 29 (0x1D) => 'd' (0x64) + 101, // input 30 (0x1E) => 'e' (0x65) + 102, // input 31 (0x1F) => 'f' (0x66) + 103, // input 32 (0x20) => 'g' (0x67) + 104, // input 33 (0x21) => 'h' (0x68) + 105, // input 34 (0x22) => 'i' (0x69) + 106, // input 35 (0x23) => 'j' (0x6A) + 107, // input 36 (0x24) => 'k' (0x6B) + 108, // input 37 (0x25) => 'l' (0x6C) + 109, // input 38 (0x26) => 'm' (0x6D) + 110, // input 39 (0x27) => 'n' (0x6E) + 111, // input 40 (0x28) => 'o' (0x6F) + 112, // input 41 (0x29) => 'p' (0x70) + 113, // input 42 (0x2A) => 'q' (0x71) + 114, // input 43 (0x2B) => 'r' (0x72) + 115, // input 44 (0x2C) => 's' (0x73) + 116, // input 45 (0x2D) => 't' (0x74) + 117, // input 46 (0x2E) => 'u' (0x75) + 118, // input 47 (0x2F) => 'v' (0x76) + 119, // input 48 (0x30) => 'w' (0x77) + 120, // input 49 (0x31) => 'x' (0x78) + 121, // input 50 (0x32) => 'y' (0x79) + 122, // input 51 (0x33) => 'z' (0x7A) + 48, // input 52 (0x34) => '0' (0x30) + 49, // input 53 (0x35) => '1' (0x31) + 50, // input 54 (0x36) => '2' (0x32) + 51, // input 55 (0x37) => '3' (0x33) + 52, // input 56 (0x38) => '4' (0x34) + 53, // input 57 (0x39) => '5' (0x35) + 54, // input 58 (0x3A) => '6' (0x36) + 55, // input 59 (0x3B) => '7' (0x37) + 56, // input 60 (0x3C) => '8' (0x38) + 57, // input 61 (0x3D) => '9' (0x39) + 43, // input 62 (0x3E) => '+' (0x2B) + 47, // input 63 (0x3F) => '/' (0x2F) +]; +#[rustfmt::skip] +pub const STANDARD_DECODE: &[u8; 256] = &[ + INVALID_VALUE, // input 0 (0x0) + INVALID_VALUE, // input 1 (0x1) + INVALID_VALUE, // input 2 (0x2) + INVALID_VALUE, // input 3 (0x3) + INVALID_VALUE, // input 4 (0x4) + INVALID_VALUE, // input 5 (0x5) + INVALID_VALUE, // input 6 (0x6) + INVALID_VALUE, // input 7 (0x7) + INVALID_VALUE, // input 8 (0x8) + INVALID_VALUE, // input 9 (0x9) + INVALID_VALUE, // input 10 (0xA) + INVALID_VALUE, // input 11 (0xB) + INVALID_VALUE, // input 12 (0xC) + INVALID_VALUE, // input 13 (0xD) + INVALID_VALUE, // input 14 (0xE) + INVALID_VALUE, // input 15 (0xF) + INVALID_VALUE, // input 16 (0x10) + INVALID_VALUE, // input 17 (0x11) + INVALID_VALUE, // input 18 (0x12) + INVALID_VALUE, // input 19 (0x13) + INVALID_VALUE, // input 20 (0x14) + INVALID_VALUE, // input 21 (0x15) + INVALID_VALUE, // input 22 (0x16) + INVALID_VALUE, // input 23 (0x17) + INVALID_VALUE, // input 24 (0x18) + INVALID_VALUE, // input 25 (0x19) + INVALID_VALUE, // input 26 (0x1A) + INVALID_VALUE, // input 27 (0x1B) + INVALID_VALUE, // input 28 (0x1C) + INVALID_VALUE, // input 29 (0x1D) + INVALID_VALUE, // input 30 (0x1E) + INVALID_VALUE, // input 31 (0x1F) + INVALID_VALUE, // input 32 (0x20) + INVALID_VALUE, // input 33 (0x21) + INVALID_VALUE, // input 34 (0x22) + INVALID_VALUE, // input 35 (0x23) + INVALID_VALUE, // input 36 (0x24) + INVALID_VALUE, // input 37 (0x25) + INVALID_VALUE, // input 38 (0x26) + INVALID_VALUE, // input 39 (0x27) + INVALID_VALUE, // input 40 (0x28) + INVALID_VALUE, // input 41 (0x29) + INVALID_VALUE, // input 42 (0x2A) + 62, // input 43 (0x2B char '+') => 62 (0x3E) + INVALID_VALUE, // input 44 (0x2C) + INVALID_VALUE, // input 45 (0x2D) + INVALID_VALUE, // input 46 (0x2E) + 63, // input 47 (0x2F char '/') => 63 (0x3F) + 52, // input 48 (0x30 char '0') => 52 (0x34) + 53, // input 49 (0x31 char '1') => 53 (0x35) + 54, // input 50 (0x32 char '2') => 54 (0x36) + 55, // input 51 (0x33 char '3') => 55 (0x37) + 56, // input 52 (0x34 char '4') => 56 (0x38) + 57, // input 53 (0x35 char '5') => 57 (0x39) + 58, // input 54 (0x36 char '6') => 58 (0x3A) + 59, // input 55 (0x37 char '7') => 59 (0x3B) + 60, // input 56 (0x38 char '8') => 60 (0x3C) + 61, // input 57 (0x39 char '9') => 61 (0x3D) + INVALID_VALUE, // input 58 (0x3A) + INVALID_VALUE, // input 59 (0x3B) + INVALID_VALUE, // input 60 (0x3C) + INVALID_VALUE, // input 61 (0x3D) + INVALID_VALUE, // input 62 (0x3E) + INVALID_VALUE, // input 63 (0x3F) + INVALID_VALUE, // input 64 (0x40) + 0, // input 65 (0x41 char 'A') => 0 (0x0) + 1, // input 66 (0x42 char 'B') => 1 (0x1) + 2, // input 67 (0x43 char 'C') => 2 (0x2) + 3, // input 68 (0x44 char 'D') => 3 (0x3) + 4, // input 69 (0x45 char 'E') => 4 (0x4) + 5, // input 70 (0x46 char 'F') => 5 (0x5) + 6, // input 71 (0x47 char 'G') => 6 (0x6) + 7, // input 72 (0x48 char 'H') => 7 (0x7) + 8, // input 73 (0x49 char 'I') => 8 (0x8) + 9, // input 74 (0x4A char 'J') => 9 (0x9) + 10, // input 75 (0x4B char 'K') => 10 (0xA) + 11, // input 76 (0x4C char 'L') => 11 (0xB) + 12, // input 77 (0x4D char 'M') => 12 (0xC) + 13, // input 78 (0x4E char 'N') => 13 (0xD) + 14, // input 79 (0x4F char 'O') => 14 (0xE) + 15, // input 80 (0x50 char 'P') => 15 (0xF) + 16, // input 81 (0x51 char 'Q') => 16 (0x10) + 17, // input 82 (0x52 char 'R') => 17 (0x11) + 18, // input 83 (0x53 char 'S') => 18 (0x12) + 19, // input 84 (0x54 char 'T') => 19 (0x13) + 20, // input 85 (0x55 char 'U') => 20 (0x14) + 21, // input 86 (0x56 char 'V') => 21 (0x15) + 22, // input 87 (0x57 char 'W') => 22 (0x16) + 23, // input 88 (0x58 char 'X') => 23 (0x17) + 24, // input 89 (0x59 char 'Y') => 24 (0x18) + 25, // input 90 (0x5A char 'Z') => 25 (0x19) + INVALID_VALUE, // input 91 (0x5B) + INVALID_VALUE, // input 92 (0x5C) + INVALID_VALUE, // input 93 (0x5D) + INVALID_VALUE, // input 94 (0x5E) + INVALID_VALUE, // input 95 (0x5F) + INVALID_VALUE, // input 96 (0x60) + 26, // input 97 (0x61 char 'a') => 26 (0x1A) + 27, // input 98 (0x62 char 'b') => 27 (0x1B) + 28, // input 99 (0x63 char 'c') => 28 (0x1C) + 29, // input 100 (0x64 char 'd') => 29 (0x1D) + 30, // input 101 (0x65 char 'e') => 30 (0x1E) + 31, // input 102 (0x66 char 'f') => 31 (0x1F) + 32, // input 103 (0x67 char 'g') => 32 (0x20) + 33, // input 104 (0x68 char 'h') => 33 (0x21) + 34, // input 105 (0x69 char 'i') => 34 (0x22) + 35, // input 106 (0x6A char 'j') => 35 (0x23) + 36, // input 107 (0x6B char 'k') => 36 (0x24) + 37, // input 108 (0x6C char 'l') => 37 (0x25) + 38, // input 109 (0x6D char 'm') => 38 (0x26) + 39, // input 110 (0x6E char 'n') => 39 (0x27) + 40, // input 111 (0x6F char 'o') => 40 (0x28) + 41, // input 112 (0x70 char 'p') => 41 (0x29) + 42, // input 113 (0x71 char 'q') => 42 (0x2A) + 43, // input 114 (0x72 char 'r') => 43 (0x2B) + 44, // input 115 (0x73 char 's') => 44 (0x2C) + 45, // input 116 (0x74 char 't') => 45 (0x2D) + 46, // input 117 (0x75 char 'u') => 46 (0x2E) + 47, // input 118 (0x76 char 'v') => 47 (0x2F) + 48, // input 119 (0x77 char 'w') => 48 (0x30) + 49, // input 120 (0x78 char 'x') => 49 (0x31) + 50, // input 121 (0x79 char 'y') => 50 (0x32) + 51, // input 122 (0x7A char 'z') => 51 (0x33) + INVALID_VALUE, // input 123 (0x7B) + INVALID_VALUE, // input 124 (0x7C) + INVALID_VALUE, // input 125 (0x7D) + INVALID_VALUE, // input 126 (0x7E) + INVALID_VALUE, // input 127 (0x7F) + INVALID_VALUE, // input 128 (0x80) + INVALID_VALUE, // input 129 (0x81) + INVALID_VALUE, // input 130 (0x82) + INVALID_VALUE, // input 131 (0x83) + INVALID_VALUE, // input 132 (0x84) + INVALID_VALUE, // input 133 (0x85) + INVALID_VALUE, // input 134 (0x86) + INVALID_VALUE, // input 135 (0x87) + INVALID_VALUE, // input 136 (0x88) + INVALID_VALUE, // input 137 (0x89) + INVALID_VALUE, // input 138 (0x8A) + INVALID_VALUE, // input 139 (0x8B) + INVALID_VALUE, // input 140 (0x8C) + INVALID_VALUE, // input 141 (0x8D) + INVALID_VALUE, // input 142 (0x8E) + INVALID_VALUE, // input 143 (0x8F) + INVALID_VALUE, // input 144 (0x90) + INVALID_VALUE, // input 145 (0x91) + INVALID_VALUE, // input 146 (0x92) + INVALID_VALUE, // input 147 (0x93) + INVALID_VALUE, // input 148 (0x94) + INVALID_VALUE, // input 149 (0x95) + INVALID_VALUE, // input 150 (0x96) + INVALID_VALUE, // input 151 (0x97) + INVALID_VALUE, // input 152 (0x98) + INVALID_VALUE, // input 153 (0x99) + INVALID_VALUE, // input 154 (0x9A) + INVALID_VALUE, // input 155 (0x9B) + INVALID_VALUE, // input 156 (0x9C) + INVALID_VALUE, // input 157 (0x9D) + INVALID_VALUE, // input 158 (0x9E) + INVALID_VALUE, // input 159 (0x9F) + INVALID_VALUE, // input 160 (0xA0) + INVALID_VALUE, // input 161 (0xA1) + INVALID_VALUE, // input 162 (0xA2) + INVALID_VALUE, // input 163 (0xA3) + INVALID_VALUE, // input 164 (0xA4) + INVALID_VALUE, // input 165 (0xA5) + INVALID_VALUE, // input 166 (0xA6) + INVALID_VALUE, // input 167 (0xA7) + INVALID_VALUE, // input 168 (0xA8) + INVALID_VALUE, // input 169 (0xA9) + INVALID_VALUE, // input 170 (0xAA) + INVALID_VALUE, // input 171 (0xAB) + INVALID_VALUE, // input 172 (0xAC) + INVALID_VALUE, // input 173 (0xAD) + INVALID_VALUE, // input 174 (0xAE) + INVALID_VALUE, // input 175 (0xAF) + INVALID_VALUE, // input 176 (0xB0) + INVALID_VALUE, // input 177 (0xB1) + INVALID_VALUE, // input 178 (0xB2) + INVALID_VALUE, // input 179 (0xB3) + INVALID_VALUE, // input 180 (0xB4) + INVALID_VALUE, // input 181 (0xB5) + INVALID_VALUE, // input 182 (0xB6) + INVALID_VALUE, // input 183 (0xB7) + INVALID_VALUE, // input 184 (0xB8) + INVALID_VALUE, // input 185 (0xB9) + INVALID_VALUE, // input 186 (0xBA) + INVALID_VALUE, // input 187 (0xBB) + INVALID_VALUE, // input 188 (0xBC) + INVALID_VALUE, // input 189 (0xBD) + INVALID_VALUE, // input 190 (0xBE) + INVALID_VALUE, // input 191 (0xBF) + INVALID_VALUE, // input 192 (0xC0) + INVALID_VALUE, // input 193 (0xC1) + INVALID_VALUE, // input 194 (0xC2) + INVALID_VALUE, // input 195 (0xC3) + INVALID_VALUE, // input 196 (0xC4) + INVALID_VALUE, // input 197 (0xC5) + INVALID_VALUE, // input 198 (0xC6) + INVALID_VALUE, // input 199 (0xC7) + INVALID_VALUE, // input 200 (0xC8) + INVALID_VALUE, // input 201 (0xC9) + INVALID_VALUE, // input 202 (0xCA) + INVALID_VALUE, // input 203 (0xCB) + INVALID_VALUE, // input 204 (0xCC) + INVALID_VALUE, // input 205 (0xCD) + INVALID_VALUE, // input 206 (0xCE) + INVALID_VALUE, // input 207 (0xCF) + INVALID_VALUE, // input 208 (0xD0) + INVALID_VALUE, // input 209 (0xD1) + INVALID_VALUE, // input 210 (0xD2) + INVALID_VALUE, // input 211 (0xD3) + INVALID_VALUE, // input 212 (0xD4) + INVALID_VALUE, // input 213 (0xD5) + INVALID_VALUE, // input 214 (0xD6) + INVALID_VALUE, // input 215 (0xD7) + INVALID_VALUE, // input 216 (0xD8) + INVALID_VALUE, // input 217 (0xD9) + INVALID_VALUE, // input 218 (0xDA) + INVALID_VALUE, // input 219 (0xDB) + INVALID_VALUE, // input 220 (0xDC) + INVALID_VALUE, // input 221 (0xDD) + INVALID_VALUE, // input 222 (0xDE) + INVALID_VALUE, // input 223 (0xDF) + INVALID_VALUE, // input 224 (0xE0) + INVALID_VALUE, // input 225 (0xE1) + INVALID_VALUE, // input 226 (0xE2) + INVALID_VALUE, // input 227 (0xE3) + INVALID_VALUE, // input 228 (0xE4) + INVALID_VALUE, // input 229 (0xE5) + INVALID_VALUE, // input 230 (0xE6) + INVALID_VALUE, // input 231 (0xE7) + INVALID_VALUE, // input 232 (0xE8) + INVALID_VALUE, // input 233 (0xE9) + INVALID_VALUE, // input 234 (0xEA) + INVALID_VALUE, // input 235 (0xEB) + INVALID_VALUE, // input 236 (0xEC) + INVALID_VALUE, // input 237 (0xED) + INVALID_VALUE, // input 238 (0xEE) + INVALID_VALUE, // input 239 (0xEF) + INVALID_VALUE, // input 240 (0xF0) + INVALID_VALUE, // input 241 (0xF1) + INVALID_VALUE, // input 242 (0xF2) + INVALID_VALUE, // input 243 (0xF3) + INVALID_VALUE, // input 244 (0xF4) + INVALID_VALUE, // input 245 (0xF5) + INVALID_VALUE, // input 246 (0xF6) + INVALID_VALUE, // input 247 (0xF7) + INVALID_VALUE, // input 248 (0xF8) + INVALID_VALUE, // input 249 (0xF9) + INVALID_VALUE, // input 250 (0xFA) + INVALID_VALUE, // input 251 (0xFB) + INVALID_VALUE, // input 252 (0xFC) + INVALID_VALUE, // input 253 (0xFD) + INVALID_VALUE, // input 254 (0xFE) + INVALID_VALUE, // input 255 (0xFF) +]; +#[rustfmt::skip] +pub const URL_SAFE_ENCODE: &[u8; 64] = &[ + 65, // input 0 (0x0) => 'A' (0x41) + 66, // input 1 (0x1) => 'B' (0x42) + 67, // input 2 (0x2) => 'C' (0x43) + 68, // input 3 (0x3) => 'D' (0x44) + 69, // input 4 (0x4) => 'E' (0x45) + 70, // input 5 (0x5) => 'F' (0x46) + 71, // input 6 (0x6) => 'G' (0x47) + 72, // input 7 (0x7) => 'H' (0x48) + 73, // input 8 (0x8) => 'I' (0x49) + 74, // input 9 (0x9) => 'J' (0x4A) + 75, // input 10 (0xA) => 'K' (0x4B) + 76, // input 11 (0xB) => 'L' (0x4C) + 77, // input 12 (0xC) => 'M' (0x4D) + 78, // input 13 (0xD) => 'N' (0x4E) + 79, // input 14 (0xE) => 'O' (0x4F) + 80, // input 15 (0xF) => 'P' (0x50) + 81, // input 16 (0x10) => 'Q' (0x51) + 82, // input 17 (0x11) => 'R' (0x52) + 83, // input 18 (0x12) => 'S' (0x53) + 84, // input 19 (0x13) => 'T' (0x54) + 85, // input 20 (0x14) => 'U' (0x55) + 86, // input 21 (0x15) => 'V' (0x56) + 87, // input 22 (0x16) => 'W' (0x57) + 88, // input 23 (0x17) => 'X' (0x58) + 89, // input 24 (0x18) => 'Y' (0x59) + 90, // input 25 (0x19) => 'Z' (0x5A) + 97, // input 26 (0x1A) => 'a' (0x61) + 98, // input 27 (0x1B) => 'b' (0x62) + 99, // input 28 (0x1C) => 'c' (0x63) + 100, // input 29 (0x1D) => 'd' (0x64) + 101, // input 30 (0x1E) => 'e' (0x65) + 102, // input 31 (0x1F) => 'f' (0x66) + 103, // input 32 (0x20) => 'g' (0x67) + 104, // input 33 (0x21) => 'h' (0x68) + 105, // input 34 (0x22) => 'i' (0x69) + 106, // input 35 (0x23) => 'j' (0x6A) + 107, // input 36 (0x24) => 'k' (0x6B) + 108, // input 37 (0x25) => 'l' (0x6C) + 109, // input 38 (0x26) => 'm' (0x6D) + 110, // input 39 (0x27) => 'n' (0x6E) + 111, // input 40 (0x28) => 'o' (0x6F) + 112, // input 41 (0x29) => 'p' (0x70) + 113, // input 42 (0x2A) => 'q' (0x71) + 114, // input 43 (0x2B) => 'r' (0x72) + 115, // input 44 (0x2C) => 's' (0x73) + 116, // input 45 (0x2D) => 't' (0x74) + 117, // input 46 (0x2E) => 'u' (0x75) + 118, // input 47 (0x2F) => 'v' (0x76) + 119, // input 48 (0x30) => 'w' (0x77) + 120, // input 49 (0x31) => 'x' (0x78) + 121, // input 50 (0x32) => 'y' (0x79) + 122, // input 51 (0x33) => 'z' (0x7A) + 48, // input 52 (0x34) => '0' (0x30) + 49, // input 53 (0x35) => '1' (0x31) + 50, // input 54 (0x36) => '2' (0x32) + 51, // input 55 (0x37) => '3' (0x33) + 52, // input 56 (0x38) => '4' (0x34) + 53, // input 57 (0x39) => '5' (0x35) + 54, // input 58 (0x3A) => '6' (0x36) + 55, // input 59 (0x3B) => '7' (0x37) + 56, // input 60 (0x3C) => '8' (0x38) + 57, // input 61 (0x3D) => '9' (0x39) + 45, // input 62 (0x3E) => '-' (0x2D) + 95, // input 63 (0x3F) => '_' (0x5F) +]; +#[rustfmt::skip] +pub const URL_SAFE_DECODE: &[u8; 256] = &[ + INVALID_VALUE, // input 0 (0x0) + INVALID_VALUE, // input 1 (0x1) + INVALID_VALUE, // input 2 (0x2) + INVALID_VALUE, // input 3 (0x3) + INVALID_VALUE, // input 4 (0x4) + INVALID_VALUE, // input 5 (0x5) + INVALID_VALUE, // input 6 (0x6) + INVALID_VALUE, // input 7 (0x7) + INVALID_VALUE, // input 8 (0x8) + INVALID_VALUE, // input 9 (0x9) + INVALID_VALUE, // input 10 (0xA) + INVALID_VALUE, // input 11 (0xB) + INVALID_VALUE, // input 12 (0xC) + INVALID_VALUE, // input 13 (0xD) + INVALID_VALUE, // input 14 (0xE) + INVALID_VALUE, // input 15 (0xF) + INVALID_VALUE, // input 16 (0x10) + INVALID_VALUE, // input 17 (0x11) + INVALID_VALUE, // input 18 (0x12) + INVALID_VALUE, // input 19 (0x13) + INVALID_VALUE, // input 20 (0x14) + INVALID_VALUE, // input 21 (0x15) + INVALID_VALUE, // input 22 (0x16) + INVALID_VALUE, // input 23 (0x17) + INVALID_VALUE, // input 24 (0x18) + INVALID_VALUE, // input 25 (0x19) + INVALID_VALUE, // input 26 (0x1A) + INVALID_VALUE, // input 27 (0x1B) + INVALID_VALUE, // input 28 (0x1C) + INVALID_VALUE, // input 29 (0x1D) + INVALID_VALUE, // input 30 (0x1E) + INVALID_VALUE, // input 31 (0x1F) + INVALID_VALUE, // input 32 (0x20) + INVALID_VALUE, // input 33 (0x21) + INVALID_VALUE, // input 34 (0x22) + INVALID_VALUE, // input 35 (0x23) + INVALID_VALUE, // input 36 (0x24) + INVALID_VALUE, // input 37 (0x25) + INVALID_VALUE, // input 38 (0x26) + INVALID_VALUE, // input 39 (0x27) + INVALID_VALUE, // input 40 (0x28) + INVALID_VALUE, // input 41 (0x29) + INVALID_VALUE, // input 42 (0x2A) + INVALID_VALUE, // input 43 (0x2B) + INVALID_VALUE, // input 44 (0x2C) + 62, // input 45 (0x2D char '-') => 62 (0x3E) + INVALID_VALUE, // input 46 (0x2E) + INVALID_VALUE, // input 47 (0x2F) + 52, // input 48 (0x30 char '0') => 52 (0x34) + 53, // input 49 (0x31 char '1') => 53 (0x35) + 54, // input 50 (0x32 char '2') => 54 (0x36) + 55, // input 51 (0x33 char '3') => 55 (0x37) + 56, // input 52 (0x34 char '4') => 56 (0x38) + 57, // input 53 (0x35 char '5') => 57 (0x39) + 58, // input 54 (0x36 char '6') => 58 (0x3A) + 59, // input 55 (0x37 char '7') => 59 (0x3B) + 60, // input 56 (0x38 char '8') => 60 (0x3C) + 61, // input 57 (0x39 char '9') => 61 (0x3D) + INVALID_VALUE, // input 58 (0x3A) + INVALID_VALUE, // input 59 (0x3B) + INVALID_VALUE, // input 60 (0x3C) + INVALID_VALUE, // input 61 (0x3D) + INVALID_VALUE, // input 62 (0x3E) + INVALID_VALUE, // input 63 (0x3F) + INVALID_VALUE, // input 64 (0x40) + 0, // input 65 (0x41 char 'A') => 0 (0x0) + 1, // input 66 (0x42 char 'B') => 1 (0x1) + 2, // input 67 (0x43 char 'C') => 2 (0x2) + 3, // input 68 (0x44 char 'D') => 3 (0x3) + 4, // input 69 (0x45 char 'E') => 4 (0x4) + 5, // input 70 (0x46 char 'F') => 5 (0x5) + 6, // input 71 (0x47 char 'G') => 6 (0x6) + 7, // input 72 (0x48 char 'H') => 7 (0x7) + 8, // input 73 (0x49 char 'I') => 8 (0x8) + 9, // input 74 (0x4A char 'J') => 9 (0x9) + 10, // input 75 (0x4B char 'K') => 10 (0xA) + 11, // input 76 (0x4C char 'L') => 11 (0xB) + 12, // input 77 (0x4D char 'M') => 12 (0xC) + 13, // input 78 (0x4E char 'N') => 13 (0xD) + 14, // input 79 (0x4F char 'O') => 14 (0xE) + 15, // input 80 (0x50 char 'P') => 15 (0xF) + 16, // input 81 (0x51 char 'Q') => 16 (0x10) + 17, // input 82 (0x52 char 'R') => 17 (0x11) + 18, // input 83 (0x53 char 'S') => 18 (0x12) + 19, // input 84 (0x54 char 'T') => 19 (0x13) + 20, // input 85 (0x55 char 'U') => 20 (0x14) + 21, // input 86 (0x56 char 'V') => 21 (0x15) + 22, // input 87 (0x57 char 'W') => 22 (0x16) + 23, // input 88 (0x58 char 'X') => 23 (0x17) + 24, // input 89 (0x59 char 'Y') => 24 (0x18) + 25, // input 90 (0x5A char 'Z') => 25 (0x19) + INVALID_VALUE, // input 91 (0x5B) + INVALID_VALUE, // input 92 (0x5C) + INVALID_VALUE, // input 93 (0x5D) + INVALID_VALUE, // input 94 (0x5E) + 63, // input 95 (0x5F char '_') => 63 (0x3F) + INVALID_VALUE, // input 96 (0x60) + 26, // input 97 (0x61 char 'a') => 26 (0x1A) + 27, // input 98 (0x62 char 'b') => 27 (0x1B) + 28, // input 99 (0x63 char 'c') => 28 (0x1C) + 29, // input 100 (0x64 char 'd') => 29 (0x1D) + 30, // input 101 (0x65 char 'e') => 30 (0x1E) + 31, // input 102 (0x66 char 'f') => 31 (0x1F) + 32, // input 103 (0x67 char 'g') => 32 (0x20) + 33, // input 104 (0x68 char 'h') => 33 (0x21) + 34, // input 105 (0x69 char 'i') => 34 (0x22) + 35, // input 106 (0x6A char 'j') => 35 (0x23) + 36, // input 107 (0x6B char 'k') => 36 (0x24) + 37, // input 108 (0x6C char 'l') => 37 (0x25) + 38, // input 109 (0x6D char 'm') => 38 (0x26) + 39, // input 110 (0x6E char 'n') => 39 (0x27) + 40, // input 111 (0x6F char 'o') => 40 (0x28) + 41, // input 112 (0x70 char 'p') => 41 (0x29) + 42, // input 113 (0x71 char 'q') => 42 (0x2A) + 43, // input 114 (0x72 char 'r') => 43 (0x2B) + 44, // input 115 (0x73 char 's') => 44 (0x2C) + 45, // input 116 (0x74 char 't') => 45 (0x2D) + 46, // input 117 (0x75 char 'u') => 46 (0x2E) + 47, // input 118 (0x76 char 'v') => 47 (0x2F) + 48, // input 119 (0x77 char 'w') => 48 (0x30) + 49, // input 120 (0x78 char 'x') => 49 (0x31) + 50, // input 121 (0x79 char 'y') => 50 (0x32) + 51, // input 122 (0x7A char 'z') => 51 (0x33) + INVALID_VALUE, // input 123 (0x7B) + INVALID_VALUE, // input 124 (0x7C) + INVALID_VALUE, // input 125 (0x7D) + INVALID_VALUE, // input 126 (0x7E) + INVALID_VALUE, // input 127 (0x7F) + INVALID_VALUE, // input 128 (0x80) + INVALID_VALUE, // input 129 (0x81) + INVALID_VALUE, // input 130 (0x82) + INVALID_VALUE, // input 131 (0x83) + INVALID_VALUE, // input 132 (0x84) + INVALID_VALUE, // input 133 (0x85) + INVALID_VALUE, // input 134 (0x86) + INVALID_VALUE, // input 135 (0x87) + INVALID_VALUE, // input 136 (0x88) + INVALID_VALUE, // input 137 (0x89) + INVALID_VALUE, // input 138 (0x8A) + INVALID_VALUE, // input 139 (0x8B) + INVALID_VALUE, // input 140 (0x8C) + INVALID_VALUE, // input 141 (0x8D) + INVALID_VALUE, // input 142 (0x8E) + INVALID_VALUE, // input 143 (0x8F) + INVALID_VALUE, // input 144 (0x90) + INVALID_VALUE, // input 145 (0x91) + INVALID_VALUE, // input 146 (0x92) + INVALID_VALUE, // input 147 (0x93) + INVALID_VALUE, // input 148 (0x94) + INVALID_VALUE, // input 149 (0x95) + INVALID_VALUE, // input 150 (0x96) + INVALID_VALUE, // input 151 (0x97) + INVALID_VALUE, // input 152 (0x98) + INVALID_VALUE, // input 153 (0x99) + INVALID_VALUE, // input 154 (0x9A) + INVALID_VALUE, // input 155 (0x9B) + INVALID_VALUE, // input 156 (0x9C) + INVALID_VALUE, // input 157 (0x9D) + INVALID_VALUE, // input 158 (0x9E) + INVALID_VALUE, // input 159 (0x9F) + INVALID_VALUE, // input 160 (0xA0) + INVALID_VALUE, // input 161 (0xA1) + INVALID_VALUE, // input 162 (0xA2) + INVALID_VALUE, // input 163 (0xA3) + INVALID_VALUE, // input 164 (0xA4) + INVALID_VALUE, // input 165 (0xA5) + INVALID_VALUE, // input 166 (0xA6) + INVALID_VALUE, // input 167 (0xA7) + INVALID_VALUE, // input 168 (0xA8) + INVALID_VALUE, // input 169 (0xA9) + INVALID_VALUE, // input 170 (0xAA) + INVALID_VALUE, // input 171 (0xAB) + INVALID_VALUE, // input 172 (0xAC) + INVALID_VALUE, // input 173 (0xAD) + INVALID_VALUE, // input 174 (0xAE) + INVALID_VALUE, // input 175 (0xAF) + INVALID_VALUE, // input 176 (0xB0) + INVALID_VALUE, // input 177 (0xB1) + INVALID_VALUE, // input 178 (0xB2) + INVALID_VALUE, // input 179 (0xB3) + INVALID_VALUE, // input 180 (0xB4) + INVALID_VALUE, // input 181 (0xB5) + INVALID_VALUE, // input 182 (0xB6) + INVALID_VALUE, // input 183 (0xB7) + INVALID_VALUE, // input 184 (0xB8) + INVALID_VALUE, // input 185 (0xB9) + INVALID_VALUE, // input 186 (0xBA) + INVALID_VALUE, // input 187 (0xBB) + INVALID_VALUE, // input 188 (0xBC) + INVALID_VALUE, // input 189 (0xBD) + INVALID_VALUE, // input 190 (0xBE) + INVALID_VALUE, // input 191 (0xBF) + INVALID_VALUE, // input 192 (0xC0) + INVALID_VALUE, // input 193 (0xC1) + INVALID_VALUE, // input 194 (0xC2) + INVALID_VALUE, // input 195 (0xC3) + INVALID_VALUE, // input 196 (0xC4) + INVALID_VALUE, // input 197 (0xC5) + INVALID_VALUE, // input 198 (0xC6) + INVALID_VALUE, // input 199 (0xC7) + INVALID_VALUE, // input 200 (0xC8) + INVALID_VALUE, // input 201 (0xC9) + INVALID_VALUE, // input 202 (0xCA) + INVALID_VALUE, // input 203 (0xCB) + INVALID_VALUE, // input 204 (0xCC) + INVALID_VALUE, // input 205 (0xCD) + INVALID_VALUE, // input 206 (0xCE) + INVALID_VALUE, // input 207 (0xCF) + INVALID_VALUE, // input 208 (0xD0) + INVALID_VALUE, // input 209 (0xD1) + INVALID_VALUE, // input 210 (0xD2) + INVALID_VALUE, // input 211 (0xD3) + INVALID_VALUE, // input 212 (0xD4) + INVALID_VALUE, // input 213 (0xD5) + INVALID_VALUE, // input 214 (0xD6) + INVALID_VALUE, // input 215 (0xD7) + INVALID_VALUE, // input 216 (0xD8) + INVALID_VALUE, // input 217 (0xD9) + INVALID_VALUE, // input 218 (0xDA) + INVALID_VALUE, // input 219 (0xDB) + INVALID_VALUE, // input 220 (0xDC) + INVALID_VALUE, // input 221 (0xDD) + INVALID_VALUE, // input 222 (0xDE) + INVALID_VALUE, // input 223 (0xDF) + INVALID_VALUE, // input 224 (0xE0) + INVALID_VALUE, // input 225 (0xE1) + INVALID_VALUE, // input 226 (0xE2) + INVALID_VALUE, // input 227 (0xE3) + INVALID_VALUE, // input 228 (0xE4) + INVALID_VALUE, // input 229 (0xE5) + INVALID_VALUE, // input 230 (0xE6) + INVALID_VALUE, // input 231 (0xE7) + INVALID_VALUE, // input 232 (0xE8) + INVALID_VALUE, // input 233 (0xE9) + INVALID_VALUE, // input 234 (0xEA) + INVALID_VALUE, // input 235 (0xEB) + INVALID_VALUE, // input 236 (0xEC) + INVALID_VALUE, // input 237 (0xED) + INVALID_VALUE, // input 238 (0xEE) + INVALID_VALUE, // input 239 (0xEF) + INVALID_VALUE, // input 240 (0xF0) + INVALID_VALUE, // input 241 (0xF1) + INVALID_VALUE, // input 242 (0xF2) + INVALID_VALUE, // input 243 (0xF3) + INVALID_VALUE, // input 244 (0xF4) + INVALID_VALUE, // input 245 (0xF5) + INVALID_VALUE, // input 246 (0xF6) + INVALID_VALUE, // input 247 (0xF7) + INVALID_VALUE, // input 248 (0xF8) + INVALID_VALUE, // input 249 (0xF9) + INVALID_VALUE, // input 250 (0xFA) + INVALID_VALUE, // input 251 (0xFB) + INVALID_VALUE, // input 252 (0xFC) + INVALID_VALUE, // input 253 (0xFD) + INVALID_VALUE, // input 254 (0xFE) + INVALID_VALUE, // input 255 (0xFF) +]; +#[rustfmt::skip] +pub const CRYPT_ENCODE: &[u8; 64] = &[ + 46, // input 0 (0x0) => '.' (0x2E) + 47, // input 1 (0x1) => '/' (0x2F) + 48, // input 2 (0x2) => '0' (0x30) + 49, // input 3 (0x3) => '1' (0x31) + 50, // input 4 (0x4) => '2' (0x32) + 51, // input 5 (0x5) => '3' (0x33) + 52, // input 6 (0x6) => '4' (0x34) + 53, // input 7 (0x7) => '5' (0x35) + 54, // input 8 (0x8) => '6' (0x36) + 55, // input 9 (0x9) => '7' (0x37) + 56, // input 10 (0xA) => '8' (0x38) + 57, // input 11 (0xB) => '9' (0x39) + 65, // input 12 (0xC) => 'A' (0x41) + 66, // input 13 (0xD) => 'B' (0x42) + 67, // input 14 (0xE) => 'C' (0x43) + 68, // input 15 (0xF) => 'D' (0x44) + 69, // input 16 (0x10) => 'E' (0x45) + 70, // input 17 (0x11) => 'F' (0x46) + 71, // input 18 (0x12) => 'G' (0x47) + 72, // input 19 (0x13) => 'H' (0x48) + 73, // input 20 (0x14) => 'I' (0x49) + 74, // input 21 (0x15) => 'J' (0x4A) + 75, // input 22 (0x16) => 'K' (0x4B) + 76, // input 23 (0x17) => 'L' (0x4C) + 77, // input 24 (0x18) => 'M' (0x4D) + 78, // input 25 (0x19) => 'N' (0x4E) + 79, // input 26 (0x1A) => 'O' (0x4F) + 80, // input 27 (0x1B) => 'P' (0x50) + 81, // input 28 (0x1C) => 'Q' (0x51) + 82, // input 29 (0x1D) => 'R' (0x52) + 83, // input 30 (0x1E) => 'S' (0x53) + 84, // input 31 (0x1F) => 'T' (0x54) + 85, // input 32 (0x20) => 'U' (0x55) + 86, // input 33 (0x21) => 'V' (0x56) + 87, // input 34 (0x22) => 'W' (0x57) + 88, // input 35 (0x23) => 'X' (0x58) + 89, // input 36 (0x24) => 'Y' (0x59) + 90, // input 37 (0x25) => 'Z' (0x5A) + 97, // input 38 (0x26) => 'a' (0x61) + 98, // input 39 (0x27) => 'b' (0x62) + 99, // input 40 (0x28) => 'c' (0x63) + 100, // input 41 (0x29) => 'd' (0x64) + 101, // input 42 (0x2A) => 'e' (0x65) + 102, // input 43 (0x2B) => 'f' (0x66) + 103, // input 44 (0x2C) => 'g' (0x67) + 104, // input 45 (0x2D) => 'h' (0x68) + 105, // input 46 (0x2E) => 'i' (0x69) + 106, // input 47 (0x2F) => 'j' (0x6A) + 107, // input 48 (0x30) => 'k' (0x6B) + 108, // input 49 (0x31) => 'l' (0x6C) + 109, // input 50 (0x32) => 'm' (0x6D) + 110, // input 51 (0x33) => 'n' (0x6E) + 111, // input 52 (0x34) => 'o' (0x6F) + 112, // input 53 (0x35) => 'p' (0x70) + 113, // input 54 (0x36) => 'q' (0x71) + 114, // input 55 (0x37) => 'r' (0x72) + 115, // input 56 (0x38) => 's' (0x73) + 116, // input 57 (0x39) => 't' (0x74) + 117, // input 58 (0x3A) => 'u' (0x75) + 118, // input 59 (0x3B) => 'v' (0x76) + 119, // input 60 (0x3C) => 'w' (0x77) + 120, // input 61 (0x3D) => 'x' (0x78) + 121, // input 62 (0x3E) => 'y' (0x79) + 122, // input 63 (0x3F) => 'z' (0x7A) +]; +#[rustfmt::skip] +pub const CRYPT_DECODE: &[u8; 256] = &[ + INVALID_VALUE, // input 0 (0x0) + INVALID_VALUE, // input 1 (0x1) + INVALID_VALUE, // input 2 (0x2) + INVALID_VALUE, // input 3 (0x3) + INVALID_VALUE, // input 4 (0x4) + INVALID_VALUE, // input 5 (0x5) + INVALID_VALUE, // input 6 (0x6) + INVALID_VALUE, // input 7 (0x7) + INVALID_VALUE, // input 8 (0x8) + INVALID_VALUE, // input 9 (0x9) + INVALID_VALUE, // input 10 (0xA) + INVALID_VALUE, // input 11 (0xB) + INVALID_VALUE, // input 12 (0xC) + INVALID_VALUE, // input 13 (0xD) + INVALID_VALUE, // input 14 (0xE) + INVALID_VALUE, // input 15 (0xF) + INVALID_VALUE, // input 16 (0x10) + INVALID_VALUE, // input 17 (0x11) + INVALID_VALUE, // input 18 (0x12) + INVALID_VALUE, // input 19 (0x13) + INVALID_VALUE, // input 20 (0x14) + INVALID_VALUE, // input 21 (0x15) + INVALID_VALUE, // input 22 (0x16) + INVALID_VALUE, // input 23 (0x17) + INVALID_VALUE, // input 24 (0x18) + INVALID_VALUE, // input 25 (0x19) + INVALID_VALUE, // input 26 (0x1A) + INVALID_VALUE, // input 27 (0x1B) + INVALID_VALUE, // input 28 (0x1C) + INVALID_VALUE, // input 29 (0x1D) + INVALID_VALUE, // input 30 (0x1E) + INVALID_VALUE, // input 31 (0x1F) + INVALID_VALUE, // input 32 (0x20) + INVALID_VALUE, // input 33 (0x21) + INVALID_VALUE, // input 34 (0x22) + INVALID_VALUE, // input 35 (0x23) + INVALID_VALUE, // input 36 (0x24) + INVALID_VALUE, // input 37 (0x25) + INVALID_VALUE, // input 38 (0x26) + INVALID_VALUE, // input 39 (0x27) + INVALID_VALUE, // input 40 (0x28) + INVALID_VALUE, // input 41 (0x29) + INVALID_VALUE, // input 42 (0x2A) + INVALID_VALUE, // input 43 (0x2B) + INVALID_VALUE, // input 44 (0x2C) + INVALID_VALUE, // input 45 (0x2D) + 0, // input 46 (0x2E char '.') => 0 (0x0) + 1, // input 47 (0x2F char '/') => 1 (0x1) + 2, // input 48 (0x30 char '0') => 2 (0x2) + 3, // input 49 (0x31 char '1') => 3 (0x3) + 4, // input 50 (0x32 char '2') => 4 (0x4) + 5, // input 51 (0x33 char '3') => 5 (0x5) + 6, // input 52 (0x34 char '4') => 6 (0x6) + 7, // input 53 (0x35 char '5') => 7 (0x7) + 8, // input 54 (0x36 char '6') => 8 (0x8) + 9, // input 55 (0x37 char '7') => 9 (0x9) + 10, // input 56 (0x38 char '8') => 10 (0xA) + 11, // input 57 (0x39 char '9') => 11 (0xB) + INVALID_VALUE, // input 58 (0x3A) + INVALID_VALUE, // input 59 (0x3B) + INVALID_VALUE, // input 60 (0x3C) + INVALID_VALUE, // input 61 (0x3D) + INVALID_VALUE, // input 62 (0x3E) + INVALID_VALUE, // input 63 (0x3F) + INVALID_VALUE, // input 64 (0x40) + 12, // input 65 (0x41 char 'A') => 12 (0xC) + 13, // input 66 (0x42 char 'B') => 13 (0xD) + 14, // input 67 (0x43 char 'C') => 14 (0xE) + 15, // input 68 (0x44 char 'D') => 15 (0xF) + 16, // input 69 (0x45 char 'E') => 16 (0x10) + 17, // input 70 (0x46 char 'F') => 17 (0x11) + 18, // input 71 (0x47 char 'G') => 18 (0x12) + 19, // input 72 (0x48 char 'H') => 19 (0x13) + 20, // input 73 (0x49 char 'I') => 20 (0x14) + 21, // input 74 (0x4A char 'J') => 21 (0x15) + 22, // input 75 (0x4B char 'K') => 22 (0x16) + 23, // input 76 (0x4C char 'L') => 23 (0x17) + 24, // input 77 (0x4D char 'M') => 24 (0x18) + 25, // input 78 (0x4E char 'N') => 25 (0x19) + 26, // input 79 (0x4F char 'O') => 26 (0x1A) + 27, // input 80 (0x50 char 'P') => 27 (0x1B) + 28, // input 81 (0x51 char 'Q') => 28 (0x1C) + 29, // input 82 (0x52 char 'R') => 29 (0x1D) + 30, // input 83 (0x53 char 'S') => 30 (0x1E) + 31, // input 84 (0x54 char 'T') => 31 (0x1F) + 32, // input 85 (0x55 char 'U') => 32 (0x20) + 33, // input 86 (0x56 char 'V') => 33 (0x21) + 34, // input 87 (0x57 char 'W') => 34 (0x22) + 35, // input 88 (0x58 char 'X') => 35 (0x23) + 36, // input 89 (0x59 char 'Y') => 36 (0x24) + 37, // input 90 (0x5A char 'Z') => 37 (0x25) + INVALID_VALUE, // input 91 (0x5B) + INVALID_VALUE, // input 92 (0x5C) + INVALID_VALUE, // input 93 (0x5D) + INVALID_VALUE, // input 94 (0x5E) + INVALID_VALUE, // input 95 (0x5F) + INVALID_VALUE, // input 96 (0x60) + 38, // input 97 (0x61 char 'a') => 38 (0x26) + 39, // input 98 (0x62 char 'b') => 39 (0x27) + 40, // input 99 (0x63 char 'c') => 40 (0x28) + 41, // input 100 (0x64 char 'd') => 41 (0x29) + 42, // input 101 (0x65 char 'e') => 42 (0x2A) + 43, // input 102 (0x66 char 'f') => 43 (0x2B) + 44, // input 103 (0x67 char 'g') => 44 (0x2C) + 45, // input 104 (0x68 char 'h') => 45 (0x2D) + 46, // input 105 (0x69 char 'i') => 46 (0x2E) + 47, // input 106 (0x6A char 'j') => 47 (0x2F) + 48, // input 107 (0x6B char 'k') => 48 (0x30) + 49, // input 108 (0x6C char 'l') => 49 (0x31) + 50, // input 109 (0x6D char 'm') => 50 (0x32) + 51, // input 110 (0x6E char 'n') => 51 (0x33) + 52, // input 111 (0x6F char 'o') => 52 (0x34) + 53, // input 112 (0x70 char 'p') => 53 (0x35) + 54, // input 113 (0x71 char 'q') => 54 (0x36) + 55, // input 114 (0x72 char 'r') => 55 (0x37) + 56, // input 115 (0x73 char 's') => 56 (0x38) + 57, // input 116 (0x74 char 't') => 57 (0x39) + 58, // input 117 (0x75 char 'u') => 58 (0x3A) + 59, // input 118 (0x76 char 'v') => 59 (0x3B) + 60, // input 119 (0x77 char 'w') => 60 (0x3C) + 61, // input 120 (0x78 char 'x') => 61 (0x3D) + 62, // input 121 (0x79 char 'y') => 62 (0x3E) + 63, // input 122 (0x7A char 'z') => 63 (0x3F) + INVALID_VALUE, // input 123 (0x7B) + INVALID_VALUE, // input 124 (0x7C) + INVALID_VALUE, // input 125 (0x7D) + INVALID_VALUE, // input 126 (0x7E) + INVALID_VALUE, // input 127 (0x7F) + INVALID_VALUE, // input 128 (0x80) + INVALID_VALUE, // input 129 (0x81) + INVALID_VALUE, // input 130 (0x82) + INVALID_VALUE, // input 131 (0x83) + INVALID_VALUE, // input 132 (0x84) + INVALID_VALUE, // input 133 (0x85) + INVALID_VALUE, // input 134 (0x86) + INVALID_VALUE, // input 135 (0x87) + INVALID_VALUE, // input 136 (0x88) + INVALID_VALUE, // input 137 (0x89) + INVALID_VALUE, // input 138 (0x8A) + INVALID_VALUE, // input 139 (0x8B) + INVALID_VALUE, // input 140 (0x8C) + INVALID_VALUE, // input 141 (0x8D) + INVALID_VALUE, // input 142 (0x8E) + INVALID_VALUE, // input 143 (0x8F) + INVALID_VALUE, // input 144 (0x90) + INVALID_VALUE, // input 145 (0x91) + INVALID_VALUE, // input 146 (0x92) + INVALID_VALUE, // input 147 (0x93) + INVALID_VALUE, // input 148 (0x94) + INVALID_VALUE, // input 149 (0x95) + INVALID_VALUE, // input 150 (0x96) + INVALID_VALUE, // input 151 (0x97) + INVALID_VALUE, // input 152 (0x98) + INVALID_VALUE, // input 153 (0x99) + INVALID_VALUE, // input 154 (0x9A) + INVALID_VALUE, // input 155 (0x9B) + INVALID_VALUE, // input 156 (0x9C) + INVALID_VALUE, // input 157 (0x9D) + INVALID_VALUE, // input 158 (0x9E) + INVALID_VALUE, // input 159 (0x9F) + INVALID_VALUE, // input 160 (0xA0) + INVALID_VALUE, // input 161 (0xA1) + INVALID_VALUE, // input 162 (0xA2) + INVALID_VALUE, // input 163 (0xA3) + INVALID_VALUE, // input 164 (0xA4) + INVALID_VALUE, // input 165 (0xA5) + INVALID_VALUE, // input 166 (0xA6) + INVALID_VALUE, // input 167 (0xA7) + INVALID_VALUE, // input 168 (0xA8) + INVALID_VALUE, // input 169 (0xA9) + INVALID_VALUE, // input 170 (0xAA) + INVALID_VALUE, // input 171 (0xAB) + INVALID_VALUE, // input 172 (0xAC) + INVALID_VALUE, // input 173 (0xAD) + INVALID_VALUE, // input 174 (0xAE) + INVALID_VALUE, // input 175 (0xAF) + INVALID_VALUE, // input 176 (0xB0) + INVALID_VALUE, // input 177 (0xB1) + INVALID_VALUE, // input 178 (0xB2) + INVALID_VALUE, // input 179 (0xB3) + INVALID_VALUE, // input 180 (0xB4) + INVALID_VALUE, // input 181 (0xB5) + INVALID_VALUE, // input 182 (0xB6) + INVALID_VALUE, // input 183 (0xB7) + INVALID_VALUE, // input 184 (0xB8) + INVALID_VALUE, // input 185 (0xB9) + INVALID_VALUE, // input 186 (0xBA) + INVALID_VALUE, // input 187 (0xBB) + INVALID_VALUE, // input 188 (0xBC) + INVALID_VALUE, // input 189 (0xBD) + INVALID_VALUE, // input 190 (0xBE) + INVALID_VALUE, // input 191 (0xBF) + INVALID_VALUE, // input 192 (0xC0) + INVALID_VALUE, // input 193 (0xC1) + INVALID_VALUE, // input 194 (0xC2) + INVALID_VALUE, // input 195 (0xC3) + INVALID_VALUE, // input 196 (0xC4) + INVALID_VALUE, // input 197 (0xC5) + INVALID_VALUE, // input 198 (0xC6) + INVALID_VALUE, // input 199 (0xC7) + INVALID_VALUE, // input 200 (0xC8) + INVALID_VALUE, // input 201 (0xC9) + INVALID_VALUE, // input 202 (0xCA) + INVALID_VALUE, // input 203 (0xCB) + INVALID_VALUE, // input 204 (0xCC) + INVALID_VALUE, // input 205 (0xCD) + INVALID_VALUE, // input 206 (0xCE) + INVALID_VALUE, // input 207 (0xCF) + INVALID_VALUE, // input 208 (0xD0) + INVALID_VALUE, // input 209 (0xD1) + INVALID_VALUE, // input 210 (0xD2) + INVALID_VALUE, // input 211 (0xD3) + INVALID_VALUE, // input 212 (0xD4) + INVALID_VALUE, // input 213 (0xD5) + INVALID_VALUE, // input 214 (0xD6) + INVALID_VALUE, // input 215 (0xD7) + INVALID_VALUE, // input 216 (0xD8) + INVALID_VALUE, // input 217 (0xD9) + INVALID_VALUE, // input 218 (0xDA) + INVALID_VALUE, // input 219 (0xDB) + INVALID_VALUE, // input 220 (0xDC) + INVALID_VALUE, // input 221 (0xDD) + INVALID_VALUE, // input 222 (0xDE) + INVALID_VALUE, // input 223 (0xDF) + INVALID_VALUE, // input 224 (0xE0) + INVALID_VALUE, // input 225 (0xE1) + INVALID_VALUE, // input 226 (0xE2) + INVALID_VALUE, // input 227 (0xE3) + INVALID_VALUE, // input 228 (0xE4) + INVALID_VALUE, // input 229 (0xE5) + INVALID_VALUE, // input 230 (0xE6) + INVALID_VALUE, // input 231 (0xE7) + INVALID_VALUE, // input 232 (0xE8) + INVALID_VALUE, // input 233 (0xE9) + INVALID_VALUE, // input 234 (0xEA) + INVALID_VALUE, // input 235 (0xEB) + INVALID_VALUE, // input 236 (0xEC) + INVALID_VALUE, // input 237 (0xED) + INVALID_VALUE, // input 238 (0xEE) + INVALID_VALUE, // input 239 (0xEF) + INVALID_VALUE, // input 240 (0xF0) + INVALID_VALUE, // input 241 (0xF1) + INVALID_VALUE, // input 242 (0xF2) + INVALID_VALUE, // input 243 (0xF3) + INVALID_VALUE, // input 244 (0xF4) + INVALID_VALUE, // input 245 (0xF5) + INVALID_VALUE, // input 246 (0xF6) + INVALID_VALUE, // input 247 (0xF7) + INVALID_VALUE, // input 248 (0xF8) + INVALID_VALUE, // input 249 (0xF9) + INVALID_VALUE, // input 250 (0xFA) + INVALID_VALUE, // input 251 (0xFB) + INVALID_VALUE, // input 252 (0xFC) + INVALID_VALUE, // input 253 (0xFD) + INVALID_VALUE, // input 254 (0xFE) + INVALID_VALUE, // input 255 (0xFF) +]; +#[rustfmt::skip] +pub const IMAP_MUTF7_ENCODE: &[u8; 64] = &[ + 65, // input 0 (0x0) => 'A' (0x41) + 66, // input 1 (0x1) => 'B' (0x42) + 67, // input 2 (0x2) => 'C' (0x43) + 68, // input 3 (0x3) => 'D' (0x44) + 69, // input 4 (0x4) => 'E' (0x45) + 70, // input 5 (0x5) => 'F' (0x46) + 71, // input 6 (0x6) => 'G' (0x47) + 72, // input 7 (0x7) => 'H' (0x48) + 73, // input 8 (0x8) => 'I' (0x49) + 74, // input 9 (0x9) => 'J' (0x4A) + 75, // input 10 (0xA) => 'K' (0x4B) + 76, // input 11 (0xB) => 'L' (0x4C) + 77, // input 12 (0xC) => 'M' (0x4D) + 78, // input 13 (0xD) => 'N' (0x4E) + 79, // input 14 (0xE) => 'O' (0x4F) + 80, // input 15 (0xF) => 'P' (0x50) + 81, // input 16 (0x10) => 'Q' (0x51) + 82, // input 17 (0x11) => 'R' (0x52) + 83, // input 18 (0x12) => 'S' (0x53) + 84, // input 19 (0x13) => 'T' (0x54) + 85, // input 20 (0x14) => 'U' (0x55) + 86, // input 21 (0x15) => 'V' (0x56) + 87, // input 22 (0x16) => 'W' (0x57) + 88, // input 23 (0x17) => 'X' (0x58) + 89, // input 24 (0x18) => 'Y' (0x59) + 90, // input 25 (0x19) => 'Z' (0x5A) + 97, // input 26 (0x1A) => 'a' (0x61) + 98, // input 27 (0x1B) => 'b' (0x62) + 99, // input 28 (0x1C) => 'c' (0x63) + 100, // input 29 (0x1D) => 'd' (0x64) + 101, // input 30 (0x1E) => 'e' (0x65) + 102, // input 31 (0x1F) => 'f' (0x66) + 103, // input 32 (0x20) => 'g' (0x67) + 104, // input 33 (0x21) => 'h' (0x68) + 105, // input 34 (0x22) => 'i' (0x69) + 106, // input 35 (0x23) => 'j' (0x6A) + 107, // input 36 (0x24) => 'k' (0x6B) + 108, // input 37 (0x25) => 'l' (0x6C) + 109, // input 38 (0x26) => 'm' (0x6D) + 110, // input 39 (0x27) => 'n' (0x6E) + 111, // input 40 (0x28) => 'o' (0x6F) + 112, // input 41 (0x29) => 'p' (0x70) + 113, // input 42 (0x2A) => 'q' (0x71) + 114, // input 43 (0x2B) => 'r' (0x72) + 115, // input 44 (0x2C) => 's' (0x73) + 116, // input 45 (0x2D) => 't' (0x74) + 117, // input 46 (0x2E) => 'u' (0x75) + 118, // input 47 (0x2F) => 'v' (0x76) + 119, // input 48 (0x30) => 'w' (0x77) + 120, // input 49 (0x31) => 'x' (0x78) + 121, // input 50 (0x32) => 'y' (0x79) + 122, // input 51 (0x33) => 'z' (0x7A) + 48, // input 52 (0x34) => '0' (0x30) + 49, // input 53 (0x35) => '1' (0x31) + 50, // input 54 (0x36) => '2' (0x32) + 51, // input 55 (0x37) => '3' (0x33) + 52, // input 56 (0x38) => '4' (0x34) + 53, // input 57 (0x39) => '5' (0x35) + 54, // input 58 (0x3A) => '6' (0x36) + 55, // input 59 (0x3B) => '7' (0x37) + 56, // input 60 (0x3C) => '8' (0x38) + 57, // input 61 (0x3D) => '9' (0x39) + 43, // input 62 (0x3E) => '+' (0x2B) + 44, // input 63 (0x3F) => ',' (0x2C) +]; +#[rustfmt::skip] +pub const IMAP_MUTF7_DECODE: &[u8; 256] = &[ + INVALID_VALUE, // input 0 (0x0) + INVALID_VALUE, // input 1 (0x1) + INVALID_VALUE, // input 2 (0x2) + INVALID_VALUE, // input 3 (0x3) + INVALID_VALUE, // input 4 (0x4) + INVALID_VALUE, // input 5 (0x5) + INVALID_VALUE, // input 6 (0x6) + INVALID_VALUE, // input 7 (0x7) + INVALID_VALUE, // input 8 (0x8) + INVALID_VALUE, // input 9 (0x9) + INVALID_VALUE, // input 10 (0xA) + INVALID_VALUE, // input 11 (0xB) + INVALID_VALUE, // input 12 (0xC) + INVALID_VALUE, // input 13 (0xD) + INVALID_VALUE, // input 14 (0xE) + INVALID_VALUE, // input 15 (0xF) + INVALID_VALUE, // input 16 (0x10) + INVALID_VALUE, // input 17 (0x11) + INVALID_VALUE, // input 18 (0x12) + INVALID_VALUE, // input 19 (0x13) + INVALID_VALUE, // input 20 (0x14) + INVALID_VALUE, // input 21 (0x15) + INVALID_VALUE, // input 22 (0x16) + INVALID_VALUE, // input 23 (0x17) + INVALID_VALUE, // input 24 (0x18) + INVALID_VALUE, // input 25 (0x19) + INVALID_VALUE, // input 26 (0x1A) + INVALID_VALUE, // input 27 (0x1B) + INVALID_VALUE, // input 28 (0x1C) + INVALID_VALUE, // input 29 (0x1D) + INVALID_VALUE, // input 30 (0x1E) + INVALID_VALUE, // input 31 (0x1F) + INVALID_VALUE, // input 32 (0x20) + INVALID_VALUE, // input 33 (0x21) + INVALID_VALUE, // input 34 (0x22) + INVALID_VALUE, // input 35 (0x23) + INVALID_VALUE, // input 36 (0x24) + INVALID_VALUE, // input 37 (0x25) + INVALID_VALUE, // input 38 (0x26) + INVALID_VALUE, // input 39 (0x27) + INVALID_VALUE, // input 40 (0x28) + INVALID_VALUE, // input 41 (0x29) + INVALID_VALUE, // input 42 (0x2A) + 62, // input 43 (0x2B char '+') => 62 (0x3E) + 63, // input 44 (0x2C char ',') => 63 (0x3F) + INVALID_VALUE, // input 45 (0x2D) + INVALID_VALUE, // input 46 (0x2E) + INVALID_VALUE, // input 47 (0x2F) + 52, // input 48 (0x30 char '0') => 52 (0x34) + 53, // input 49 (0x31 char '1') => 53 (0x35) + 54, // input 50 (0x32 char '2') => 54 (0x36) + 55, // input 51 (0x33 char '3') => 55 (0x37) + 56, // input 52 (0x34 char '4') => 56 (0x38) + 57, // input 53 (0x35 char '5') => 57 (0x39) + 58, // input 54 (0x36 char '6') => 58 (0x3A) + 59, // input 55 (0x37 char '7') => 59 (0x3B) + 60, // input 56 (0x38 char '8') => 60 (0x3C) + 61, // input 57 (0x39 char '9') => 61 (0x3D) + INVALID_VALUE, // input 58 (0x3A) + INVALID_VALUE, // input 59 (0x3B) + INVALID_VALUE, // input 60 (0x3C) + INVALID_VALUE, // input 61 (0x3D) + INVALID_VALUE, // input 62 (0x3E) + INVALID_VALUE, // input 63 (0x3F) + INVALID_VALUE, // input 64 (0x40) + 0, // input 65 (0x41 char 'A') => 0 (0x0) + 1, // input 66 (0x42 char 'B') => 1 (0x1) + 2, // input 67 (0x43 char 'C') => 2 (0x2) + 3, // input 68 (0x44 char 'D') => 3 (0x3) + 4, // input 69 (0x45 char 'E') => 4 (0x4) + 5, // input 70 (0x46 char 'F') => 5 (0x5) + 6, // input 71 (0x47 char 'G') => 6 (0x6) + 7, // input 72 (0x48 char 'H') => 7 (0x7) + 8, // input 73 (0x49 char 'I') => 8 (0x8) + 9, // input 74 (0x4A char 'J') => 9 (0x9) + 10, // input 75 (0x4B char 'K') => 10 (0xA) + 11, // input 76 (0x4C char 'L') => 11 (0xB) + 12, // input 77 (0x4D char 'M') => 12 (0xC) + 13, // input 78 (0x4E char 'N') => 13 (0xD) + 14, // input 79 (0x4F char 'O') => 14 (0xE) + 15, // input 80 (0x50 char 'P') => 15 (0xF) + 16, // input 81 (0x51 char 'Q') => 16 (0x10) + 17, // input 82 (0x52 char 'R') => 17 (0x11) + 18, // input 83 (0x53 char 'S') => 18 (0x12) + 19, // input 84 (0x54 char 'T') => 19 (0x13) + 20, // input 85 (0x55 char 'U') => 20 (0x14) + 21, // input 86 (0x56 char 'V') => 21 (0x15) + 22, // input 87 (0x57 char 'W') => 22 (0x16) + 23, // input 88 (0x58 char 'X') => 23 (0x17) + 24, // input 89 (0x59 char 'Y') => 24 (0x18) + 25, // input 90 (0x5A char 'Z') => 25 (0x19) + INVALID_VALUE, // input 91 (0x5B) + INVALID_VALUE, // input 92 (0x5C) + INVALID_VALUE, // input 93 (0x5D) + INVALID_VALUE, // input 94 (0x5E) + INVALID_VALUE, // input 95 (0x5F) + INVALID_VALUE, // input 96 (0x60) + 26, // input 97 (0x61 char 'a') => 26 (0x1A) + 27, // input 98 (0x62 char 'b') => 27 (0x1B) + 28, // input 99 (0x63 char 'c') => 28 (0x1C) + 29, // input 100 (0x64 char 'd') => 29 (0x1D) + 30, // input 101 (0x65 char 'e') => 30 (0x1E) + 31, // input 102 (0x66 char 'f') => 31 (0x1F) + 32, // input 103 (0x67 char 'g') => 32 (0x20) + 33, // input 104 (0x68 char 'h') => 33 (0x21) + 34, // input 105 (0x69 char 'i') => 34 (0x22) + 35, // input 106 (0x6A char 'j') => 35 (0x23) + 36, // input 107 (0x6B char 'k') => 36 (0x24) + 37, // input 108 (0x6C char 'l') => 37 (0x25) + 38, // input 109 (0x6D char 'm') => 38 (0x26) + 39, // input 110 (0x6E char 'n') => 39 (0x27) + 40, // input 111 (0x6F char 'o') => 40 (0x28) + 41, // input 112 (0x70 char 'p') => 41 (0x29) + 42, // input 113 (0x71 char 'q') => 42 (0x2A) + 43, // input 114 (0x72 char 'r') => 43 (0x2B) + 44, // input 115 (0x73 char 's') => 44 (0x2C) + 45, // input 116 (0x74 char 't') => 45 (0x2D) + 46, // input 117 (0x75 char 'u') => 46 (0x2E) + 47, // input 118 (0x76 char 'v') => 47 (0x2F) + 48, // input 119 (0x77 char 'w') => 48 (0x30) + 49, // input 120 (0x78 char 'x') => 49 (0x31) + 50, // input 121 (0x79 char 'y') => 50 (0x32) + 51, // input 122 (0x7A char 'z') => 51 (0x33) + INVALID_VALUE, // input 123 (0x7B) + INVALID_VALUE, // input 124 (0x7C) + INVALID_VALUE, // input 125 (0x7D) + INVALID_VALUE, // input 126 (0x7E) + INVALID_VALUE, // input 127 (0x7F) + INVALID_VALUE, // input 128 (0x80) + INVALID_VALUE, // input 129 (0x81) + INVALID_VALUE, // input 130 (0x82) + INVALID_VALUE, // input 131 (0x83) + INVALID_VALUE, // input 132 (0x84) + INVALID_VALUE, // input 133 (0x85) + INVALID_VALUE, // input 134 (0x86) + INVALID_VALUE, // input 135 (0x87) + INVALID_VALUE, // input 136 (0x88) + INVALID_VALUE, // input 137 (0x89) + INVALID_VALUE, // input 138 (0x8A) + INVALID_VALUE, // input 139 (0x8B) + INVALID_VALUE, // input 140 (0x8C) + INVALID_VALUE, // input 141 (0x8D) + INVALID_VALUE, // input 142 (0x8E) + INVALID_VALUE, // input 143 (0x8F) + INVALID_VALUE, // input 144 (0x90) + INVALID_VALUE, // input 145 (0x91) + INVALID_VALUE, // input 146 (0x92) + INVALID_VALUE, // input 147 (0x93) + INVALID_VALUE, // input 148 (0x94) + INVALID_VALUE, // input 149 (0x95) + INVALID_VALUE, // input 150 (0x96) + INVALID_VALUE, // input 151 (0x97) + INVALID_VALUE, // input 152 (0x98) + INVALID_VALUE, // input 153 (0x99) + INVALID_VALUE, // input 154 (0x9A) + INVALID_VALUE, // input 155 (0x9B) + INVALID_VALUE, // input 156 (0x9C) + INVALID_VALUE, // input 157 (0x9D) + INVALID_VALUE, // input 158 (0x9E) + INVALID_VALUE, // input 159 (0x9F) + INVALID_VALUE, // input 160 (0xA0) + INVALID_VALUE, // input 161 (0xA1) + INVALID_VALUE, // input 162 (0xA2) + INVALID_VALUE, // input 163 (0xA3) + INVALID_VALUE, // input 164 (0xA4) + INVALID_VALUE, // input 165 (0xA5) + INVALID_VALUE, // input 166 (0xA6) + INVALID_VALUE, // input 167 (0xA7) + INVALID_VALUE, // input 168 (0xA8) + INVALID_VALUE, // input 169 (0xA9) + INVALID_VALUE, // input 170 (0xAA) + INVALID_VALUE, // input 171 (0xAB) + INVALID_VALUE, // input 172 (0xAC) + INVALID_VALUE, // input 173 (0xAD) + INVALID_VALUE, // input 174 (0xAE) + INVALID_VALUE, // input 175 (0xAF) + INVALID_VALUE, // input 176 (0xB0) + INVALID_VALUE, // input 177 (0xB1) + INVALID_VALUE, // input 178 (0xB2) + INVALID_VALUE, // input 179 (0xB3) + INVALID_VALUE, // input 180 (0xB4) + INVALID_VALUE, // input 181 (0xB5) + INVALID_VALUE, // input 182 (0xB6) + INVALID_VALUE, // input 183 (0xB7) + INVALID_VALUE, // input 184 (0xB8) + INVALID_VALUE, // input 185 (0xB9) + INVALID_VALUE, // input 186 (0xBA) + INVALID_VALUE, // input 187 (0xBB) + INVALID_VALUE, // input 188 (0xBC) + INVALID_VALUE, // input 189 (0xBD) + INVALID_VALUE, // input 190 (0xBE) + INVALID_VALUE, // input 191 (0xBF) + INVALID_VALUE, // input 192 (0xC0) + INVALID_VALUE, // input 193 (0xC1) + INVALID_VALUE, // input 194 (0xC2) + INVALID_VALUE, // input 195 (0xC3) + INVALID_VALUE, // input 196 (0xC4) + INVALID_VALUE, // input 197 (0xC5) + INVALID_VALUE, // input 198 (0xC6) + INVALID_VALUE, // input 199 (0xC7) + INVALID_VALUE, // input 200 (0xC8) + INVALID_VALUE, // input 201 (0xC9) + INVALID_VALUE, // input 202 (0xCA) + INVALID_VALUE, // input 203 (0xCB) + INVALID_VALUE, // input 204 (0xCC) + INVALID_VALUE, // input 205 (0xCD) + INVALID_VALUE, // input 206 (0xCE) + INVALID_VALUE, // input 207 (0xCF) + INVALID_VALUE, // input 208 (0xD0) + INVALID_VALUE, // input 209 (0xD1) + INVALID_VALUE, // input 210 (0xD2) + INVALID_VALUE, // input 211 (0xD3) + INVALID_VALUE, // input 212 (0xD4) + INVALID_VALUE, // input 213 (0xD5) + INVALID_VALUE, // input 214 (0xD6) + INVALID_VALUE, // input 215 (0xD7) + INVALID_VALUE, // input 216 (0xD8) + INVALID_VALUE, // input 217 (0xD9) + INVALID_VALUE, // input 218 (0xDA) + INVALID_VALUE, // input 219 (0xDB) + INVALID_VALUE, // input 220 (0xDC) + INVALID_VALUE, // input 221 (0xDD) + INVALID_VALUE, // input 222 (0xDE) + INVALID_VALUE, // input 223 (0xDF) + INVALID_VALUE, // input 224 (0xE0) + INVALID_VALUE, // input 225 (0xE1) + INVALID_VALUE, // input 226 (0xE2) + INVALID_VALUE, // input 227 (0xE3) + INVALID_VALUE, // input 228 (0xE4) + INVALID_VALUE, // input 229 (0xE5) + INVALID_VALUE, // input 230 (0xE6) + INVALID_VALUE, // input 231 (0xE7) + INVALID_VALUE, // input 232 (0xE8) + INVALID_VALUE, // input 233 (0xE9) + INVALID_VALUE, // input 234 (0xEA) + INVALID_VALUE, // input 235 (0xEB) + INVALID_VALUE, // input 236 (0xEC) + INVALID_VALUE, // input 237 (0xED) + INVALID_VALUE, // input 238 (0xEE) + INVALID_VALUE, // input 239 (0xEF) + INVALID_VALUE, // input 240 (0xF0) + INVALID_VALUE, // input 241 (0xF1) + INVALID_VALUE, // input 242 (0xF2) + INVALID_VALUE, // input 243 (0xF3) + INVALID_VALUE, // input 244 (0xF4) + INVALID_VALUE, // input 245 (0xF5) + INVALID_VALUE, // input 246 (0xF6) + INVALID_VALUE, // input 247 (0xF7) + INVALID_VALUE, // input 248 (0xF8) + INVALID_VALUE, // input 249 (0xF9) + INVALID_VALUE, // input 250 (0xFA) + INVALID_VALUE, // input 251 (0xFB) + INVALID_VALUE, // input 252 (0xFC) + INVALID_VALUE, // input 253 (0xFD) + INVALID_VALUE, // input 254 (0xFE) + INVALID_VALUE, // input 255 (0xFF) +]; diff --git a/third_party/rust/base64/src/tests.rs b/third_party/rust/base64/src/tests.rs new file mode 100644 index 0000000000..558ab4ee1b --- /dev/null +++ b/third_party/rust/base64/src/tests.rs @@ -0,0 +1,80 @@ +use crate::{decode_config, encode::encoded_size, encode_config_buf, CharacterSet, Config}; + +use std::str; + +use rand::{ + distributions::{Distribution, Uniform}, + seq::SliceRandom, + FromEntropy, Rng, +}; + +#[test] +fn roundtrip_random_config_short() { + // exercise the slower encode/decode routines that operate on shorter buffers more vigorously + roundtrip_random_config(Uniform::new(0, 50), 10_000); +} + +#[test] +fn roundtrip_random_config_long() { + roundtrip_random_config(Uniform::new(0, 1000), 10_000); +} + +pub fn assert_encode_sanity(encoded: &str, config: Config, input_len: usize) { + let input_rem = input_len % 3; + let expected_padding_len = if input_rem > 0 { + if config.pad { + 3 - input_rem + } else { + 0 + } + } else { + 0 + }; + + let expected_encoded_len = encoded_size(input_len, config).unwrap(); + + assert_eq!(expected_encoded_len, encoded.len()); + + let padding_len = encoded.chars().filter(|&c| c == '=').count(); + + assert_eq!(expected_padding_len, padding_len); + + let _ = str::from_utf8(encoded.as_bytes()).expect("Base64 should be valid utf8"); +} + +fn roundtrip_random_config(input_len_range: Uniform<usize>, iterations: u32) { + let mut input_buf: Vec<u8> = Vec::new(); + let mut encoded_buf = String::new(); + let mut rng = rand::rngs::SmallRng::from_entropy(); + + for _ in 0..iterations { + input_buf.clear(); + encoded_buf.clear(); + + let input_len = input_len_range.sample(&mut rng); + + let config = random_config(&mut rng); + + for _ in 0..input_len { + input_buf.push(rng.gen()); + } + + encode_config_buf(&input_buf, config, &mut encoded_buf); + + assert_encode_sanity(&encoded_buf, config, input_len); + + assert_eq!(input_buf, decode_config(&encoded_buf, config).unwrap()); + } +} + +pub fn random_config<R: Rng>(rng: &mut R) -> Config { + const CHARSETS: &[CharacterSet] = &[ + CharacterSet::UrlSafe, + CharacterSet::Standard, + CharacterSet::Crypt, + CharacterSet::ImapMutf7, + ]; + let charset = *CHARSETS.choose(rng).unwrap(); + + Config::new(charset, rng.gen()) +} diff --git a/third_party/rust/base64/src/write/encoder.rs b/third_party/rust/base64/src/write/encoder.rs new file mode 100644 index 0000000000..bece69b36b --- /dev/null +++ b/third_party/rust/base64/src/write/encoder.rs @@ -0,0 +1,355 @@ +use crate::encode::encode_to_slice; +use crate::{encode_config_slice, Config}; +use std::{ + cmp, fmt, + io::{ErrorKind, Result, Write}, +}; + +pub(crate) const BUF_SIZE: usize = 1024; +/// The most bytes whose encoding will fit in `BUF_SIZE` +const MAX_INPUT_LEN: usize = BUF_SIZE / 4 * 3; +// 3 bytes of input = 4 bytes of base64, always (because we don't allow line wrapping) +const MIN_ENCODE_CHUNK_SIZE: usize = 3; + +/// A `Write` implementation that base64 encodes data before delegating to the wrapped writer. +/// +/// Because base64 has special handling for the end of the input data (padding, etc), there's a +/// `finish()` method on this type that encodes any leftover input bytes and adds padding if +/// appropriate. It's called automatically when deallocated (see the `Drop` implementation), but +/// any error that occurs when invoking the underlying writer will be suppressed. If you want to +/// handle such errors, call `finish()` yourself. +/// +/// # Examples +/// +/// ``` +/// use std::io::Write; +/// +/// // use a vec as the simplest possible `Write` -- in real code this is probably a file, etc. +/// let mut wrapped_writer = Vec::new(); +/// { +/// let mut enc = base64::write::EncoderWriter::new( +/// &mut wrapped_writer, base64::STANDARD); +/// +/// // handle errors as you normally would +/// enc.write_all(b"asdf").unwrap(); +/// // could leave this out to be called by Drop, if you don't care +/// // about handling errors +/// enc.finish().unwrap(); +/// +/// } +/// +/// // base64 was written to the writer +/// assert_eq!(b"YXNkZg==", &wrapped_writer[..]); +/// +/// ``` +/// +/// # Panics +/// +/// Calling `write()` after `finish()` is invalid and will panic. +/// +/// # Errors +/// +/// Base64 encoding itself does not generate errors, but errors from the wrapped writer will be +/// returned as per the contract of `Write`. +/// +/// # Performance +/// +/// It has some minor performance loss compared to encoding slices (a couple percent). +/// It does not do any heap allocation. +pub struct EncoderWriter<'a, W: 'a + Write> { + config: Config, + /// Where encoded data is written to + w: &'a mut W, + /// Holds a partial chunk, if any, after the last `write()`, so that we may then fill the chunk + /// with the next `write()`, encode it, then proceed with the rest of the input normally. + extra_input: [u8; MIN_ENCODE_CHUNK_SIZE], + /// How much of `extra` is occupied, in `[0, MIN_ENCODE_CHUNK_SIZE]`. + extra_input_occupied_len: usize, + /// Buffer to encode into. May hold leftover encoded bytes from a previous write call that the underlying writer + /// did not write last time. + output: [u8; BUF_SIZE], + /// How much of `output` is occupied with encoded data that couldn't be written last time + output_occupied_len: usize, + /// True iff padding / partial last chunk has been written. + finished: bool, + /// panic safety: don't write again in destructor if writer panicked while we were writing to it + panicked: bool, +} + +impl<'a, W: Write> fmt::Debug for EncoderWriter<'a, W> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "extra_input: {:?} extra_input_occupied_len:{:?} output[..5]: {:?} output_occupied_len: {:?}", + self.extra_input, + self.extra_input_occupied_len, + &self.output[0..5], + self.output_occupied_len + ) + } +} + +impl<'a, W: Write> EncoderWriter<'a, W> { + /// Create a new encoder that will write to the provided delegate writer `w`. + pub fn new(w: &'a mut W, config: Config) -> EncoderWriter<'a, W> { + EncoderWriter { + config, + w, + extra_input: [0u8; MIN_ENCODE_CHUNK_SIZE], + extra_input_occupied_len: 0, + output: [0u8; BUF_SIZE], + output_occupied_len: 0, + finished: false, + panicked: false, + } + } + + /// Encode all remaining buffered data and write it, including any trailing incomplete input + /// triples and associated padding. + /// + /// Once this succeeds, no further writes can be performed, as that would produce invalid + /// base64. + /// + /// This may write to the delegate writer multiple times if the delegate writer does not accept all input provided + /// to its `write` each invocation. + /// + /// # Errors + /// + /// The first error that is not of [`ErrorKind::Interrupted`] will be returned. + pub fn finish(&mut self) -> Result<()> { + if self.finished { + return Ok(()); + }; + + self.write_all_encoded_output()?; + + if self.extra_input_occupied_len > 0 { + let encoded_len = encode_config_slice( + &self.extra_input[..self.extra_input_occupied_len], + self.config, + &mut self.output[..], + ); + + self.output_occupied_len = encoded_len; + + self.write_all_encoded_output()?; + + // write succeeded, do not write the encoding of extra again if finish() is retried + self.extra_input_occupied_len = 0; + } + + self.finished = true; + Ok(()) + } + + /// Write as much of the encoded output to the delegate writer as it will accept, and store the + /// leftovers to be attempted at the next write() call. Updates `self.output_occupied_len`. + /// + /// # Errors + /// + /// Errors from the delegate writer are returned. In the case of an error, + /// `self.output_occupied_len` will not be updated, as errors from `write` are specified to mean + /// that no write took place. + fn write_to_delegate(&mut self, current_output_len: usize) -> Result<()> { + self.panicked = true; + let res = self.w.write(&self.output[..current_output_len]); + self.panicked = false; + + res.map(|consumed| { + debug_assert!(consumed <= current_output_len); + + if consumed < current_output_len { + self.output_occupied_len = current_output_len.checked_sub(consumed).unwrap(); + // If we're blocking on I/O, the minor inefficiency of copying bytes to the + // start of the buffer is the least of our concerns... + // Rotate moves more than we need to, but copy_within isn't stabilized yet. + self.output.rotate_left(consumed); + } else { + self.output_occupied_len = 0; + } + }) + } + + /// Write all buffered encoded output. If this returns `Ok`, `self.output_occupied_len` is `0`. + /// + /// This is basically write_all for the remaining buffered data but without the undesirable + /// abort-on-`Ok(0)` behavior. + /// + /// # Errors + /// + /// Any error emitted by the delegate writer abort the write loop and is returned, unless it's + /// `Interrupted`, in which case the error is ignored and writes will continue. + fn write_all_encoded_output(&mut self) -> Result<()> { + while self.output_occupied_len > 0 { + let remaining_len = self.output_occupied_len; + match self.write_to_delegate(remaining_len) { + // try again on interrupts ala write_all + Err(ref e) if e.kind() == ErrorKind::Interrupted => {} + // other errors return + Err(e) => return Err(e), + // success no-ops because remaining length is already updated + Ok(_) => {} + }; + } + + debug_assert_eq!(0, self.output_occupied_len); + Ok(()) + } +} + +impl<'a, W: Write> Write for EncoderWriter<'a, W> { + /// Encode input and then write to the delegate writer. + /// + /// Under non-error circumstances, this returns `Ok` with the value being the number of bytes + /// of `input` consumed. The value may be `0`, which interacts poorly with `write_all`, which + /// interprets `Ok(0)` as an error, despite it being allowed by the contract of `write`. See + /// https://github.com/rust-lang/rust/issues/56889 for more on that. + /// + /// If the previous call to `write` provided more (encoded) data than the delegate writer could + /// accept in a single call to its `write`, the remaining data is buffered. As long as buffered + /// data is present, subsequent calls to `write` will try to write the remaining buffered data + /// to the delegate and return either `Ok(0)` -- and therefore not consume any of `input` -- or + /// an error. + /// + /// # Errors + /// + /// Any errors emitted by the delegate writer are returned. + fn write(&mut self, input: &[u8]) -> Result<usize> { + if self.finished { + panic!("Cannot write more after calling finish()"); + } + + if input.is_empty() { + return Ok(0); + } + + // The contract of `Write::write` places some constraints on this implementation: + // - a call to `write()` represents at most one call to a wrapped `Write`, so we can't + // iterate over the input and encode multiple chunks. + // - Errors mean that "no bytes were written to this writer", so we need to reset the + // internal state to what it was before the error occurred + + // before reading any input, write any leftover encoded output from last time + if self.output_occupied_len > 0 { + let current_len = self.output_occupied_len; + return self + .write_to_delegate(current_len) + // did not read any input + .map(|_| 0); + } + + debug_assert_eq!(0, self.output_occupied_len); + + // how many bytes, if any, were read into `extra` to create a triple to encode + let mut extra_input_read_len = 0; + let mut input = input; + + let orig_extra_len = self.extra_input_occupied_len; + + let mut encoded_size = 0; + // always a multiple of MIN_ENCODE_CHUNK_SIZE + let mut max_input_len = MAX_INPUT_LEN; + + // process leftover un-encoded input from last write + if self.extra_input_occupied_len > 0 { + debug_assert!(self.extra_input_occupied_len < 3); + if input.len() + self.extra_input_occupied_len >= MIN_ENCODE_CHUNK_SIZE { + // Fill up `extra`, encode that into `output`, and consume as much of the rest of + // `input` as possible. + // We could write just the encoding of `extra` by itself but then we'd have to + // return after writing only 4 bytes, which is inefficient if the underlying writer + // would make a syscall. + extra_input_read_len = MIN_ENCODE_CHUNK_SIZE - self.extra_input_occupied_len; + debug_assert!(extra_input_read_len > 0); + // overwrite only bytes that weren't already used. If we need to rollback extra_len + // (when the subsequent write errors), the old leading bytes will still be there. + self.extra_input[self.extra_input_occupied_len..MIN_ENCODE_CHUNK_SIZE] + .copy_from_slice(&input[0..extra_input_read_len]); + + let len = encode_to_slice( + &self.extra_input[0..MIN_ENCODE_CHUNK_SIZE], + &mut self.output[..], + self.config.char_set.encode_table(), + ); + debug_assert_eq!(4, len); + + input = &input[extra_input_read_len..]; + + // consider extra to be used up, since we encoded it + self.extra_input_occupied_len = 0; + // don't clobber where we just encoded to + encoded_size = 4; + // and don't read more than can be encoded + max_input_len = MAX_INPUT_LEN - MIN_ENCODE_CHUNK_SIZE; + + // fall through to normal encoding + } else { + // `extra` and `input` are non empty, but `|extra| + |input| < 3`, so there must be + // 1 byte in each. + debug_assert_eq!(1, input.len()); + debug_assert_eq!(1, self.extra_input_occupied_len); + + self.extra_input[self.extra_input_occupied_len] = input[0]; + self.extra_input_occupied_len += 1; + return Ok(1); + }; + } else if input.len() < MIN_ENCODE_CHUNK_SIZE { + // `extra` is empty, and `input` fits inside it + self.extra_input[0..input.len()].copy_from_slice(input); + self.extra_input_occupied_len = input.len(); + return Ok(input.len()); + }; + + // either 0 or 1 complete chunks encoded from extra + debug_assert!(encoded_size == 0 || encoded_size == 4); + debug_assert!( + // didn't encode extra input + MAX_INPUT_LEN == max_input_len + // encoded one triple + || MAX_INPUT_LEN == max_input_len + MIN_ENCODE_CHUNK_SIZE + ); + + // encode complete triples only + let input_complete_chunks_len = input.len() - (input.len() % MIN_ENCODE_CHUNK_SIZE); + let input_chunks_to_encode_len = cmp::min(input_complete_chunks_len, max_input_len); + debug_assert_eq!(0, max_input_len % MIN_ENCODE_CHUNK_SIZE); + debug_assert_eq!(0, input_chunks_to_encode_len % MIN_ENCODE_CHUNK_SIZE); + + encoded_size += encode_to_slice( + &input[..(input_chunks_to_encode_len)], + &mut self.output[encoded_size..], + self.config.char_set.encode_table(), + ); + + // not updating `self.output_occupied_len` here because if the below write fails, it should + // "never take place" -- the buffer contents we encoded are ignored and perhaps retried + // later, if the consumer chooses. + + self.write_to_delegate(encoded_size) + // no matter whether we wrote the full encoded buffer or not, we consumed the same + // input + .map(|_| extra_input_read_len + input_chunks_to_encode_len) + .map_err(|e| { + // in case we filled and encoded `extra`, reset extra_len + self.extra_input_occupied_len = orig_extra_len; + + e + }) + } + + /// Because this is usually treated as OK to call multiple times, it will *not* flush any + /// incomplete chunks of input or write padding. + fn flush(&mut self) -> Result<()> { + self.write_all_encoded_output()?; + self.w.flush() + } +} + +impl<'a, W: Write> Drop for EncoderWriter<'a, W> { + fn drop(&mut self) { + if !self.panicked { + // like `BufWriter`, ignore errors during drop + let _ = self.finish(); + } + } +} diff --git a/third_party/rust/base64/src/write/encoder_tests.rs b/third_party/rust/base64/src/write/encoder_tests.rs new file mode 100644 index 0000000000..59a6127a9d --- /dev/null +++ b/third_party/rust/base64/src/write/encoder_tests.rs @@ -0,0 +1,568 @@ +use super::EncoderWriter; +use crate::tests::random_config; +use crate::{encode_config, encode_config_buf, STANDARD_NO_PAD, URL_SAFE}; + +use std::io::{Cursor, Write}; +use std::{cmp, io, str}; + +use rand::Rng; + +#[test] +fn encode_three_bytes() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, URL_SAFE); + + let sz = enc.write(b"abc").unwrap(); + assert_eq!(sz, 3); + } + assert_eq!(&c.get_ref()[..], encode_config("abc", URL_SAFE).as_bytes()); +} + +#[test] +fn encode_nine_bytes_two_writes() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, URL_SAFE); + + let sz = enc.write(b"abcdef").unwrap(); + assert_eq!(sz, 6); + let sz = enc.write(b"ghi").unwrap(); + assert_eq!(sz, 3); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abcdefghi", URL_SAFE).as_bytes() + ); +} + +#[test] +fn encode_one_then_two_bytes() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, URL_SAFE); + + let sz = enc.write(b"a").unwrap(); + assert_eq!(sz, 1); + let sz = enc.write(b"bc").unwrap(); + assert_eq!(sz, 2); + } + assert_eq!(&c.get_ref()[..], encode_config("abc", URL_SAFE).as_bytes()); +} + +#[test] +fn encode_one_then_five_bytes() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, URL_SAFE); + + let sz = enc.write(b"a").unwrap(); + assert_eq!(sz, 1); + let sz = enc.write(b"bcdef").unwrap(); + assert_eq!(sz, 5); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abcdef", URL_SAFE).as_bytes() + ); +} + +#[test] +fn encode_1_2_3_bytes() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, URL_SAFE); + + let sz = enc.write(b"a").unwrap(); + assert_eq!(sz, 1); + let sz = enc.write(b"bc").unwrap(); + assert_eq!(sz, 2); + let sz = enc.write(b"def").unwrap(); + assert_eq!(sz, 3); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abcdef", URL_SAFE).as_bytes() + ); +} + +#[test] +fn encode_with_padding() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, URL_SAFE); + + enc.write_all(b"abcd").unwrap(); + + enc.flush().unwrap(); + } + assert_eq!(&c.get_ref()[..], encode_config("abcd", URL_SAFE).as_bytes()); +} + +#[test] +fn encode_with_padding_multiple_writes() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, URL_SAFE); + + assert_eq!(1, enc.write(b"a").unwrap()); + assert_eq!(2, enc.write(b"bc").unwrap()); + assert_eq!(3, enc.write(b"def").unwrap()); + assert_eq!(1, enc.write(b"g").unwrap()); + + enc.flush().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abcdefg", URL_SAFE).as_bytes() + ); +} + +#[test] +fn finish_writes_extra_byte() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, URL_SAFE); + + assert_eq!(6, enc.write(b"abcdef").unwrap()); + + // will be in extra + assert_eq!(1, enc.write(b"g").unwrap()); + + // 1 trailing byte = 2 encoded chars + let _ = enc.finish().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abcdefg", URL_SAFE).as_bytes() + ); +} + +#[test] +fn write_partial_chunk_encodes_partial_chunk() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, STANDARD_NO_PAD); + + // nothing encoded yet + assert_eq!(2, enc.write(b"ab").unwrap()); + // encoded here + let _ = enc.finish().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("ab", STANDARD_NO_PAD).as_bytes() + ); + assert_eq!(3, c.get_ref().len()); +} + +#[test] +fn write_1_chunk_encodes_complete_chunk() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, STANDARD_NO_PAD); + + assert_eq!(3, enc.write(b"abc").unwrap()); + let _ = enc.finish().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abc", STANDARD_NO_PAD).as_bytes() + ); + assert_eq!(4, c.get_ref().len()); +} + +#[test] +fn write_1_chunk_and_partial_encodes_only_complete_chunk() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, STANDARD_NO_PAD); + + // "d" not written + assert_eq!(3, enc.write(b"abcd").unwrap()); + let _ = enc.finish().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abc", STANDARD_NO_PAD).as_bytes() + ); + assert_eq!(4, c.get_ref().len()); +} + +#[test] +fn write_2_partials_to_exactly_complete_chunk_encodes_complete_chunk() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, STANDARD_NO_PAD); + + assert_eq!(1, enc.write(b"a").unwrap()); + assert_eq!(2, enc.write(b"bc").unwrap()); + let _ = enc.finish().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abc", STANDARD_NO_PAD).as_bytes() + ); + assert_eq!(4, c.get_ref().len()); +} + +#[test] +fn write_partial_then_enough_to_complete_chunk_but_not_complete_another_chunk_encodes_complete_chunk_without_consuming_remaining( +) { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, STANDARD_NO_PAD); + + assert_eq!(1, enc.write(b"a").unwrap()); + // doesn't consume "d" + assert_eq!(2, enc.write(b"bcd").unwrap()); + let _ = enc.finish().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abc", STANDARD_NO_PAD).as_bytes() + ); + assert_eq!(4, c.get_ref().len()); +} + +#[test] +fn write_partial_then_enough_to_complete_chunk_and_another_chunk_encodes_complete_chunks() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, STANDARD_NO_PAD); + + assert_eq!(1, enc.write(b"a").unwrap()); + // completes partial chunk, and another chunk + assert_eq!(5, enc.write(b"bcdef").unwrap()); + let _ = enc.finish().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abcdef", STANDARD_NO_PAD).as_bytes() + ); + assert_eq!(8, c.get_ref().len()); +} + +#[test] +fn write_partial_then_enough_to_complete_chunk_and_another_chunk_and_another_partial_chunk_encodes_only_complete_chunks( +) { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, STANDARD_NO_PAD); + + assert_eq!(1, enc.write(b"a").unwrap()); + // completes partial chunk, and another chunk, with one more partial chunk that's not + // consumed + assert_eq!(5, enc.write(b"bcdefe").unwrap()); + let _ = enc.finish().unwrap(); + } + assert_eq!( + &c.get_ref()[..], + encode_config("abcdef", STANDARD_NO_PAD).as_bytes() + ); + assert_eq!(8, c.get_ref().len()); +} + +#[test] +fn drop_calls_finish_for_you() { + let mut c = Cursor::new(Vec::new()); + { + let mut enc = EncoderWriter::new(&mut c, STANDARD_NO_PAD); + assert_eq!(1, enc.write(b"a").unwrap()); + } + assert_eq!( + &c.get_ref()[..], + encode_config("a", STANDARD_NO_PAD).as_bytes() + ); + assert_eq!(2, c.get_ref().len()); +} + +#[test] +fn every_possible_split_of_input() { + let mut rng = rand::thread_rng(); + let mut orig_data = Vec::<u8>::new(); + let mut stream_encoded = Vec::<u8>::new(); + let mut normal_encoded = String::new(); + + let size = 5_000; + + for i in 0..size { + orig_data.clear(); + stream_encoded.clear(); + normal_encoded.clear(); + + for _ in 0..size { + orig_data.push(rng.gen()); + } + + let config = random_config(&mut rng); + encode_config_buf(&orig_data, config, &mut normal_encoded); + + { + let mut stream_encoder = EncoderWriter::new(&mut stream_encoded, config); + // Write the first i bytes, then the rest + stream_encoder.write_all(&orig_data[0..i]).unwrap(); + stream_encoder.write_all(&orig_data[i..]).unwrap(); + } + + assert_eq!(normal_encoded, str::from_utf8(&stream_encoded).unwrap()); + } +} + +#[test] +fn encode_random_config_matches_normal_encode_reasonable_input_len() { + // choose up to 2 * buf size, so ~half the time it'll use a full buffer + do_encode_random_config_matches_normal_encode(super::encoder::BUF_SIZE * 2) +} + +#[test] +fn encode_random_config_matches_normal_encode_tiny_input_len() { + do_encode_random_config_matches_normal_encode(10) +} + +#[test] +fn retrying_writes_that_error_with_interrupted_works() { + let mut rng = rand::thread_rng(); + let mut orig_data = Vec::<u8>::new(); + let mut stream_encoded = Vec::<u8>::new(); + let mut normal_encoded = String::new(); + + for _ in 0..1_000 { + orig_data.clear(); + stream_encoded.clear(); + normal_encoded.clear(); + + let orig_len: usize = rng.gen_range(100, 20_000); + for _ in 0..orig_len { + orig_data.push(rng.gen()); + } + + // encode the normal way + let config = random_config(&mut rng); + encode_config_buf(&orig_data, config, &mut normal_encoded); + + // encode via the stream encoder + { + let mut interrupt_rng = rand::thread_rng(); + let mut interrupting_writer = InterruptingWriter { + w: &mut stream_encoded, + rng: &mut interrupt_rng, + fraction: 0.8, + }; + + let mut stream_encoder = EncoderWriter::new(&mut interrupting_writer, config); + let mut bytes_consumed = 0; + while bytes_consumed < orig_len { + // use short inputs since we want to use `extra` a lot as that's what needs rollback + // when errors occur + let input_len: usize = cmp::min(rng.gen_range(0, 10), orig_len - bytes_consumed); + + retry_interrupted_write_all( + &mut stream_encoder, + &orig_data[bytes_consumed..bytes_consumed + input_len], + ) + .unwrap(); + + bytes_consumed += input_len; + } + + loop { + let res = stream_encoder.finish(); + match res { + Ok(_) => break, + Err(e) => match e.kind() { + io::ErrorKind::Interrupted => continue, + _ => Err(e).unwrap(), // bail + }, + } + } + + assert_eq!(orig_len, bytes_consumed); + } + + assert_eq!(normal_encoded, str::from_utf8(&stream_encoded).unwrap()); + } +} + +#[test] +fn writes_that_only_write_part_of_input_and_sometimes_interrupt_produce_correct_encoded_data() { + let mut rng = rand::thread_rng(); + let mut orig_data = Vec::<u8>::new(); + let mut stream_encoded = Vec::<u8>::new(); + let mut normal_encoded = String::new(); + + for _ in 0..1_000 { + orig_data.clear(); + stream_encoded.clear(); + normal_encoded.clear(); + + let orig_len: usize = rng.gen_range(100, 20_000); + for _ in 0..orig_len { + orig_data.push(rng.gen()); + } + + // encode the normal way + let config = random_config(&mut rng); + encode_config_buf(&orig_data, config, &mut normal_encoded); + + // encode via the stream encoder + { + let mut partial_rng = rand::thread_rng(); + let mut partial_writer = PartialInterruptingWriter { + w: &mut stream_encoded, + rng: &mut partial_rng, + full_input_fraction: 0.1, + no_interrupt_fraction: 0.1, + }; + + let mut stream_encoder = EncoderWriter::new(&mut partial_writer, config); + let mut bytes_consumed = 0; + while bytes_consumed < orig_len { + // use at most medium-length inputs to exercise retry logic more aggressively + let input_len: usize = cmp::min(rng.gen_range(0, 100), orig_len - bytes_consumed); + + let res = + stream_encoder.write(&orig_data[bytes_consumed..bytes_consumed + input_len]); + + // retry on interrupt + match res { + Ok(len) => bytes_consumed += len, + Err(e) => match e.kind() { + io::ErrorKind::Interrupted => continue, + _ => { + panic!("should not see other errors"); + } + }, + } + } + + stream_encoder.finish().unwrap(); + + assert_eq!(orig_len, bytes_consumed); + } + + assert_eq!(normal_encoded, str::from_utf8(&stream_encoded).unwrap()); + } +} + +/// Retry writes until all the data is written or an error that isn't Interrupted is returned. +fn retry_interrupted_write_all<W: Write>(w: &mut W, buf: &[u8]) -> io::Result<()> { + let mut bytes_consumed = 0; + + while bytes_consumed < buf.len() { + let res = w.write(&buf[bytes_consumed..]); + + match res { + Ok(len) => bytes_consumed += len, + Err(e) => match e.kind() { + io::ErrorKind::Interrupted => continue, + _ => return Err(e), + }, + } + } + + Ok(()) +} + +fn do_encode_random_config_matches_normal_encode(max_input_len: usize) { + let mut rng = rand::thread_rng(); + let mut orig_data = Vec::<u8>::new(); + let mut stream_encoded = Vec::<u8>::new(); + let mut normal_encoded = String::new(); + + for _ in 0..1_000 { + orig_data.clear(); + stream_encoded.clear(); + normal_encoded.clear(); + + let orig_len: usize = rng.gen_range(100, 20_000); + for _ in 0..orig_len { + orig_data.push(rng.gen()); + } + + // encode the normal way + let config = random_config(&mut rng); + encode_config_buf(&orig_data, config, &mut normal_encoded); + + // encode via the stream encoder + { + let mut stream_encoder = EncoderWriter::new(&mut stream_encoded, config); + let mut bytes_consumed = 0; + while bytes_consumed < orig_len { + let input_len: usize = + cmp::min(rng.gen_range(0, max_input_len), orig_len - bytes_consumed); + + // write a little bit of the data + stream_encoder + .write_all(&orig_data[bytes_consumed..bytes_consumed + input_len]) + .unwrap(); + + bytes_consumed += input_len; + } + + stream_encoder.finish().unwrap(); + + assert_eq!(orig_len, bytes_consumed); + } + + assert_eq!(normal_encoded, str::from_utf8(&stream_encoded).unwrap()); + } +} + +/// A `Write` implementation that returns Interrupted some fraction of the time, randomly. +struct InterruptingWriter<'a, W: 'a + Write, R: 'a + Rng> { + w: &'a mut W, + rng: &'a mut R, + /// In [0, 1]. If a random number in [0, 1] is `<= threshold`, `Write` methods will return + /// an `Interrupted` error + fraction: f64, +} + +impl<'a, W: Write, R: Rng> Write for InterruptingWriter<'a, W, R> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + if self.rng.gen_range(0.0, 1.0) <= self.fraction { + return Err(io::Error::new(io::ErrorKind::Interrupted, "interrupted")); + } + + self.w.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + if self.rng.gen_range(0.0, 1.0) <= self.fraction { + return Err(io::Error::new(io::ErrorKind::Interrupted, "interrupted")); + } + + self.w.flush() + } +} + +/// A `Write` implementation that sometimes will only write part of its input. +struct PartialInterruptingWriter<'a, W: 'a + Write, R: 'a + Rng> { + w: &'a mut W, + rng: &'a mut R, + /// In [0, 1]. If a random number in [0, 1] is `<= threshold`, `write()` will write all its + /// input. Otherwise, it will write a random substring + full_input_fraction: f64, + no_interrupt_fraction: f64, +} + +impl<'a, W: Write, R: Rng> Write for PartialInterruptingWriter<'a, W, R> { + fn write(&mut self, buf: &[u8]) -> io::Result<usize> { + if self.rng.gen_range(0.0, 1.0) > self.no_interrupt_fraction { + return Err(io::Error::new(io::ErrorKind::Interrupted, "interrupted")); + } + + if self.rng.gen_range(0.0, 1.0) <= self.full_input_fraction || buf.len() == 0 { + // pass through the buf untouched + self.w.write(buf) + } else { + // only use a prefix of it + self.w + .write(&buf[0..(self.rng.gen_range(0, buf.len() - 1))]) + } + } + + fn flush(&mut self) -> io::Result<()> { + self.w.flush() + } +} diff --git a/third_party/rust/base64/src/write/mod.rs b/third_party/rust/base64/src/write/mod.rs new file mode 100644 index 0000000000..f8ed70768c --- /dev/null +++ b/third_party/rust/base64/src/write/mod.rs @@ -0,0 +1,6 @@ +//! Implementations of `io::Write` to transparently handle base64. +mod encoder; +pub use self::encoder::EncoderWriter; + +#[cfg(test)] +mod encoder_tests; diff --git a/third_party/rust/base64/tests/decode.rs b/third_party/rust/base64/tests/decode.rs new file mode 100644 index 0000000000..fbe07691dc --- /dev/null +++ b/third_party/rust/base64/tests/decode.rs @@ -0,0 +1,310 @@ +extern crate base64; + +use base64::*; + +mod helpers; + +use self::helpers::*; + +#[test] +fn decode_rfc4648_0() { + compare_decode("", ""); +} + +#[test] +fn decode_rfc4648_1() { + compare_decode("f", "Zg=="); +} + +#[test] +fn decode_rfc4648_1_just_a_bit_of_padding() { + // allows less padding than required + compare_decode("f", "Zg="); +} + +#[test] +fn decode_rfc4648_1_no_padding() { + compare_decode("f", "Zg"); +} + +#[test] +fn decode_rfc4648_2() { + compare_decode("fo", "Zm8="); +} + +#[test] +fn decode_rfc4648_2_no_padding() { + compare_decode("fo", "Zm8"); +} + +#[test] +fn decode_rfc4648_3() { + compare_decode("foo", "Zm9v"); +} + +#[test] +fn decode_rfc4648_4() { + compare_decode("foob", "Zm9vYg=="); +} + +#[test] +fn decode_rfc4648_4_no_padding() { + compare_decode("foob", "Zm9vYg"); +} + +#[test] +fn decode_rfc4648_5() { + compare_decode("fooba", "Zm9vYmE="); +} + +#[test] +fn decode_rfc4648_5_no_padding() { + compare_decode("fooba", "Zm9vYmE"); +} + +#[test] +fn decode_rfc4648_6() { + compare_decode("foobar", "Zm9vYmFy"); +} + +#[test] +fn decode_reject_null() { + assert_eq!( + DecodeError::InvalidByte(3, 0x0), + decode_config("YWx\0pY2U==", config_std_pad()).unwrap_err() + ); +} + +#[test] +fn decode_single_pad_byte_after_2_chars_in_trailing_quad_ok() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("Zg="); + + let input_len = num_quads * 3 + 1; + + // Since there are 3 bytes in the trailing quad, want to be sure this allows for the fact + // that it could be bad padding rather than assuming that it will decode to 2 bytes and + // therefore allow 1 extra round of fast decode logic (stage 1 / 2). + + let mut decoded = Vec::new(); + decoded.resize(input_len, 0); + + assert_eq!( + input_len, + decode_config_slice(&s, STANDARD, &mut decoded).unwrap() + ); + } +} + +//this is a MAY in the rfc: https://tools.ietf.org/html/rfc4648#section-3.3 +#[test] +fn decode_1_pad_byte_in_fast_loop_then_extra_padding_chunk_error() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("YWxpY2U====="); + + // since the first 8 bytes are handled in stage 1 or 2, the padding is detected as a + // generic invalid byte, not specifcally a padding issue. + // Could argue that the *next* padding byte (in the next quad) is technically the first + // erroneous one, but reporting that accurately is more complex and probably nobody cares + assert_eq!( + DecodeError::InvalidByte(num_quads * 4 + 7, b'='), + decode(&s).unwrap_err() + ); + } +} + +#[test] +fn decode_2_pad_bytes_in_leftovers_then_extra_padding_chunk_error() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("YWxpY2UABB===="); + + // 6 bytes (4 padding) after last 8-byte chunk, so it's decoded by stage 4. + // First padding byte is invalid. + assert_eq!( + DecodeError::InvalidByte(num_quads * 4 + 10, b'='), + decode(&s).unwrap_err() + ); + } +} + +#[test] +fn decode_valid_bytes_after_padding_in_leftovers_error() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("YWxpY2UABB=B"); + + // 4 bytes after last 8-byte chunk, so it's decoded by stage 4. + // First (and only) padding byte is invalid. + assert_eq!( + DecodeError::InvalidByte(num_quads * 4 + 10, b'='), + decode(&s).unwrap_err() + ); + } +} + +#[test] +fn decode_absurd_pad_error() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("==Y=Wx===pY=2U====="); + + // Plenty of remaining bytes, so handled by stage 1 or 2. + // first padding byte + assert_eq!( + DecodeError::InvalidByte(num_quads * 4, b'='), + decode(&s).unwrap_err() + ); + } +} + +#[test] +fn decode_extra_padding_after_1_pad_bytes_in_trailing_quad_returns_error() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("EEE==="); + + // handled by stage 1, 2, or 4 depending on length + // first padding byte -- which would be legal if it was the only padding + assert_eq!( + DecodeError::InvalidByte(num_quads * 4 + 3, b'='), + decode(&s).unwrap_err() + ); + } +} + +#[test] +fn decode_extra_padding_after_2_pad_bytes_in_trailing_quad_2_returns_error() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("EE===="); + + // handled by stage 1, 2, or 4 depending on length + // first padding byte -- which would be legal if it was by itself + assert_eq!( + DecodeError::InvalidByte(num_quads * 4 + 2, b'='), + decode(&s).unwrap_err() + ); + } +} + +#[test] +fn decode_start_quad_with_padding_returns_error() { + for num_quads in 0..25 { + // add enough padding to ensure that we'll hit all 4 stages at the different lengths + for pad_bytes in 1..32 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + let padding: String = std::iter::repeat("=").take(pad_bytes).collect(); + s.push_str(&padding); + + if pad_bytes % 4 == 1 { + // detected in early length check + assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err()); + } else { + // padding lengths 2 - 8 are handled by stage 4 + // padding length >= 8 will hit at least one chunk at stages 1, 2, 3 at different + // prefix lengths + assert_eq!( + DecodeError::InvalidByte(num_quads * 4, b'='), + decode(&s).unwrap_err() + ); + } + } + } +} + +#[test] +fn decode_padding_followed_by_non_padding_returns_error() { + for num_quads in 0..25 { + for pad_bytes in 0..31 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + let padding: String = std::iter::repeat("=").take(pad_bytes).collect(); + s.push_str(&padding); + s.push_str("E"); + + if pad_bytes % 4 == 0 { + assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err()); + } else { + // pad len 1 - 8 will be handled by stage 4 + // pad len 9 (suffix len 10) will have 8 bytes of padding handled by stage 3 + // first padding byte + assert_eq!( + DecodeError::InvalidByte(num_quads * 4, b'='), + decode(&s).unwrap_err() + ); + } + } + } +} + +#[test] +fn decode_one_char_in_quad_with_padding_error() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("E="); + + assert_eq!( + DecodeError::InvalidByte(num_quads * 4 + 1, b'='), + decode(&s).unwrap_err() + ); + + // more padding doesn't change the error + s.push_str("="); + assert_eq!( + DecodeError::InvalidByte(num_quads * 4 + 1, b'='), + decode(&s).unwrap_err() + ); + + s.push_str("="); + assert_eq!( + DecodeError::InvalidByte(num_quads * 4 + 1, b'='), + decode(&s).unwrap_err() + ); + } +} + +#[test] +fn decode_one_char_in_quad_without_padding_error() { + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push('E'); + + assert_eq!(DecodeError::InvalidLength, decode(&s).unwrap_err()); + } +} + +#[test] +fn decode_reject_invalid_bytes_with_correct_error() { + for length in 1..100 { + for index in 0_usize..length { + for invalid_byte in " \t\n\r\x0C\x0B\x00%*.".bytes() { + let prefix: String = std::iter::repeat("A").take(index).collect(); + let suffix: String = std::iter::repeat("B").take(length - index - 1).collect(); + + let input = prefix + &String::from_utf8(vec![invalid_byte]).unwrap() + &suffix; + assert_eq!( + length, + input.len(), + "length {} error position {}", + length, + index + ); + + if length % 4 == 1 { + assert_eq!(DecodeError::InvalidLength, decode(&input).unwrap_err()); + } else { + assert_eq!( + DecodeError::InvalidByte(index, invalid_byte), + decode(&input).unwrap_err() + ); + } + } + } + } +} + +fn config_std_pad() -> Config { + Config::new(CharacterSet::Standard, true) +} diff --git a/third_party/rust/base64/tests/encode.rs b/third_party/rust/base64/tests/encode.rs new file mode 100644 index 0000000000..0004be00fe --- /dev/null +++ b/third_party/rust/base64/tests/encode.rs @@ -0,0 +1,105 @@ +extern crate base64; + +use base64::*; + +fn compare_encode(expected: &str, target: &[u8]) { + assert_eq!(expected, encode(target)); +} + +#[test] +fn encode_rfc4648_0() { + compare_encode("", b""); +} + +#[test] +fn encode_rfc4648_1() { + compare_encode("Zg==", b"f"); +} + +#[test] +fn encode_rfc4648_2() { + compare_encode("Zm8=", b"fo"); +} + +#[test] +fn encode_rfc4648_3() { + compare_encode("Zm9v", b"foo"); +} + +#[test] +fn encode_rfc4648_4() { + compare_encode("Zm9vYg==", b"foob"); +} + +#[test] +fn encode_rfc4648_5() { + compare_encode("Zm9vYmE=", b"fooba"); +} + +#[test] +fn encode_rfc4648_6() { + compare_encode("Zm9vYmFy", b"foobar"); +} + +#[test] +fn encode_all_ascii() { + let mut ascii = Vec::<u8>::with_capacity(128); + + for i in 0..128 { + ascii.push(i); + } + + compare_encode( + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7P\ + D0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8\ + =", + &ascii, + ); +} + +#[test] +fn encode_all_bytes() { + let mut bytes = Vec::<u8>::with_capacity(256); + + for i in 0..255 { + bytes.push(i); + } + bytes.push(255); //bug with "overflowing" ranges? + + compare_encode( + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7P\ + D0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn\ + +AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6\ + /wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==", + &bytes, + ); +} + +#[test] +fn encode_all_bytes_url() { + let mut bytes = Vec::<u8>::with_capacity(256); + + for i in 0..255 { + bytes.push(i); + } + bytes.push(255); //bug with "overflowing" ranges? + + assert_eq!( + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0\ + -P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn\ + -AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq\ + -wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy\ + 8_T19vf4-fr7_P3-_w==", + encode_config(&bytes, URL_SAFE) + ); +} + +#[test] +fn encode_url_safe_without_padding() { + let encoded = encode_config(b"alice", URL_SAFE_NO_PAD); + assert_eq!(&encoded, "YWxpY2U"); + assert_eq!( + String::from_utf8(decode(&encoded).unwrap()).unwrap(), + "alice" + ); +} diff --git a/third_party/rust/base64/tests/helpers.rs b/third_party/rust/base64/tests/helpers.rs new file mode 100644 index 0000000000..5144988448 --- /dev/null +++ b/third_party/rust/base64/tests/helpers.rs @@ -0,0 +1,14 @@ +extern crate base64; + +use base64::*; + +pub fn compare_decode(expected: &str, target: &str) { + assert_eq!( + expected, + String::from_utf8(decode(target).unwrap()).unwrap() + ); + assert_eq!( + expected, + String::from_utf8(decode(target.as_bytes()).unwrap()).unwrap() + ); +} diff --git a/third_party/rust/base64/tests/tests.rs b/third_party/rust/base64/tests/tests.rs new file mode 100644 index 0000000000..11fed960ff --- /dev/null +++ b/third_party/rust/base64/tests/tests.rs @@ -0,0 +1,194 @@ +extern crate base64; +extern crate rand; + +use rand::{FromEntropy, Rng}; + +use base64::*; + +mod helpers; +use self::helpers::*; + +// generate random contents of the specified length and test encode/decode roundtrip +fn roundtrip_random( + byte_buf: &mut Vec<u8>, + str_buf: &mut String, + config: Config, + byte_len: usize, + approx_values_per_byte: u8, + max_rounds: u64, +) { + // let the short ones be short but don't let it get too crazy large + let num_rounds = calculate_number_of_rounds(byte_len, approx_values_per_byte, max_rounds); + let mut r = rand::rngs::SmallRng::from_entropy(); + let mut decode_buf = Vec::new(); + + for _ in 0..num_rounds { + byte_buf.clear(); + str_buf.clear(); + decode_buf.clear(); + while byte_buf.len() < byte_len { + byte_buf.push(r.gen::<u8>()); + } + + encode_config_buf(&byte_buf, config, str_buf); + decode_config_buf(&str_buf, config, &mut decode_buf).unwrap(); + + assert_eq!(byte_buf, &decode_buf); + } +} + +fn calculate_number_of_rounds(byte_len: usize, approx_values_per_byte: u8, max: u64) -> u64 { + // don't overflow + let mut prod = approx_values_per_byte as u64; + + for _ in 0..byte_len { + if prod > max { + return max; + } + + prod = prod.saturating_mul(prod); + } + + prod +} + +fn no_pad_config() -> Config { + Config::new(CharacterSet::Standard, false) +} + +#[test] +fn roundtrip_random_short_standard() { + let mut byte_buf: Vec<u8> = Vec::new(); + let mut str_buf = String::new(); + + for input_len in 0..40 { + roundtrip_random(&mut byte_buf, &mut str_buf, STANDARD, input_len, 4, 10000); + } +} + +#[test] +fn roundtrip_random_with_fast_loop_standard() { + let mut byte_buf: Vec<u8> = Vec::new(); + let mut str_buf = String::new(); + + for input_len in 40..100 { + roundtrip_random(&mut byte_buf, &mut str_buf, STANDARD, input_len, 4, 1000); + } +} + +#[test] +fn roundtrip_random_short_no_padding() { + let mut byte_buf: Vec<u8> = Vec::new(); + let mut str_buf = String::new(); + + for input_len in 0..40 { + roundtrip_random( + &mut byte_buf, + &mut str_buf, + no_pad_config(), + input_len, + 4, + 10000, + ); + } +} + +#[test] +fn roundtrip_random_no_padding() { + let mut byte_buf: Vec<u8> = Vec::new(); + let mut str_buf = String::new(); + + for input_len in 40..100 { + roundtrip_random( + &mut byte_buf, + &mut str_buf, + no_pad_config(), + input_len, + 4, + 1000, + ); + } +} + +#[test] +fn roundtrip_decode_trailing_10_bytes() { + // This is a special case because we decode 8 byte blocks of input at a time as much as we can, + // ideally unrolled to 32 bytes at a time, in stages 1 and 2. Since we also write a u64's worth + // of bytes (8) to the output, we always write 2 garbage bytes that then will be overwritten by + // the NEXT block. However, if the next block only contains 2 bytes, it will decode to 1 byte, + // and therefore be too short to cover up the trailing 2 garbage bytes. Thus, we have stage 3 + // to handle that case. + + for num_quads in 0..25 { + let mut s: String = std::iter::repeat("ABCD").take(num_quads).collect(); + s.push_str("EFGHIJKLZg"); + + let decoded = decode(&s).unwrap(); + assert_eq!(num_quads * 3 + 7, decoded.len()); + + assert_eq!(s, encode_config(&decoded, STANDARD_NO_PAD)); + } +} + +#[test] +fn display_wrapper_matches_normal_encode() { + let mut bytes = Vec::<u8>::with_capacity(256); + + for i in 0..255 { + bytes.push(i); + } + bytes.push(255); + + assert_eq!( + encode(&bytes), + format!( + "{}", + base64::display::Base64Display::with_config(&bytes, STANDARD) + ) + ); +} + +#[test] +fn because_we_can() { + compare_decode("alice", "YWxpY2U="); + compare_decode("alice", &encode(b"alice")); + compare_decode("alice", &encode(&decode(&encode(b"alice")).unwrap())); +} + +#[test] +fn encode_config_slice_can_use_inline_buffer() { + let mut buf: [u8; 22] = [0; 22]; + let mut larger_buf: [u8; 24] = [0; 24]; + let mut input: [u8; 16] = [0; 16]; + + let mut rng = rand::rngs::SmallRng::from_entropy(); + for elt in &mut input { + *elt = rng.gen(); + } + + assert_eq!(22, encode_config_slice(&input, STANDARD_NO_PAD, &mut buf)); + let decoded = decode_config(&buf, STANDARD_NO_PAD).unwrap(); + + assert_eq!(decoded, input); + + // let's try it again with padding + + assert_eq!(24, encode_config_slice(&input, STANDARD, &mut larger_buf)); + let decoded = decode_config(&buf, STANDARD).unwrap(); + + assert_eq!(decoded, input); +} + +#[test] +#[should_panic(expected = "index 24 out of range for slice of length 22")] +fn encode_config_slice_panics_when_buffer_too_small() { + let mut buf: [u8; 22] = [0; 22]; + let mut input: [u8; 16] = [0; 16]; + + let mut rng = rand::rngs::SmallRng::from_entropy(); + for elt in &mut input { + *elt = rng.gen(); + } + + encode_config_slice(&input, STANDARD, &mut buf); +} |