diff options
Diffstat (limited to 'third_party/rust/base64-0.10.1')
23 files changed, 5369 insertions, 0 deletions
diff --git a/third_party/rust/base64-0.10.1/.cargo-checksum.json b/third_party/rust/base64-0.10.1/.cargo-checksum.json new file mode 100644 index 0000000000..0f4d24099a --- /dev/null +++ b/third_party/rust/base64-0.10.1/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"e2c92a2cbab69decb49684c88d92e3a3ea1b5a57bbbc9a1ecfb6c0a30c44eaf5","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7","README.md":"3128245c3e486cc551373a478aa1729565b936308d821dd02ba7936750b0cef6","RELEASE-NOTES.md":"bf3b3b676794ca44197800dbbf96ab2b805f468bc6d89a798374e684e8524374","benches/benchmarks.rs":"f5151f52360c2abca0bbf66eb96aa4deb35e0f06f932389b9b146d48cd9bcd3f","examples/make_tables.rs":"709e19bfb613dc890c72a243bac9ad99b2f97cb793006ce712560575e3162969","icon_CLion.svg":"cffa044ba75cb998ee3306991dc4a3755ec2f39ab95ddd4b74bc21988389020f","src/chunked_encoder.rs":"7a8a8f47f0d85064b648ea1a824417a8fb84803e7e63bd3a03d38d856864f50a","src/decode.rs":"431ca48cc959e26b5a648716bf4e00c6a7f598b670ba9a800a90ba98658b413d","src/display.rs":"2ecee9c0f3c2c00a54f70c950c8a97354691721e0b984b6f5c94299bb744f459","src/encode.rs":"932c4b6168fc2060c582d7a6a8a8fd7d900f8203ef0fe36c1c4d197217ee9a7a","src/lib.rs":"b2c15e224e9b16b5c1b1abef5c8622b8c7261300c582f45f0b24da953396bbd3","src/tables.rs":"4f71ead5d026284f056ef4c7cc233802be5a523790726e5785e576387e11d326","src/tests.rs":"25e4dc1b09124f3bc9f3c3efc39492c059a5de7bf3b515410f83823549ce4cd0","src/write/encoder.rs":"9e705fe50a5f6ba6156c2497f168cee239a5a7610855aa2107e4450c992fbdb3","src/write/encoder_tests.rs":"7489331c0d23eb718b94a40115e92c038a5e5c8f0282d735b0697dcbc8a14846","src/write/mod.rs":"124b223f3effcb1d85671a4173a635cdfae480341b032217c77876647344176b","tests/decode.rs":"216007a44c9201a29b67da14df90ff0d0d219187d209cedfdceee060a379975a","tests/encode.rs":"5efb6904c36c6f899a05078e5c9be756fc58af1ee9940edfa8dea1ee53675364","tests/helpers.rs":"a76015e4a4e8f98213bdbaa592cd9574ccdc95a28e1b1f835a2753e09fa6037f","tests/tests.rs":"5583e10bae1f4264af9a0ffb77330d7a710e2c2cf483ce7013bf13f9ad523491"},"package":"0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"}
\ No newline at end of file diff --git a/third_party/rust/base64-0.10.1/Cargo.toml b/third_party/rust/base64-0.10.1/Cargo.toml new file mode 100644 index 0000000000..25725e56aa --- /dev/null +++ b/third_party/rust/base64-0.10.1/Cargo.toml @@ -0,0 +1,36 @@ +# 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] +name = "base64" +version = "0.10.1" +authors = ["Alice Maz <alice@alicemaz.com>", "Marshall Pierce <marshall@mpierce.org>"] +description = "encodes and decodes base64 as bytes or utf8" +documentation = "https://github.com/alicemaz/rust-base64/blob/master/README.md" +readme = "README.md" +keywords = ["base64", "utf8", "encode", "decode"] +categories = ["encoding"] +license = "MIT/Apache-2.0" +repository = "https://github.com/alicemaz/rust-base64" +[profile.bench] +debug = true + +[[bench]] +name = "benchmarks" +harness = false +[dependencies.byteorder] +version = "1.2.6" +[dev-dependencies.criterion] +version = "0.2" + +[dev-dependencies.rand] +version = "0.6.1" diff --git a/third_party/rust/base64-0.10.1/LICENSE-APACHE b/third_party/rust/base64-0.10.1/LICENSE-APACHE new file mode 100644 index 0000000000..16fe87b06e --- /dev/null +++ b/third_party/rust/base64-0.10.1/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-0.10.1/LICENSE-MIT b/third_party/rust/base64-0.10.1/LICENSE-MIT new file mode 100644 index 0000000000..7bc10f80a0 --- /dev/null +++ b/third_party/rust/base64-0.10.1/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-0.10.1/README.md b/third_party/rust/base64-0.10.1/README.md new file mode 100644 index 0000000000..c10d170e3b --- /dev/null +++ b/third_party/rust/base64-0.10.1/README.md @@ -0,0 +1,106 @@ +[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/alicemaz/rust-base64.svg?branch=master)](https://travis-ci.org/alicemaz/rust-base64) [![codecov](https://codecov.io/gh/alicemaz/rust-base64/branch/master/graph/badge.svg)](https://codecov.io/gh/alicemaz/rust-base64) + +<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? + +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.27.2. + +Developing +--- + +Benchmarks are in `benches/`. Running them requires nightly rust, but `rustup` makes it easy: + +``` +rustup run nightly cargo bench +``` + +Decoding is aided by some pre-calculated tables, which are generated by: + +``` +cargo run --example make_tables > src/tables.rs.tmp && mv src/tables.rs.tmp src/tables.rs +``` + +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). + +``` +sudo perf record target/release/deps/benchmarks-* --bench decode_10mib_reuse +``` + +Then analyze the results, again with perf: + +``` +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. + +``` + 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: + +``` +cargo +nightly fuzz run roundtrip +cargo +nightly fuzz run roundtrip_no_pad +cargo +nightly fuzz run roundtrip_random_config -- -max_len=10240 +``` + + +License +--- + +This project is dual-licensed under MIT and Apache 2.0. diff --git a/third_party/rust/base64-0.10.1/RELEASE-NOTES.md b/third_party/rust/base64-0.10.1/RELEASE-NOTES.md new file mode 100644 index 0000000000..5e032eaeac --- /dev/null +++ b/third_party/rust/base64-0.10.1/RELEASE-NOTES.md @@ -0,0 +1,82 @@ +# Next + +- TBD + +# 0.10.1 + +- Minimum rust version 1.27.2 +- Fix bug in streaming encoding ([#90](https://github.com/alicemaz/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-0.10.1/benches/benchmarks.rs b/third_party/rust/base64-0.10.1/benches/benchmarks.rs new file mode 100644 index 0000000000..e6ae3d205c --- /dev/null +++ b/third_party/rust/base64-0.10.1/benches/benchmarks.rs @@ -0,0 +1,160 @@ +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::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_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 u32)) + .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 u32)) + .with_function("decode_reuse_buf", do_decode_bench_reuse_buf) + .with_function("decode_slice", do_decode_bench_slice) +} + +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-0.10.1/examples/make_tables.rs b/third_party/rust/base64-0.10.1/examples/make_tables.rs new file mode 100644 index 0000000000..9f170c047a --- /dev/null +++ b/third_party/rust/base64-0.10.1/examples/make_tables.rs @@ -0,0 +1,116 @@ +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) + // _s + .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); +} + +fn print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) { + println!("#[cfg_attr(rustfmt, rustfmt_skip)]"); + println!( + "{:width$}pub const {}: &'static [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!("#[cfg_attr(rustfmt, rustfmt_skip)]"); + println!( + "{:width$}pub const {}: &'static [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-0.10.1/icon_CLion.svg b/third_party/rust/base64-0.10.1/icon_CLion.svg new file mode 100644 index 0000000000..e9edb0445e --- /dev/null +++ b/third_party/rust/base64-0.10.1/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-0.10.1/src/chunked_encoder.rs b/third_party/rust/base64-0.10.1/src/chunked_encoder.rs new file mode 100644 index 0000000000..db03ba5213 --- /dev/null +++ b/third_party/rust/base64-0.10.1/src/chunked_encoder.rs @@ -0,0 +1,240 @@ +use encode::{add_padding, encode_to_slice}; +use std::{cmp, str}; +use Config; + +/// 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 +pub(crate) struct StringSink<'a> { + string: &'a mut String, +} + +impl<'a> StringSink<'a> { + pub(crate) fn new(s: &mut String) -> StringSink { + StringSink { string: s } + } +} + +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 { + extern crate rand; + + use super::*; + use tests::random_config; + use *; + + use self::rand::distributions::{Distribution, Uniform}; + use self::rand::{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(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-0.10.1/src/decode.rs b/third_party/rust/base64-0.10.1/src/decode.rs new file mode 100644 index 0000000000..2dfa3ad038 --- /dev/null +++ b/third_party/rust/base64-0.10.1/src/decode.rs @@ -0,0 +1,838 @@ +use byteorder::{BigEndian, ByteOrder}; +use {tables, Config, STANDARD}; + +use std::{error, fmt, str}; + +// 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) + } + } + } +} + +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<&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); +///} +///``` +pub fn decode<T: ?Sized + 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); +///} +///``` +pub fn decode_config<T: ?Sized + 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); +///} +///``` +pub fn decode_config_buf<T: ?Sized + 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: ?Sized + 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) +} + +/// 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; + + BigEndian::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 { + extern crate rand; + + use super::*; + use encode::encode_config_buf; + use tests::{assert_encode_sanity, random_config}; + + use self::rand::distributions::{Distribution, Uniform}; + use self::rand::{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/alicemaz/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) + ), + } + } + } + } +} diff --git a/third_party/rust/base64-0.10.1/src/display.rs b/third_party/rust/base64-0.10.1/src/display.rs new file mode 100644 index 0000000000..3d76829466 --- /dev/null +++ b/third_party/rust/base64-0.10.1/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 std::fmt::{Display, Formatter}; +use std::{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-0.10.1/src/encode.rs b/third_party/rust/base64-0.10.1/src/encode.rs new file mode 100644 index 0000000000..fb65f1e912 --- /dev/null +++ b/third_party/rust/base64-0.10.1/src/encode.rs @@ -0,0 +1,659 @@ +use byteorder::{BigEndian, ByteOrder}; +use {Config, STANDARD}; + +///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); +///} +///``` +pub fn encode<T: ?Sized + 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); +///} +///``` +pub fn encode_config<T: ?Sized + 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"), + }; + + let encoded_len = encode_config_slice(input.as_ref(), config, &mut buf[..]); + debug_assert_eq!(encoded_len, buf.len()); + + 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); +///} +///``` +pub fn encode_config_buf<T: ?Sized + 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: ?Sized + 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); +} + +/// 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 = BigEndian::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 = BigEndian::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 = BigEndian::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 = BigEndian::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 { + extern crate rand; + + use super::*; + use decode::decode_config_buf; + use tests::{assert_encode_sanity, random_config}; + use {Config, STANDARD, URL_SAFE_NO_PAD}; + + use self::rand::distributions::{Distribution, Uniform}; + use self::rand::{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()); + } + +} diff --git a/third_party/rust/base64-0.10.1/src/lib.rs b/third_party/rust/base64-0.10.1/src/lib.rs new file mode 100644 index 0000000000..651cc0e479 --- /dev/null +++ b/third_party/rust/base64-0.10.1/src/lib.rs @@ -0,0 +1,180 @@ +//! # 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. +//! +//! # 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(cast_lossless))] +#![deny( + missing_docs, + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_import_braces, + unused_results, + variant_size_differences, + warnings, + unsafe_code +)] + +extern crate byteorder; + +mod chunked_encoder; +pub mod display; +mod tables; +pub mod write; + +mod encode; +pub use encode::{encode, encode_config, encode_config_buf, encode_config_slice}; + +mod decode; +pub use decode::{decode, decode_config, decode_config_buf, 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, +} + +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, + } + } + + 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, + } + } +} + +/// 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, +}; diff --git a/third_party/rust/base64-0.10.1/src/tables.rs b/third_party/rust/base64-0.10.1/src/tables.rs new file mode 100644 index 0000000000..a1466c3e90 --- /dev/null +++ b/third_party/rust/base64-0.10.1/src/tables.rs @@ -0,0 +1,979 @@ +pub const INVALID_VALUE: u8 = 255; +#[cfg_attr(rustfmt, 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) +]; +#[cfg_attr(rustfmt, 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) +]; +#[cfg_attr(rustfmt, 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) +]; +#[cfg_attr(rustfmt, 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) +]; +#[cfg_attr(rustfmt, 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) +]; +#[cfg_attr(rustfmt, 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) +]; diff --git a/third_party/rust/base64-0.10.1/src/tests.rs b/third_party/rust/base64-0.10.1/src/tests.rs new file mode 100644 index 0000000000..a1fb52a227 --- /dev/null +++ b/third_party/rust/base64-0.10.1/src/tests.rs @@ -0,0 +1,80 @@ +extern crate rand; + +use encode::encoded_size; +use *; + +use std::str; + +use self::rand::distributions::{Distribution, Uniform}; +use self::rand::{FromEntropy, Rng}; +use self::rand::seq::SliceRandom; + +#[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, + ]; + let charset = *CHARSETS.choose(rng).unwrap(); + + Config::new(charset, rng.gen()) +} diff --git a/third_party/rust/base64-0.10.1/src/write/encoder.rs b/third_party/rust/base64-0.10.1/src/write/encoder.rs new file mode 100644 index 0000000000..23de075eba --- /dev/null +++ b/third_party/rust/base64-0.10.1/src/write/encoder.rs @@ -0,0 +1,355 @@ +use encode::encode_to_slice; +use std::io::{ErrorKind, Result, Write}; +use std::{cmp, fmt}; +use {encode_config_slice, Config}; + +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; + + return 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-0.10.1/src/write/encoder_tests.rs b/third_party/rust/base64-0.10.1/src/write/encoder_tests.rs new file mode 100644 index 0000000000..681235b5fd --- /dev/null +++ b/third_party/rust/base64-0.10.1/src/write/encoder_tests.rs @@ -0,0 +1,564 @@ +extern crate rand; + +use super::EncoderWriter; +use tests::random_config; +use {encode_config, encode_config_buf, STANDARD_NO_PAD, URL_SAFE}; + +use std::io::{Cursor, Write}; +use std::{cmp, io, str}; + +use self::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() + } +}
\ No newline at end of file diff --git a/third_party/rust/base64-0.10.1/src/write/mod.rs b/third_party/rust/base64-0.10.1/src/write/mod.rs new file mode 100644 index 0000000000..f8ed70768c --- /dev/null +++ b/third_party/rust/base64-0.10.1/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-0.10.1/tests/decode.rs b/third_party/rust/base64-0.10.1/tests/decode.rs new file mode 100644 index 0000000000..eafa44173c --- /dev/null +++ b/third_party/rust/base64-0.10.1/tests/decode.rs @@ -0,0 +1,310 @@ +extern crate base64; + +use base64::*; + +mod helpers; + +use 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-0.10.1/tests/encode.rs b/third_party/rust/base64-0.10.1/tests/encode.rs new file mode 100644 index 0000000000..0004be00fe --- /dev/null +++ b/third_party/rust/base64-0.10.1/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-0.10.1/tests/helpers.rs b/third_party/rust/base64-0.10.1/tests/helpers.rs new file mode 100644 index 0000000000..5144988448 --- /dev/null +++ b/third_party/rust/base64-0.10.1/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-0.10.1/tests/tests.rs b/third_party/rust/base64-0.10.1/tests/tests.rs new file mode 100644 index 0000000000..699cb76299 --- /dev/null +++ b/third_party/rust/base64-0.10.1/tests/tests.rs @@ -0,0 +1,194 @@ +extern crate base64; +extern crate rand; + +use rand::{FromEntropy, Rng}; + +use base64::*; + +mod helpers; +use 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); +} |