Bug 1414254 - Vendor Rust dependencies r=jgraham

MozReview-Commit-ID: CnT3DLl2KBR

--HG--
rename : third_party/rust/base64-0.5.2/LICENSE-APACHE => third_party/rust/cc/LICENSE-APACHE
rename : third_party/rust/gcc-0.3.42/LICENSE-MIT => third_party/rust/cc/LICENSE-MIT
rename : third_party/rust/gcc-0.3.42/src/bin/gcc-shim.rs => third_party/rust/cc/src/bin/gcc-shim.rs
rename : third_party/rust/gcc-0.3.42/src/registry.rs => third_party/rust/cc/src/registry.rs
rename : third_party/rust/clap/.cargo-checksum.json => third_party/rust/clap-2.25.0/.cargo-checksum.json
rename : third_party/rust/clap-2.24.2/.clog.toml => third_party/rust/clap-2.25.0/.clog.toml
rename : third_party/rust/clap-2.24.2/.github/CONTRIBUTING.md => third_party/rust/clap-2.25.0/.github/CONTRIBUTING.md
rename : third_party/rust/clap-2.24.2/.github/ISSUE_TEMPLATE.md => third_party/rust/clap-2.25.0/.github/ISSUE_TEMPLATE.md
rename : third_party/rust/clap-2.24.2/.travis.yml => third_party/rust/clap-2.25.0/.travis.yml
rename : third_party/rust/clap/CHANGELOG.md => third_party/rust/clap-2.25.0/CHANGELOG.md
rename : third_party/rust/clap-2.24.2/CONTRIBUTORS.md => third_party/rust/clap-2.25.0/CONTRIBUTORS.md
rename : third_party/rust/clap/Cargo.toml => third_party/rust/clap-2.25.0/Cargo.toml
rename : third_party/rust/clap-2.24.2/LICENSE-MIT => third_party/rust/clap-2.25.0/LICENSE-MIT
rename : third_party/rust/clap/README.md => third_party/rust/clap-2.25.0/README.md
rename : third_party/rust/clap-2.24.2/index.html => third_party/rust/clap-2.25.0/index.html
rename : third_party/rust/clap-2.24.2/justfile => third_party/rust/clap-2.25.0/justfile
rename : third_party/rust/clap-2.24.2/rustfmt.toml => third_party/rust/clap-2.25.0/rustfmt.toml
rename : third_party/rust/clap/src/app/help.rs => third_party/rust/clap-2.25.0/src/app/help.rs
rename : third_party/rust/clap/src/app/macros.rs => third_party/rust/clap-2.25.0/src/app/macros.rs
rename : third_party/rust/clap-2.24.2/src/app/meta.rs => third_party/rust/clap-2.25.0/src/app/meta.rs
rename : third_party/rust/clap-2.24.2/src/app/mod.rs => third_party/rust/clap-2.25.0/src/app/mod.rs
rename : third_party/rust/clap/src/app/parser.rs => third_party/rust/clap-2.25.0/src/app/parser.rs
rename : third_party/rust/clap/src/app/settings.rs => third_party/rust/clap-2.25.0/src/app/settings.rs
rename : third_party/rust/clap/src/app/validator.rs => third_party/rust/clap-2.25.0/src/app/validator.rs
rename : third_party/rust/clap-2.24.2/src/args/any_arg.rs => third_party/rust/clap-2.25.0/src/args/any_arg.rs
rename : third_party/rust/clap-2.24.2/src/args/arg.rs => third_party/rust/clap-2.25.0/src/args/arg.rs
rename : third_party/rust/clap-2.24.2/src/args/arg_builder/base.rs => third_party/rust/clap-2.25.0/src/args/arg_builder/base.rs
rename : third_party/rust/clap-2.24.2/src/args/arg_builder/flag.rs => third_party/rust/clap-2.25.0/src/args/arg_builder/flag.rs
rename : third_party/rust/clap-2.24.2/src/args/arg_builder/mod.rs => third_party/rust/clap-2.25.0/src/args/arg_builder/mod.rs
rename : third_party/rust/clap-2.24.2/src/args/arg_builder/option.rs => third_party/rust/clap-2.25.0/src/args/arg_builder/option.rs
rename : third_party/rust/clap/src/args/arg_builder/positional.rs => third_party/rust/clap-2.25.0/src/args/arg_builder/positional.rs
rename : third_party/rust/clap-2.24.2/src/args/arg_builder/switched.rs => third_party/rust/clap-2.25.0/src/args/arg_builder/switched.rs
rename : third_party/rust/clap-2.24.2/src/args/arg_builder/valued.rs => third_party/rust/clap-2.25.0/src/args/arg_builder/valued.rs
rename : third_party/rust/clap-2.24.2/src/args/arg_matcher.rs => third_party/rust/clap-2.25.0/src/args/arg_matcher.rs
rename : third_party/rust/clap-2.24.2/src/args/arg_matches.rs => third_party/rust/clap-2.25.0/src/args/arg_matches.rs
rename : third_party/rust/clap-2.24.2/src/args/matched_arg.rs => third_party/rust/clap-2.25.0/src/args/matched_arg.rs
rename : third_party/rust/clap-2.24.2/src/args/mod.rs => third_party/rust/clap-2.25.0/src/args/mod.rs
rename : third_party/rust/clap-2.24.2/src/args/subcommand.rs => third_party/rust/clap-2.25.0/src/args/subcommand.rs
rename : third_party/rust/clap-2.24.2/src/completions/bash.rs => third_party/rust/clap-2.25.0/src/completions/bash.rs
rename : third_party/rust/clap-2.24.2/src/completions/fish.rs => third_party/rust/clap-2.25.0/src/completions/fish.rs
rename : third_party/rust/clap-2.24.2/src/completions/macros.rs => third_party/rust/clap-2.25.0/src/completions/macros.rs
rename : third_party/rust/clap-2.24.2/src/completions/mod.rs => third_party/rust/clap-2.25.0/src/completions/mod.rs
rename : third_party/rust/clap-2.24.2/src/completions/shell.rs => third_party/rust/clap-2.25.0/src/completions/shell.rs
rename : third_party/rust/clap-2.24.2/src/completions/zsh.rs => third_party/rust/clap-2.25.0/src/completions/zsh.rs
rename : third_party/rust/clap/src/lib.rs => third_party/rust/clap-2.25.0/src/lib.rs
rename : third_party/rust/clap/src/macros.rs => third_party/rust/clap-2.25.0/src/macros.rs
rename : third_party/rust/clap-2.24.2/src/osstringext.rs => third_party/rust/clap-2.25.0/src/osstringext.rs
rename : third_party/rust/clap-2.24.2/src/strext.rs => third_party/rust/clap-2.25.0/src/strext.rs
rename : third_party/rust/clap/src/suggestions.rs => third_party/rust/clap-2.25.0/src/suggestions.rs
rename : third_party/rust/clap-2.24.2/src/usage_parser.rs => third_party/rust/clap-2.25.0/src/usage_parser.rs
rename : third_party/rust/gcc-0.3.42/LICENSE-APACHE => third_party/rust/libc-0.2.30/LICENSE-APACHE
rename : third_party/rust/log-0.3.6/LICENSE-MIT => third_party/rust/libc-0.2.30/LICENSE-MIT
rename : third_party/rust/num-integer/.cargo-checksum.json => third_party/rust/num-integer-0.1.33/.cargo-checksum.json
rename : third_party/rust/num-integer/Cargo.toml => third_party/rust/num-integer-0.1.33/Cargo.toml
rename : third_party/rust/log-0.3.6/LICENSE-APACHE => third_party/rust/num-integer-0.1.33/LICENSE-APACHE
rename : third_party/rust/num-traits-0.1.37/LICENSE-MIT => third_party/rust/num-integer-0.1.33/LICENSE-MIT
rename : third_party/rust/num-integer/src/lib.rs => third_party/rust/num-integer-0.1.33/src/lib.rs
rename : third_party/rust/num-traits/.cargo-checksum.json => third_party/rust/num-traits-0.1.39/.cargo-checksum.json
rename : third_party/rust/num-traits/Cargo.toml => third_party/rust/num-traits-0.1.39/Cargo.toml
rename : third_party/rust/num-traits-0.1.37/LICENSE-APACHE => third_party/rust/num-traits-0.1.39/LICENSE-APACHE
rename : third_party/rust/regex-0.2.1/LICENSE-MIT => third_party/rust/num-traits-0.1.39/LICENSE-MIT
rename : third_party/rust/num-traits-0.1.37/src/float.rs => third_party/rust/num-traits-0.1.39/src/float.rs
rename : third_party/rust/num-traits-0.1.37/src/int.rs => third_party/rust/num-traits-0.1.39/src/int.rs
rename : third_party/rust/num-traits-0.1.37/src/ops/mod.rs => third_party/rust/num-traits-0.1.39/src/ops/mod.rs
rename : third_party/rust/num-traits-0.1.37/src/ops/saturating.rs => third_party/rust/num-traits-0.1.39/src/ops/saturating.rs
rename : third_party/rust/num-traits-0.1.37/src/pow.rs => third_party/rust/num-traits-0.1.39/src/pow.rs
rename : third_party/rust/num_cpus-1.2.1/.travis.yml => third_party/rust/num_cpus-1.6.2/.travis.yml
rename : third_party/rust/num_cpus-1.2.1/LICENSE-APACHE => third_party/rust/num_cpus-1.6.2/LICENSE-APACHE
rename : third_party/rust/num_cpus-1.2.1/LICENSE-MIT => third_party/rust/num_cpus-1.6.2/LICENSE-MIT
rename : third_party/rust/textwrap/.appveyor.yml => third_party/rust/textwrap-0.6.0/.appveyor.yml
rename : third_party/rust/textwrap/.cargo-checksum.json => third_party/rust/textwrap-0.6.0/.cargo-checksum.json
rename : third_party/rust/textwrap/.travis.yml => third_party/rust/textwrap-0.6.0/.travis.yml
rename : third_party/rust/textwrap/Cargo.toml => third_party/rust/textwrap-0.6.0/Cargo.toml
rename : third_party/rust/textwrap/README.md => third_party/rust/textwrap-0.6.0/README.md
rename : third_party/rust/textwrap/benches/linear.rs => third_party/rust/textwrap-0.6.0/benches/linear.rs
rename : third_party/rust/textwrap/examples/layout.rs => third_party/rust/textwrap-0.6.0/examples/layout.rs
rename : third_party/rust/textwrap/examples/termwidth.rs => third_party/rust/textwrap-0.6.0/examples/termwidth.rs
rename : third_party/rust/textwrap/src/lib.rs => third_party/rust/textwrap-0.6.0/src/lib.rs
extra : rebase_source : 75ee4cb067c477d9bd7921594fd0138355392635
This commit is contained in:
Andreas Tolfsen 2017-11-03 13:39:05 +00:00
parent cc0d8c0695
commit 318d3094c5
653 changed files with 51606 additions and 76695 deletions

View File

@ -1 +0,0 @@
{"files":{".travis.yml":"e17babe5ba0bdd19ec59a37b4a099fd4313bff58be63a2ff506075f9a97dc172","COPYING":"01c266bced4a434da0051174d6bee16a4c82cf634e2679b6155d40d75012390f","Cargo.toml":"367c53caf576e1c811c77b5234f4d00ee23f5b1052d5e11bdc0c3153a8d9ae82","LICENSE-MIT":"0f96a83840e146e43c0ec96a22ec1f392e0680e6c1226e6f3ba87e0740af850f","Makefile":"a45a128685a2ae7d4fa39d310786674417ee113055ef290a11f88002285865fc","README.md":"9bc60d2cec222b50f87c85cf9475349bb228a36f89796c5d6481c52560ddde3a","UNLICENSE":"7e12e5df4bae12cb21581ba157ced20e1986a0508dd10d0e8a4ab9a4cf94e85c","benches/bench.rs":"acf4844efadeafc7bc396c2b16f2a184e140b6c17d1084dbaf454196de2090cd","benches/random.txt":"9386fb3efedc7ffbd09fb49088347f1056bc2d90a861009fa2f804cdb714efcb","ctags.rust":"3d128d3cc59f702e68953ba2fe6c3f46bc6991fc575308db060482d5da0c79f3","examples/dict-search.rs":"30eb44b1a0b599507db4c23a90f74199faabc64a8ae1d603ecdf3bba7428eb1e","session.vim":"95cb1d7caf0ff7fbe76ec911988d908ddd883381c925ba64b537695bc9f021c4","src/autiter.rs":"dc8817af24825c356842c814d771868fb07b6965addf4780e8b9dea9718344a0","src/full.rs":"b83a9c8ff3ef611c316b68650915df2d7f361a49b59dab103dc2c5476f2d8303","src/lib.rs":"68bf2ed02d58bebee6f7f7579038f1e4b60a2c4acc334263cb837bcbe15ffe94","src/main.rs":"fc867cb5f0b02d0f49ecab06b72c05a247cbcf3bf9228c235de8e787bda7bef5"},"package":"0638fd549427caa90c499814196d1b9e3725eb4d15d7339d6de073a680ed0ca2"}

View File

@ -1,13 +0,0 @@
language: rust
rust:
- 1.12.0
- stable
- beta
- nightly
script:
- cargo build --verbose
- cargo test --verbose
- cargo doc
- if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
cargo bench --verbose;
fi

View File

@ -1,3 +0,0 @@
This project is dual-licensed under the Unlicense and MIT licenses.
You may use this code under the terms of either license.

View File

@ -1,47 +0,0 @@
[package]
name = "aho-corasick"
version = "0.6.2" #:version
authors = ["Andrew Gallant <jamslam@gmail.com>"]
description = "Fast multiple substring searching with finite state machines."
documentation = "http://burntsushi.net/rustdoc/aho_corasick/"
homepage = "https://github.com/BurntSushi/aho-corasick"
repository = "https://github.com/BurntSushi/aho-corasick"
readme = "README.md"
keywords = ["string", "search", "text", "aho", "corasick"]
license = "Unlicense/MIT"
exclude = ["benches/sherlock.txt"]
[lib]
name = "aho_corasick"
[[bin]]
name = "aho-corasick-dot"
test = false
doc = false
bench = false
[dependencies]
memchr = "1"
[dev-dependencies]
csv = "0.15"
docopt = "0.7"
memmap = "0.5"
quickcheck = { version = "0.4", default-features = false }
rand = "0.3"
rustc-serialize = "0.3"
[[bench]]
name = "bench"
path = "benches/bench.rs"
test = false
bench = true
[profile.test]
debug = true
[profile.bench]
debug = true
[profile.release]
debug = true

View File

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Andrew Gallant
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.

View File

@ -1,14 +0,0 @@
all:
echo Nothing to do...
ctags:
ctags --recurse --options=ctags.rust --languages=Rust
docs:
cargo doc
in-dir ./target/doc fix-perms
rscp ./target/doc/* gopher:~/www/burntsushi.net/rustdoc/
push:
git push origin master
git push github master

View File

@ -1,55 +0,0 @@
This crate provides an implementation of the
[Aho-Corasick](http://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_string_matching_algorithm)
algorithm. Its intended use case is for fast substring matching, particularly
when matching multiple substrings in a search text. This is achieved by
compiling the substrings into a finite state machine.
This implementation provides optimal algorithmic time complexity. Construction
of the finite state machine is `O(p)` where `p` is the length of the substrings
concatenated. Matching against search text is `O(n + p + m)`, where `n` is
the length of the search text and `m` is the number of matches.
[![Build status](https://api.travis-ci.org/BurntSushi/aho-corasick.png)](https://travis-ci.org/BurntSushi/aho-corasick)
[![](http://meritbadge.herokuapp.com/aho-corasick)](https://crates.io/crates/aho-corasick)
Dual-licensed under MIT or the [UNLICENSE](http://unlicense.org).
### Documentation
[http://burntsushi.net/rustdoc/aho_corasick/](http://burntsushi.net/rustdoc/aho_corasick/).
### Example
The documentation contains several examples, and there is a more complete
example as a full program in `examples/dict-search.rs`.
Here is a quick example showing simple substring matching:
```rust
use aho_corasick::{Automaton, AcAutomaton, Match};
let aut = AcAutomaton::new(vec!["apple", "maple"]);
let mut it = aut.find("I like maple apples.");
assert_eq!(it.next(), Some(Match {
pati: 1,
start: 7,
end: 12,
}));
assert_eq!(it.next(), Some(Match {
pati: 0,
start: 13,
end: 18,
}));
assert_eq!(it.next(), None);
```
### Alternatives
Aho-Corasick is useful for matching multiple substrings against many long
strings. If your long string is fixed, then you might consider building a
[suffix array](https://github.com/BurntSushi/suffix)
of the search text (which takes `O(n)` time). Matches can then be found in
`O(plogn)` time.

View File

@ -1,24 +0,0 @@
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
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 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.
For more information, please refer to <http://unlicense.org/>

View File

@ -1,339 +0,0 @@
#![feature(test)]
extern crate aho_corasick;
extern crate test;
use std::iter;
use aho_corasick::{Automaton, AcAutomaton, Transitions};
use test::Bencher;
const HAYSTACK_RANDOM: &'static str = include_str!("random.txt");
const HAYSTACK_SHERLOCK: &'static str = include_str!("sherlock.txt");
fn bench_aut_no_match<P: AsRef<[u8]>, T: Transitions>(
b: &mut Bencher,
aut: AcAutomaton<P, T>,
haystack: &str,
) {
b.bytes = haystack.len() as u64;
b.iter(|| assert!(aut.find(haystack).next().is_none()));
}
fn bench_box_aut_no_match<P: AsRef<[u8]>, T: Transitions>(
b: &mut Bencher,
aut: AcAutomaton<P, T>,
haystack: &str,
) {
b.bytes = haystack.len() as u64;
let aut: &Automaton<P> = &aut;
b.iter(|| assert!(Automaton::find(&aut, haystack).next().is_none()));
}
fn bench_full_aut_no_match<P: AsRef<[u8]>, T: Transitions>(
b: &mut Bencher,
aut: AcAutomaton<P, T>,
haystack: &str,
) {
let aut = aut.into_full();
b.bytes = haystack.len() as u64;
b.iter(|| assert!(aut.find(haystack).next().is_none()));
}
fn bench_full_aut_overlapping_no_match<P: AsRef<[u8]>, T: Transitions>(
b: &mut Bencher,
aut: AcAutomaton<P, T>,
haystack: &str,
) {
let aut = aut.into_full();
b.bytes = haystack.len() as u64;
b.iter(|| assert!(aut.find_overlapping(haystack).count() == 0));
}
fn bench_naive_no_match<S>(b: &mut Bencher, needles: Vec<S>, haystack: &str)
where S: Into<String> {
b.bytes = haystack.len() as u64;
let needles: Vec<String> = needles.into_iter().map(Into::into).collect();
b.iter(|| assert!(!naive_find(&needles, haystack)));
}
fn haystack_same(letter: char) -> String {
iter::repeat(letter).take(10000).collect()
}
macro_rules! aut_benches {
($prefix:ident, $aut:expr, $bench:expr) => {
mod $prefix {
#![allow(unused_imports)]
use aho_corasick::{Automaton, AcAutomaton, Sparse};
use test::Bencher;
use super::{
HAYSTACK_RANDOM, haystack_same,
bench_aut_no_match, bench_box_aut_no_match,
bench_full_aut_no_match, bench_full_aut_overlapping_no_match,
};
#[bench]
fn ac_one_byte(b: &mut Bencher) {
let aut = $aut(vec!["a"]);
$bench(b, aut, &haystack_same('z'));
}
#[bench]
fn ac_one_prefix_byte_no_match(b: &mut Bencher) {
let aut = $aut(vec!["zbc"]);
$bench(b, aut, &haystack_same('y'));
}
#[bench]
fn ac_one_prefix_byte_every_match(b: &mut Bencher) {
// We lose the benefit of `memchr` because the first byte matches
// in every position in the haystack.
let aut = $aut(vec!["zbc"]);
$bench(b, aut, &haystack_same('z'));
}
#[bench]
fn ac_one_prefix_byte_random(b: &mut Bencher) {
let aut = $aut(vec!["zbc\x00"]);
$bench(b, aut, HAYSTACK_RANDOM);
}
#[bench]
fn ac_two_bytes(b: &mut Bencher) {
let aut = $aut(vec!["a", "b"]);
$bench(b, aut, &haystack_same('z'));
}
#[bench]
fn ac_two_diff_prefix(b: &mut Bencher) {
let aut = $aut(vec!["abcdef", "bmnopq"]);
$bench(b, aut, &haystack_same('z'));
}
#[bench]
fn ac_two_one_prefix_byte_every_match(b: &mut Bencher) {
let aut = $aut(vec!["zbcdef", "zmnopq"]);
$bench(b, aut, &haystack_same('z'));
}
#[bench]
fn ac_two_one_prefix_byte_no_match(b: &mut Bencher) {
let aut = $aut(vec!["zbcdef", "zmnopq"]);
$bench(b, aut, &haystack_same('y'));
}
#[bench]
fn ac_two_one_prefix_byte_random(b: &mut Bencher) {
let aut = $aut(vec!["zbcdef\x00", "zmnopq\x00"]);
$bench(b, aut, HAYSTACK_RANDOM);
}
#[bench]
fn ac_ten_bytes(b: &mut Bencher) {
let aut = $aut(vec!["a", "b", "c", "d", "e",
"f", "g", "h", "i", "j"]);
$bench(b, aut, &haystack_same('z'));
}
#[bench]
fn ac_ten_diff_prefix(b: &mut Bencher) {
let aut = $aut(vec!["abcdef", "bbcdef", "cbcdef", "dbcdef",
"ebcdef", "fbcdef", "gbcdef", "hbcdef",
"ibcdef", "jbcdef"]);
$bench(b, aut, &haystack_same('z'));
}
#[bench]
fn ac_ten_one_prefix_byte_every_match(b: &mut Bencher) {
let aut = $aut(vec!["zacdef", "zbcdef", "zccdef", "zdcdef",
"zecdef", "zfcdef", "zgcdef", "zhcdef",
"zicdef", "zjcdef"]);
$bench(b, aut, &haystack_same('z'));
}
#[bench]
fn ac_ten_one_prefix_byte_no_match(b: &mut Bencher) {
let aut = $aut(vec!["zacdef", "zbcdef", "zccdef", "zdcdef",
"zecdef", "zfcdef", "zgcdef", "zhcdef",
"zicdef", "zjcdef"]);
$bench(b, aut, &haystack_same('y'));
}
#[bench]
fn ac_ten_one_prefix_byte_random(b: &mut Bencher) {
let aut = $aut(vec!["zacdef\x00", "zbcdef\x00", "zccdef\x00",
"zdcdef\x00", "zecdef\x00", "zfcdef\x00",
"zgcdef\x00", "zhcdef\x00", "zicdef\x00",
"zjcdef\x00"]);
$bench(b, aut, HAYSTACK_RANDOM);
}
}
}
}
aut_benches!(dense, AcAutomaton::new, bench_aut_no_match);
aut_benches!(dense_boxed, AcAutomaton::new, bench_box_aut_no_match);
aut_benches!(sparse, AcAutomaton::<&str, Sparse>::with_transitions,
bench_aut_no_match);
aut_benches!(full, AcAutomaton::new, bench_full_aut_no_match);
aut_benches!(full_overlap, AcAutomaton::new, bench_full_aut_overlapping_no_match);
// A naive multi-pattern search.
// We use this to benchmark *throughput*, so it should never match anything.
fn naive_find(needles: &[String], haystack: &str) -> bool {
for hi in 0..haystack.len() {
let rest = &haystack.as_bytes()[hi..];
for needle in needles {
let needle = needle.as_bytes();
if needle.len() > rest.len() {
continue;
}
if needle == &rest[..needle.len()] {
// should never happen in throughput benchmarks.
return true;
}
}
}
false
}
#[bench]
fn naive_one_byte(b: &mut Bencher) {
bench_naive_no_match(b, vec!["a"], &haystack_same('z'));
}
#[bench]
fn naive_one_prefix_byte_no_match(b: &mut Bencher) {
bench_naive_no_match(b, vec!["zbc"], &haystack_same('y'));
}
#[bench]
fn naive_one_prefix_byte_every_match(b: &mut Bencher) {
bench_naive_no_match(b, vec!["zbc"], &haystack_same('z'));
}
#[bench]
fn naive_one_prefix_byte_random(b: &mut Bencher) {
bench_naive_no_match(b, vec!["zbc\x00"], HAYSTACK_RANDOM);
}
#[bench]
fn naive_two_bytes(b: &mut Bencher) {
bench_naive_no_match(b, vec!["a", "b"], &haystack_same('z'));
}
#[bench]
fn naive_two_diff_prefix(b: &mut Bencher) {
bench_naive_no_match(b, vec!["abcdef", "bmnopq"], &haystack_same('z'));
}
#[bench]
fn naive_two_one_prefix_byte_every_match(b: &mut Bencher) {
bench_naive_no_match(b, vec!["zbcdef", "zmnopq"], &haystack_same('z'));
}
#[bench]
fn naive_two_one_prefix_byte_no_match(b: &mut Bencher) {
bench_naive_no_match(b, vec!["zbcdef", "zmnopq"], &haystack_same('y'));
}
#[bench]
fn naive_two_one_prefix_byte_random(b: &mut Bencher) {
bench_naive_no_match(b, vec!["zbcdef\x00", "zmnopq\x00"], HAYSTACK_RANDOM);
}
#[bench]
fn naive_ten_bytes(b: &mut Bencher) {
let needles = vec!["a", "b", "c", "d", "e",
"f", "g", "h", "i", "j"];
bench_naive_no_match(b, needles, &haystack_same('z'));
}
#[bench]
fn naive_ten_diff_prefix(b: &mut Bencher) {
let needles = vec!["abcdef", "bbcdef", "cbcdef", "dbcdef",
"ebcdef", "fbcdef", "gbcdef", "hbcdef",
"ibcdef", "jbcdef"];
bench_naive_no_match(b, needles, &haystack_same('z'));
}
#[bench]
fn naive_ten_one_prefix_byte_every_match(b: &mut Bencher) {
let needles = vec!["zacdef", "zbcdef", "zccdef", "zdcdef",
"zecdef", "zfcdef", "zgcdef", "zhcdef",
"zicdef", "zjcdef"];
bench_naive_no_match(b, needles, &haystack_same('z'));
}
#[bench]
fn naive_ten_one_prefix_byte_no_match(b: &mut Bencher) {
let needles = vec!["zacdef", "zbcdef", "zccdef", "zdcdef",
"zecdef", "zfcdef", "zgcdef", "zhcdef",
"zicdef", "zjcdef"];
bench_naive_no_match(b, needles, &haystack_same('y'));
}
#[bench]
fn naive_ten_one_prefix_byte_random(b: &mut Bencher) {
let needles = vec!["zacdef\x00", "zbcdef\x00", "zccdef\x00",
"zdcdef\x00", "zecdef\x00", "zfcdef\x00",
"zgcdef\x00", "zhcdef\x00", "zicdef\x00",
"zjcdef\x00"];
bench_naive_no_match(b, needles, HAYSTACK_RANDOM);
}
// The organization above is just awful. Let's start over...
mod sherlock {
use aho_corasick::{Automaton, AcAutomaton};
use test::Bencher;
use super::HAYSTACK_SHERLOCK;
macro_rules! sherlock {
($name:ident, $count:expr, $pats:expr) => {
#[bench]
fn $name(b: &mut Bencher) {
let haystack = HAYSTACK_SHERLOCK;
let aut = AcAutomaton::new($pats).into_full();
b.bytes = haystack.len() as u64;
b.iter(|| assert_eq!($count, aut.find(haystack).count()));
}
}
}
sherlock!(name_alt1, 158, vec!["Sherlock", "Street"]);
sherlock!(name_alt2, 558, vec!["Sherlock", "Holmes"]);
sherlock!(name_alt3, 740, vec![
"Sherlock", "Holmes", "Watson", "Irene", "Adler", "John", "Baker",
]);
sherlock!(name_alt3_nocase, 1764, vec![
"ADL", "ADl", "AdL", "Adl", "BAK", "BAk", "BA", "BaK", "Bak", "Ba",
"HOL", "HOl", "HoL", "Hol", "IRE", "IRe", "IrE", "Ire", "JOH", "JOh",
"JoH", "Joh", "SHE", "SHe", "ShE", "She", "WAT", "WAt", "WaT", "Wat",
"aDL", "aDl", "adL", "adl", "bAK", "bAk", "bA", "baK", "bak", "ba",
"hOL", "hOl", "hoL", "hol", "iRE", "iRe", "irE", "ire", "jOH", "jOh",
"joH", "joh", "sHE", "sHe", "shE", "she", "wAT", "wAt", "waT", "wat",
"ſHE", "ſHe", "ſhE", "ſhe",
]);
sherlock!(name_alt4, 582, vec!["Sher", "Hol"]);
sherlock!(name_alt4_nocase, 1307, vec![
"HOL", "HOl", "HoL", "Hol", "SHE", "SHe", "ShE", "She", "hOL", "hOl",
"hoL", "hol", "sHE", "sHe", "shE", "she", "ſHE", "ſHe", "ſhE", "ſhe",
]);
sherlock!(name_alt5, 639, vec!["Sherlock", "Holmes", "Watson"]);
sherlock!(name_alt5_nocase, 1442, vec![
"HOL", "HOl", "HoL", "Hol", "SHE", "SHe", "ShE", "She", "WAT", "WAt",
"WaT", "Wat", "hOL", "hOl", "hoL", "hol", "sHE", "sHe", "shE", "she",
"wAT", "wAt", "waT", "wat", "ſHE", "ſHe", "ſhE", "ſhe",
]);
}

View File

@ -1,513 +0,0 @@
mnxnsynfvuugtbxsxbfxwreuspglnplefzwsp
tacfqcwnmodnmgnyiuvqoco
z
qjuozfkexn
zoaxzncje
sldhqtmgxzyurfyzwazmmu
bbeuv
mzsrihycwcb
xzfqozfmlnpmrzpxxxytqs
xrg
mcplby
nmslhfgjowhzfxsvyddydnsyehdskbydbjksqtpet
indvfw
bvjvvw
pddufodyqtyixbndtumndyz
xjjhtuvmsxhuwqulqtjhqrdqrmtbcphvyuqllocrnkpfv
zemshhz
wss
xewlrxfmgxnwgphcgefa
mbgsgbzrtthxweimcqzcaaheurdmd
osqefupespvh
z
tvvlakwzwjbrgjzfgubsmmonav
pjdskxcfgapsm
zqktqgkrcdrlskx
zwwfebhguskho
zlvvw
czwm
gojnpmboehlsazbexjjnuscqftrfufngygjdxcydib
d
afigycivicnknfxl
ljuwuopctiftfwctxecwipjnljyef
jonwbkodomzhqvlf
jdkizhognqsdogunwedjsmsdzho
zxvni
oynfjf
muvokjuqz
azuwrwtuxzfopwrcex
ixrjinlvxjmn
blaegnmbhsgsbmebwazaeguugtkowexgnqtbfkldadddv
tzabyoftyov
ctbtqbzscxzviuvcigwuwusrdro
ljynr
gnnnyyxslrhsbj
hhzlw
hijalf
rxlfqk
mhaofforwznvmcgplinludpgkucpa
gvvxsqqfmu
xxqhoyosixjfhjuxpv
faadjpvamjekreepizurntvwdynozfawsfawyms
lcbutr
aqyxvpozkjrecrkl
lfmochahrr
ptqyomjlwo
vcmslulznx
lmlsskcihrmxauztuarydlp
beiqsrfnmvmlmybmwpektjbikvpggthpabqsgmjhnthvysuhwbigillugjsp
dfsuegseffwcsnvsrqedytblbpzbfeyfsq
kypvqctrkuds
ylqeduokzgdqaxelhftxnxbidu
bprzyayfopxdsmfhhfqowa
ymiutdtlfaaxpbtaeslv
ggago
owpbicekdeykzfgcbgzobdvvrtetvcv
xsrlgingstiez
gyncqvq
xasohmeiwyscpehctmzmsnjklg
xsudghakxlw
dzqlfptjogzpkvwuticcyugnyopypuqqc
wlxshxbhdvuherumoppcc
znyaptivzncvkpeyeipynqefjxjjcsgfqbnezeebtowdrbjaqjlbxwvyikrmxjwoxngqgvfpbniftnmszuxg
umwpwwyvufy
pallkjtnrmtauqxauewgygwkjjwebbkabhtxticxmxfujpxlrpzlrozfslkzfdsswlmmsbdgjwmjnummk
dhsxylejzityahtqqzmohrpzjprrsraztpnuagtyzfjdekthvdogfidksrdppr
ybc
fyukknoqfnkllkwflwempjijxgo
dltvlau
rhvrvlwsribfctuzodfqkdczfzxnetqqzflnhiyl
goxmcasmq
wljbhwkpahdotqhhrbhqzijv
lszewkgdmkezvgmbmllhpksdkoiwgkvqjmurshrptlctqsosuurndcuzjfwherotv
dudxxihygxblhgchbgzyzffb
eht
fvwxvqoltdcsd
rkuig
e
axhsacsmnicugul
rubtdlhjqndxdzzwfnkuzy
swxteuyxxsktkjgv
hzwwodlqaq
vxgecev
qnwla
vdxjuzpyoqhpmuunyffptopmeauhycs
dkzo
awrfzatzohslgvqlaezepmli
qgxatixvpkkhvkumbwmwcagtgyfljdok
amdnzstpvcqj
xsrvwvhjirzfgkessve
qezwbfltfbikbmoasvoflozsjhrljnszqiciuqmflrlqowwkoevuumh
babskcvavmtvsxqsewirucwzajjcfcqwsydydqo
ywfurpsl
edacsjjkjjewkxfoh
dcgkfpcjezurnuhiatrczcp
xsatnimwbcciu
grzmbrsvvcyigcbmcqfwiiknrohveubhyijxeyzfm
kqyewccgcqrrrznwxmoztlyseagbpyho
najju
nis
awgzdvfjkzlrsjcqfeacx
oisuflfigrjaex
desbdulyuwqxuxianyypybxwlql
ekmqgspvqpftpwswayh
egbyj
fznzprhvnnwcxgcc
wfdsueieosmugirxbymbpmfrspvrktjzguxm
qkjrufshwnfwwpbhukdjlaqvljlgubmqmhnha
hwqpudgnblhlxppbrmbznotteivuzguuwlhtkytky
w
yofkyzbpg
cenolnfnllkvhikrpttcxgqxmufvorekjruyjxmr
hyexmpjijgzumawp
cdbevdilgopbzlo
fivelagckslkugdxprjxkylizewcptwxfhomzuituujixchadmnjoktnqa
csojvlinzmmkkfzqueamnuwkanzdzsavgohposbuoamoevehqrmcxdsuyelvvctoejzoertqormhaaxwofvjzekwt
sbkghhnhutrvwtyjaxndzyjamrhx
jjyqy
majwbnrhveuhrsbbbjrwpwuplifeseylqh
wyvutpxnkrnkuxxetjkkifpqb
dyzucmbcvgnjeecm
hz
uhnuipthxrzkqluosvk
lwqqzsdwiwvwaqfwlvubadlyizlo
jbd
oyzjeu
kydjkbsqxnbfiuesc
smeubjqrcxdvhsabzceyglqjzbfmoacmwvwjbhhxbr
uabipgecujfdfxpmdzrscdyvefizabgspqjrrkmgjt
xgvdgzryz
lw
uimob
ifhn
bqph
ole
g
wt
k
yslzrkwkundxfdibwqvucemepqxlmlpyngabbeciuzhptpjdetyngrtxrdtzmvq
ccwapidp
bwvrgvmtshevrophy
ni
fdkplu
mdykey
i
rhsrenoetdggpjb
djmkplpeabsholx
judxtub
fooakqwvocvpcrvxqhvtmpvhkrecy
uuxscjillynilbkrgt
evtinrmilniguarqritpeipwochmdw
sxaqzjybydyvnmmjtdcgkjnqfcklbfpkdfyewgcukqoiegyfp
kg
ovrwieqhy
jcxqtkerzjwhs
xeonglszbgypafhmqcaseimzjgebkvigbqwsayrnrprtuvhsxyitfqygohgorcdnufbcyvevvgzmjrgjqqquwkszplogx
zdketqqv
yebckucwayckeezfvtnavglpjh
zorkfrwk
pad
xqaquxudybwtgixbfktinctfirjfdayh
rieknj
ebk
qzbcfywfdmhsdruhopovemafijbscagllkmhmof
asbsnbddlobwoqatfhkbhhsymzqxjuixwreheugvngmgcuqpkjhhfwpbarqaxrwgwnjbanljlds
etevdvlc
lqyjrnmenhn
k
tsf
zczgeavcexh
jlpuxywtsrvnvluruqhecjca
ir
rikrgkmhwaosodkxgcnrexfmdrszhnmutpvwztg
bffjqovvkemctnsgeh
weysbhzixiipfithjfsk
usyzvaiyuhmksfluoirfbnsu
o
cgawpdakaszeafdtbdkqtlzkrpnoqomqvuaqcfmzgvfegovtfaonelpv
izmrcjlk
xmzemniyrzy
knqexaafsdlimdamcrprlshq
qkmqw
dntgjwsibclvposdwjuklvtejjjdjibgpyynqpgprvvaetshhmvfkcpb
otvazkrkklrxfotpopyjte
fghkcnpi
rulyaihsowvcgbzeiblhuhhfbmncqsuuqcxvseorn
exirzfmojnxcoqom
zsgpgtokun
zvamxfocorganbtlafifwdqmqtsnktbwwtewborq
cxlnaspjqvsitjyzyriqsuorjsrvzqenisprttudxntsbqrpjtdkxnwcwgjyxmgtqljcrmrbrmyvosojzlumcmjcgfjsdehec
mvx
mt
mckr
teulvroifk
laaicc
koufy
bexmwsvyarnznebdfy
ripvviosbqijsxnjilwddaqaqemzsdarnxmfooxghoypizwtbueo
ljycycuqwfnzbambibqdixmkkvwtubepla
cis
kcg
vmbbiuuoamenzepuagpfujevfstqtndjxjchdvycfrrrowochtjdmkklgnhf
pmorrwguxkvdxpluatagaziin
uwvzbmkmykjkmknzppklx
pnzxuvsrjunqxercsnvayhykcazdeclomdsasgkpqpiufyfqsxhj
yceizkddwojgweegcllaagpvrpo
ek
kuxxgbezqyxvfaxdwnqdgqsmneijunxzlwxkrs
ldldbrxmvtjlqxifngmactzqcygkvuteffcmvphevilabgukatqakamjlridznodcvblvlogulmcixxfimh
iuzjootuywjqklolzzhpeaynydjwtufjavbozxnzckuzdodkvkjfmhinelv
swlfkcufscfcovmghqwcrtxjukwafoeogrkgubbqgwzm
gjcylkwgzroubdssuqeykqjcmguso
fzq
srfvysoxtlylctp
pbfeiuzwoyixews
ocvvunfsjnrtklmuuzjojw
xdjcnrpqhmpmpcwacpcdtmbsczvhllkqapzjuaf
nfnuvjz
fwnuiyqpn
wshxxxpzzxp
hibrxcfeqca
wqhlllarl
bukcbojv
plrytapy
xm
vlgfqoyzdczqbbaxjwbjjevjhxgopuqvqcrj
vpjqfbdnsdxlbuuiqocvrhap
mgumjbvnnzgnrdru
gcgzugazxdcamrhczfzhtmdjj
uislwq
vooai
zjuqfmebuzsqngzekyajujkopvayxtdzvugwwucvlsbrnhitfotmhhmgddlzlvqrkcponictrfweuilfjiuoabkfdvpjiqjrrgi
aptjfhmrnxaq
hbs
w
mwmoxqvucwygunplzvxtxpk
fgmqmtlorfzytjdzffsosfccnfwugrsrynuej
rpmpenrhsxoefnblyumjqwvuyszyppnttuyvazjdug
zdzxraxkroknkmqgvuoqeqdtvclsvvuwmdwzfugcpteohlogxubyoebvrzbqzklvehfcqadtdrkpubfhmokzwyosogepwragcpwxo
ax
dz
de
thvkdmnbdws
ejmubw
umvwkaubzurf
wyxtxeluaoox
wwbioobtgmkebxo
miglgnafmdarzkeblyjctuayzyoeqnfnbtrcbymdzkzg
loavxq
kzhllgsenxlbgdbfzwbg
yxflogzsohlcycbyzegeubfflouvtuatixhjvicjegltjiy
jigqfjppafdiarc
mcnmwtachgearonfcymvjbrnljjxmlzkudvzqsarnfysmxlfrtlvjxwvpdbhvwysnvcdozfcruhjwnucdzakkilmlfgjiolcatpfusm
n
pdjunfcz
dc
edxkkxabsbvmvifiinnoccki
bc
gwtwsvorwzfqpz
exidmexstfflkhi
s
s
c
wtcjfywlayhpbqktcepoybowtkrmnumqsg
ozclkgjdmdk
jmegtbunyexurvfexhqptnqzie
tkoenpagzwqfawlxvzaijsjqhmg
swodqfjpdqcbkc
ujokogocyaygdibgpglecis
shlmdmgonvpuaxlhrymkxtiytmv
brhk
jmsyiuomiywxhegilycjprkyfgojdo
wzdzrgpdiosdsvkcw
odlnmsfnjrcsnflviwvawybpczdkzvdocpwrmavz
p
ubowamlskcqhdxuckrxa
fawhntiwhmdwkddnahmtajqqazpdygttqivhdiodkcpcwv
gmxujmmaufmbipaiulhurzkfdg
eixjhmbaeoybiwk
kumntgrgiofcmujlzbcopuobambsw
mnjkqiyb
iktwnsnv
hfuzcl
tqiyqvagbqgtowpjbedgjot
dfemvamelxadkztogliizdtsddoboafawficudlefo
raecmxiiibljryswntpfed
mbwrtsebkeegw
x
epp
he
vnztrswhiusokqdkmsnpuswucvfhcthjbtam
baxlwidsgbdpzvnlj
tcbjjoadrzo
aiidahyllzzsg
igebuubweicbssgddpmqxunrawavuglmpxrtkqsvjjtscibqiejjfgfnovokodmqcqitlteiakooupvzkwucucrfdzjvjbqbkgutoybmpfvhbutigdxhfiqfplyciz
cnrhbjdnjftwfwlwzrdkwhajgsizsi
qfntnt
okqyfnbresp
asyg
mjqdkdyggdxzwuzglays
h
ifaqcazoy
fol
vvsusbnugduxsceozmsarbp
epjwtorx
bwiuxxiyc
cw
bwogruhctwkfvbexjnwircykxyzjmats
kygiochfwlpsvmxcgmtjrgvfdptd
q
qmpqe
z
jghffhqfoecmszunhxmzmzhlmbrvjabhrkihgjmvckhkfpaygjkg
kfiyfgounmhlvhupswqdgws
ezzdpyqucqoocsdcjtruqpokldfkmjhqzoynirybsifyaxnaxppthjoqy
nwetlgzwrhkhtuubbkbepuhbllxspvagxrqokwnrhkbwdwtp
hlazomrhqogoaxypqaszwfxxmutvbpuuvpdffuqskcbzlwyzcssnflkwiydoveyxjnzllzhyozbsa
hwnitkwbxcyibbqsluuqywbk
ozpfjsdrc
yoepefuy
lvmspzepnetra
genbrcrmuqfvkaouvuymoxhcxotjjhk
pcshyqgbmqdubsdajnyfqvxkqvywffzn
ukhcbyzwslqeq
otfrmcbnhbyffxqregqoufdxucjunwdhlqqeiiawbxlpqeyzzopfungrryqdykgizrhqodirvazm
dhpfhzyq
cloz
eduupqifolfekve
qiec
ishnjukvomntmdthlkajxpiwk
y
axl
tmyskjqkjsvumizlal
wvvolwewsfxhhdieuagdcuhwsgqvswpbkdkpxskloalmr
ryfmhe
z
mmbpgsyrfvzdatbjrjhuipwt
llzwizmmuulgwocowwmugtaoewkhnqxparvtynlffffdfcocdbba
pyczkzbmcgrdnxnmezsx
gsqe
mcocxcolcynhpecstsn
opnpplkccobjuhtbhirpzfxuktmpsiwbvsgiaavvdge
wpaldxzasnrbvtugjwytvtfttrh
zxecurevkjiyxy
wtnovebcmglkktic
fdpwfgvlvovxrwh
bmwgdullzy
uzwhagxinwqifxjbcntqzqoxkmpqxhe
jrfizsnwxwnnhb
inapddlahrp
ndtvkceobe
buskgghihdjmjlwfc
j
rkvffxwtmzoeruhlsurwtnuh
cbvkhfepkdishfpqvijzrpleuy
jzdpxjhcgqnybssfegvrnpgyehdqpgjwudbwrjbavp
xzzvgqdrdwajmdmj
vfatwsxvwfdbdhnijdujoyotwvwjipuuetichcfmvgrsnjpqaaezjtkvc
lbfoqgfshrtwgdqufwnfuitdrjydqctqixlzufkdbp
zgau
qefdpmtkecvtj
kuphldkvnzdtpd
dti
fpd
gfrliyegxsb
i
qsddsrmkyfgzrjeqnitmnypbcakh
vfbvbrpuogzhzrbmklvhji
nkz
xlufbaoblbmeub
alwuzxzmobwdukvwnkiwmuqhuxfhevogdnqtmxjptqznrk
cngpoty
ms
qvenfg
dmeaffm
jycfgnanbmoamhmarkmjcagbp
ysqmbhopgx
jczbzgwedsp
zxzwjrxcwdtleizjlvifjwgxiibezwxhtzywqdi
mtgnlu
xboxirdchurkfnklnpkapnqfxnhrxyseiujrznjm
zm
atddskbghcahlhql
szshwzmmvu
befdtpouamwhiisyybispkchpjhownatawjfbx
ennkzbrlygd
zbt
upphzpdwzmlhhhbqvjsfmbnrar
ddcs
ipbxgzyudjyongtcyygncojdufnufqpdppgvq
gc
isu
foa
wf
jdlvqxgfbowhohhyyngbcs
zjuwjyucdwblatsnywaaoftlcamfbcnw
lzrioesuhoeevczuwrnltmkahfwiu
uicggfbddqltnjyxfltbnaekncnyxsit
zkxsqkqrwrzrxgxbsgxatybfr
ptvmfyxdcglbfipcguqthjygzqnpqssscukzawynidtchjrrxwuxifoe
w
ohu
vg
zagpowezvbniybgold
lhqseqcxteiqtgnpanpvrmvvlltxh
mtfnxn
wyodtg
rawpbgtpbaktqzmmpzxmrlwpvvmdsl
widcfbirvswraukbmkhf
vplrueuxomjkqrtjgyxjdkexttzyozawyq
hrpbahllznvmjudzxpbbv
tlavfrxygjfipkupwnbacltcfepeg
icu
otxcu
aewazy
hl
fmrp
qaacthwzohenzjr
xbyebba
rvkph
mkhhmh
swme
zjmdoypaktglcyzobquunvthcdwegtbywpijxd
jvkuhnxqc
gibhqgjojsxt
bodbktzomiqujtbstqiyquwvqgufphqstenxvddkvtdh
bpusrxkfi
zgp
pmxvgamydyakituvvsucsuidrlznupcsinltmrahulhepxmhoqtfvpjkxzhrrinncuh
jzgkjjhjqykzelaszvcwvvwbnzsxdeaerfnaravk
ynanrqyrxo
zsmuxofullob
brklgrcqefdyoczy
qkpls
snhqumae
iqdtzjadzzvnqvdvjfsaf
nfqfdqiramueblxkaqxbbkxwywzgdbndjjiqk
tc
kp
cpuckbjsxhtxmomfesgxdpz
oseif
ybhxbvyxrpkrexrhjzoaxxohrhsniewsrktjnaztn
ggelspdzhzbchruhbjbjidgjwdlhdycetqaswh
jkgivsngygkbqtlmoj
dwpnanfvitxg
ospxbwxp
wgvmvrnjescemdoiralbkvemalifxnyhrbdgodml
hjtsnkzknkplbzsiwmneefdkihnhsamjsrxggclyjqgpqltizi
sykgbuypwwhweab
nvdkkkskmtiwpoerkon
sx
sbyflwwiqylbskdlxesmylpaz
dnwcjenaluwesyywfaezznwkdwpoesxpu
kie
dslccwfryol
gfhomgfn
zprjtfqvkotktzidmoyrivall
bunvsqkysdelozemnjoeqfolruulpbipm
ullyzfahpkhkja
hwd
kvyqtprpuulgsk
zotbkcadnxmfvqmtlbxalhughceyfcibtzzj
vvpjbgxygl
hpic
mhrqd
dv
thehuzdbaacoidjoljbysnqwrrxxplrdznmgiukkvjqbopb
moszjt
rmtbunktkywqirveeqfa
kse
wbfflnatgzobjrxghjgvcsyxoruenxhyomutbptswjajawqjpqafpdcstkiyjuilimecgejpqmyciolgcmdpcstzdozbmnza

View File

@ -1,11 +0,0 @@
--langdef=Rust
--langmap=Rust:.rs
--regex-Rust=/^[ \t]*(#\[[^\]]\][ \t]*)*(pub[ \t]+)?(extern[ \t]+)?("[^"]+"[ \t]+)?(unsafe[ \t]+)?fn[ \t]+([a-zA-Z0-9_]+)/\6/f,functions,function definitions/
--regex-Rust=/^[ \t]*(pub[ \t]+)?type[ \t]+([a-zA-Z0-9_]+)/\2/T,types,type definitions/
--regex-Rust=/^[ \t]*(pub[ \t]+)?enum[ \t]+([a-zA-Z0-9_]+)/\2/g,enum,enumeration names/
--regex-Rust=/^[ \t]*(pub[ \t]+)?struct[ \t]+([a-zA-Z0-9_]+)/\2/s,structure names/
--regex-Rust=/^[ \t]*(pub[ \t]+)?mod[ \t]+([a-zA-Z0-9_]+)/\2/m,modules,module names/
--regex-Rust=/^[ \t]*(pub[ \t]+)?static[ \t]+([a-zA-Z0-9_]+)/\2/c,consts,static constants/
--regex-Rust=/^[ \t]*(pub[ \t]+)?trait[ \t]+([a-zA-Z0-9_]+)/\2/t,traits,traits/
--regex-Rust=/^[ \t]*(pub[ \t]+)?impl([ \t\n]+<.*>)?[ \t]+([a-zA-Z0-9_]+)/\3/i,impls,trait implementations/
--regex-Rust=/^[ \t]*macro_rules![ \t]+([a-zA-Z0-9_]+)/\1/d,macros,macro definitions/

View File

@ -1,151 +0,0 @@
// This example demonstrates how to use the Aho-Corasick algorithm to rapidly
// scan text for matches in a large dictionary of keywords. This example by
// default reads your system's dictionary (~120,000 words).
extern crate aho_corasick;
extern crate csv;
extern crate docopt;
extern crate memmap;
extern crate rustc_serialize;
use std::error::Error;
use std::fs::File;
use std::io::{self, BufRead, Write};
use std::process;
use aho_corasick::{Automaton, AcAutomaton, Match};
use docopt::Docopt;
use memmap::{Mmap, Protection};
static USAGE: &'static str = "
Usage: dict-search [options] <input>
dict-search --help
Options:
-d <path>, --dict <path> Path to dictionary of keywords to search.
[default: /usr/share/dict/words]
-m <len>, --min-len <len> The minimum length for a keyword in UTF-8
encoded bytes. [default: 5]
--overlapping Report overlapping matches.
-c, --count Show only the numebr of matches.
--memory-usage Show memory usage of automaton.
--full Use fully expanded transition matrix.
Warning: may use lots of memory.
-h, --help Show this usage message.
";
#[derive(Clone, Debug, RustcDecodable)]
struct Args {
arg_input: String,
flag_dict: String,
flag_min_len: usize,
flag_overlapping: bool,
flag_memory_usage: bool,
flag_full: bool,
flag_count: bool,
}
fn main() {
let args: Args = Docopt::new(USAGE)
.and_then(|d| d.decode())
.unwrap_or_else(|e| e.exit());
match run(&args) {
Ok(()) => {}
Err(err) => {
writeln!(&mut io::stderr(), "{}", err).unwrap();
process::exit(1);
}
}
}
fn run(args: &Args) -> Result<(), Box<Error>> {
let aut = try!(build_automaton(&args.flag_dict, args.flag_min_len));
if args.flag_memory_usage {
let (bytes, states) = if args.flag_full {
let aut = aut.into_full();
(aut.heap_bytes(), aut.num_states())
} else {
(aut.heap_bytes(), aut.num_states())
};
println!("{} bytes, {} states", bytes, states);
return Ok(());
}
if args.flag_full {
let aut = aut.into_full();
if args.flag_overlapping {
if args.flag_count {
let mmap = Mmap::open_path(
&args.arg_input, Protection::Read).unwrap();
let text = unsafe { mmap.as_slice() };
println!("{}", aut.find_overlapping(text).count());
} else {
let rdr = try!(File::open(&args.arg_input));
try!(write_matches(&aut, aut.stream_find_overlapping(rdr)));
}
} else {
if args.flag_count {
let mmap = Mmap::open_path(
&args.arg_input, Protection::Read).unwrap();
let text = unsafe { mmap.as_slice() };
println!("{}", aut.find(text).count());
} else {
let rdr = try!(File::open(&args.arg_input));
try!(write_matches(&aut, aut.stream_find(rdr)));
}
}
} else {
if args.flag_overlapping {
if args.flag_count {
let mmap = Mmap::open_path(
&args.arg_input, Protection::Read).unwrap();
let text = unsafe { mmap.as_slice() };
println!("{}", aut.find_overlapping(text).count());
} else {
let rdr = try!(File::open(&args.arg_input));
try!(write_matches(&aut, aut.stream_find_overlapping(rdr)));
}
} else {
if args.flag_count {
let mmap = Mmap::open_path(
&args.arg_input, Protection::Read).unwrap();
let text = unsafe { mmap.as_slice() };
println!("{}", aut.find(text).count());
} else {
let rdr = try!(File::open(&args.arg_input));
try!(write_matches(&aut, aut.stream_find(rdr)));
}
}
}
Ok(())
}
fn write_matches<A, I>(aut: &A, it: I) -> Result<(), Box<Error>>
where A: Automaton<String>, I: Iterator<Item=io::Result<Match>> {
let mut wtr = csv::Writer::from_writer(io::stdout());
try!(wtr.write(["pattern", "start", "end"].iter()));
for m in it {
let m = try!(m);
try!(wtr.write([
aut.pattern(m.pati),
&m.start.to_string(),
&m.end.to_string(),
].iter()));
}
try!(wtr.flush());
Ok(())
}
fn build_automaton(
dict_path: &str,
min_len: usize,
) -> Result<AcAutomaton<String>, Box<Error>> {
let buf = io::BufReader::new(try!(File::open(dict_path)));
let mut lines = Vec::with_capacity(1 << 10);
for line in buf.lines() {
let line = try!(line);
if line.len() >= min_len {
lines.push(line);
}
}
Ok(AcAutomaton::with_transitions(lines))
}

View File

@ -1 +0,0 @@
au BufWritePost *.rs silent!make ctags > /dev/null 2>&1

View File

@ -1,503 +0,0 @@
use std::io::{self, BufRead};
use std::marker::PhantomData;
use memchr::{memchr, memchr2, memchr3};
use super::{ROOT_STATE, StateIdx};
/// An abstraction over automatons and their corresponding iterators.
/// The type parameter `P` is the type of the pattern that was used to
/// construct this Automaton.
pub trait Automaton<P> {
/// Return the next state given the current state and next character.
fn next_state(&self, si: StateIdx, b: u8) -> StateIdx;
/// Return true if and only if the given state and current pattern index
/// indicate a match.
fn has_match(&self, si: StateIdx, outi: usize) -> bool;
/// Build a match given the current state, pattern index and input index.
fn get_match(&self, si: StateIdx, outi: usize, texti: usize) -> Match;
/// Return the set of bytes that have transitions in the root state.
fn start_bytes(&self) -> &[u8];
/// Returns all of the patterns matched by this automaton.
///
/// The order of the patterns is the order in which they were added.
fn patterns(&self) -> &[P];
/// Returns the pattern indexed at `i`.
///
/// The index corresponds to the position at which the pattern was added
/// to the automaton, starting at `0`.
fn pattern(&self, i: usize) -> &P;
/// Return the number of patterns in the automaton.
#[inline]
fn len(&self) -> usize {
self.patterns().len()
}
/// Returns true if the automaton has no patterns.
#[inline]
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Returns an iterator of non-overlapping matches in `s`.
fn find<'a, 's, Q: ?Sized + AsRef<[u8]>>(
&'a self,
s: &'s Q,
) -> Matches<'a, 's, P, Self>
where Self: Sized {
Matches {
aut: self,
text: s.as_ref(),
texti: 0,
si: ROOT_STATE,
_m: PhantomData,
}
}
/// Returns an iterator of overlapping matches in `s`.
fn find_overlapping<'a, 's, Q: ?Sized + AsRef<[u8]>>(
&'a self,
s: &'s Q,
) -> MatchesOverlapping<'a, 's, P, Self>
where Self: Sized {
MatchesOverlapping {
aut: self,
text: s.as_ref(),
texti: 0,
si: ROOT_STATE,
outi: 0,
_m: PhantomData,
}
}
/// Returns an iterator of non-overlapping matches in the given reader.
fn stream_find<'a, R: io::Read>(
&'a self,
rdr: R,
) -> StreamMatches<'a, R, P, Self>
where Self: Sized {
StreamMatches {
aut: self,
buf: io::BufReader::new(rdr),
texti: 0,
si: ROOT_STATE,
_m: PhantomData,
}
}
/// Returns an iterator of overlapping matches in the given reader.
fn stream_find_overlapping<'a, R: io::Read>(
&'a self,
rdr: R,
) -> StreamMatchesOverlapping<'a, R, P, Self>
where Self: Sized {
StreamMatchesOverlapping {
aut: self,
buf: io::BufReader::new(rdr),
texti: 0,
si: ROOT_STATE,
outi: 0,
_m: PhantomData,
}
}
}
impl<'a, P: AsRef<[u8]>, A: 'a + Automaton<P> + ?Sized>
Automaton<P> for &'a A {
fn next_state(&self, si: StateIdx, b: u8) -> StateIdx {
(**self).next_state(si, b)
}
fn has_match(&self, si: StateIdx, outi: usize) -> bool {
(**self).has_match(si, outi)
}
fn start_bytes(&self) -> &[u8] {
(**self).start_bytes()
}
fn patterns(&self) -> &[P] {
(**self).patterns()
}
fn pattern(&self, i: usize) -> &P {
(**self).pattern(i)
}
fn get_match(&self, si: StateIdx, outi: usize, texti: usize) -> Match {
(**self).get_match(si, outi, texti)
}
}
/// Records a match in the search text.
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Match {
/// The pattern index.
///
/// This corresponds to the ordering in which the matched pattern was
/// added to the automaton, starting at `0`.
pub pati: usize,
/// The starting byte offset of the match in the search text.
pub start: usize,
/// The ending byte offset of the match in the search text.
///
/// (This can be re-captiulated with `pati` and adding the pattern's
/// length to `start`, but it is convenient to have it here.)
pub end: usize,
}
/// An iterator of non-overlapping matches for in-memory text.
///
/// This iterator yields `Match` values.
///
/// `'a` is the lifetime of the automaton, `'s` is the lifetime of the
/// search text, and `P` is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct Matches<'a, 's, P, A: 'a + Automaton<P> + ?Sized> {
aut: &'a A,
text: &'s [u8],
texti: usize,
si: StateIdx,
_m: PhantomData<P>,
}
// When there's an initial lone start byte, it is usually worth it
// to use `memchr` to skip along the input. The problem is that
// the skipping function is called in the inner match loop, which
// can be quite costly if the skipping condition is never met.
// Therefore, we lift the case analysis outside of the inner loop at
// the cost of repeating code.
//
// `step_to_match` is the version of the inner loop without skipping,
// and `skip_to_match` is the version with skipping.
#[inline(never)]
fn step_to_match<P, A: Automaton<P> + ?Sized>(
aut: &A,
text: &[u8],
mut texti: usize,
mut si: StateIdx
) -> Option<(usize, StateIdx)> {
while texti < text.len() {
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
if texti + 4 < text.len() {
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
texti += 1;
}
}
None
}
fn skip_to_match<P, A: Automaton<P> + ?Sized, F: Fn(&A, &[u8], usize) -> usize>(
aut: &A,
text: &[u8],
mut texti: usize,
mut si: StateIdx,
skip: F,
) -> Option<(usize, StateIdx)> {
if si == ROOT_STATE {
texti = skip(aut, text, texti);
}
while texti < text.len() {
si = aut.next_state(si, text[texti]);
if aut.has_match(si, 0) {
return Some((texti, si));
}
if si == ROOT_STATE {
texti = skip(aut, text, texti + 1);
} else {
texti += 1;
}
}
None
}
#[inline]
fn skip1<P, A: Automaton<P> + ?Sized>(
aut: &A,
text: &[u8],
at: usize,
) -> usize {
debug_assert!(aut.start_bytes().len() == 1);
let b = aut.start_bytes()[0];
match memchr(b, &text[at..]) {
None => text.len(),
Some(i) => at + i,
}
}
#[inline]
fn skip2<P, A: Automaton<P> + ?Sized>(
aut: &A,
text: &[u8],
at: usize,
) -> usize {
debug_assert!(aut.start_bytes().len() == 2);
let (b1, b2) = (aut.start_bytes()[0], aut.start_bytes()[1]);
match memchr2(b1, b2, &text[at..]) {
None => text.len(),
Some(i) => at + i,
}
}
#[inline]
fn skip3<P, A: Automaton<P> + ?Sized>(
aut: &A,
text: &[u8],
at: usize,
) -> usize {
debug_assert!(aut.start_bytes().len() == 3);
let (b1, b2, b3) = (
aut.start_bytes()[0], aut.start_bytes()[1], aut.start_bytes()[2],
);
match memchr3(b1, b2, b3, &text[at..]) {
None => text.len(),
Some(i) => at + i,
}
}
impl<'a, 's, P, A: Automaton<P> + ?Sized> Iterator for Matches<'a, 's, P, A> {
type Item = Match;
fn next(&mut self) -> Option<Match> {
if self.aut.start_bytes().len() == 1 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip1);
if let Some((texti, si)) = skip {
self.texti = texti + 1;
self.si = ROOT_STATE;
return Some(self.aut.get_match(si, 0, texti));
}
} else if self.aut.start_bytes().len() == 2 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip2);
if let Some((texti, si)) = skip {
self.texti = texti + 1;
self.si = ROOT_STATE;
return Some(self.aut.get_match(si, 0, texti));
}
} else if self.aut.start_bytes().len() == 3 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip3);
if let Some((texti, si)) = skip {
self.texti = texti + 1;
self.si = ROOT_STATE;
return Some(self.aut.get_match(si, 0, texti));
}
} else {
let step = step_to_match(self.aut, self.text, self.texti, self.si);
if let Some((texti, si)) = step {
self.texti = texti + 1;
self.si = ROOT_STATE;
return Some(self.aut.get_match(si, 0, texti));
}
}
None
}
}
/// An iterator of non-overlapping matches for streaming text.
///
/// This iterator yields `io::Result<Match>` values.
///
/// `'a` is the lifetime of the automaton, `R` is the type of the underlying
/// `io::Read`er, and P is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct StreamMatches<'a, R, P, A: 'a + Automaton<P> + ?Sized> {
aut: &'a A,
buf: io::BufReader<R>,
texti: usize,
si: StateIdx,
_m: PhantomData<P>,
}
impl<'a, R: io::Read, P, A: Automaton<P>>
Iterator for StreamMatches<'a, R, P, A> {
type Item = io::Result<Match>;
fn next(&mut self) -> Option<io::Result<Match>> {
let mut m = None;
let mut consumed = 0;
'LOOP: loop {
self.buf.consume(consumed);
let bs = match self.buf.fill_buf() {
Err(err) => return Some(Err(err)),
Ok(bs) if bs.len() == 0 => break,
Ok(bs) => bs,
};
consumed = bs.len(); // is shortened if we find a match
for (i, &b) in bs.iter().enumerate() {
self.si = self.aut.next_state(self.si, b);
if self.aut.has_match(self.si, 0) {
m = Some(Ok(self.aut.get_match(self.si, 0, self.texti)));
consumed = i + 1;
self.texti += 1;
self.si = ROOT_STATE;
break 'LOOP;
}
self.texti += 1;
}
}
self.buf.consume(consumed);
m
}
}
/// An iterator of overlapping matches for in-memory text.
///
/// This iterator yields `Match` values.
///
/// `'a` is the lifetime of the automaton, `'s` is the lifetime of the
/// search text, and `P` is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct MatchesOverlapping<'a, 's, P, A: 'a + Automaton<P> + ?Sized> {
aut: &'a A,
text: &'s [u8],
texti: usize,
si: StateIdx,
outi: usize,
_m: PhantomData<P>,
}
impl<'a, 's, P, A: Automaton<P> + ?Sized>
Iterator for MatchesOverlapping<'a, 's, P, A> {
type Item = Match;
fn next(&mut self) -> Option<Match> {
if self.aut.has_match(self.si, self.outi) {
let m = self.aut.get_match(self.si, self.outi, self.texti);
self.outi += 1;
if !self.aut.has_match(self.si, self.outi) {
self.texti += 1;
}
return Some(m);
}
self.outi = 0;
if self.aut.start_bytes().len() == 1 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip1);
if let Some((texti, si)) = skip {
self.texti = texti;
self.si = si;
return self.next();
}
} else if self.aut.start_bytes().len() == 2 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip2);
if let Some((texti, si)) = skip {
self.texti = texti;
self.si = si;
return self.next();
}
} else if self.aut.start_bytes().len() == 3 {
let skip = skip_to_match(
self.aut, self.text, self.texti, self.si, skip3);
if let Some((texti, si)) = skip {
self.texti = texti;
self.si = si;
return self.next();
}
} else {
let step = step_to_match(self.aut, self.text, self.texti, self.si);
if let Some((texti, si)) = step {
self.texti = texti;
self.si = si;
return self.next();
}
}
None
}
}
/// An iterator of overlapping matches for streaming text.
///
/// This iterator yields `io::Result<Match>` values.
///
/// `'a` is the lifetime of the automaton, `R` is the type of the underlying
/// `io::Read`er, and P is the type of the Automaton's pattern.
#[derive(Debug)]
pub struct StreamMatchesOverlapping<'a, R, P, A: 'a + Automaton<P> + ?Sized> {
aut: &'a A,
buf: io::BufReader<R>,
texti: usize,
si: StateIdx,
outi: usize,
_m: PhantomData<P>,
}
impl<'a, R: io::Read, P, A: Automaton<P> + ?Sized>
Iterator for StreamMatchesOverlapping<'a, R, P, A> {
type Item = io::Result<Match>;
fn next(&mut self) -> Option<io::Result<Match>> {
if self.aut.has_match(self.si, self.outi) {
let m = self.aut.get_match(self.si, self.outi, self.texti);
self.outi += 1;
if !self.aut.has_match(self.si, self.outi) {
self.texti += 1;
}
return Some(Ok(m));
}
let mut m = None;
let mut consumed = 0;
self.outi = 0;
'LOOP: loop {
self.buf.consume(consumed);
let bs = match self.buf.fill_buf() {
Err(err) => return Some(Err(err)),
Ok(bs) if bs.len() == 0 => break,
Ok(bs) => bs,
};
consumed = bs.len(); // is shortened if we find a match
for (i, &b) in bs.iter().enumerate() {
self.si = self.aut.next_state(self.si, b);
if self.aut.has_match(self.si, self.outi) {
m = Some(Ok(self.aut.get_match(
self.si, self.outi, self.texti)));
consumed = i + 1;
self.outi += 1;
if !self.aut.has_match(self.si, self.outi) {
self.texti += 1;
}
break 'LOOP;
}
self.texti += 1;
}
}
self.buf.consume(consumed);
m
}
}

View File

@ -1,136 +0,0 @@
use std::fmt;
use std::mem;
use super::{
FAIL_STATE,
StateIdx, AcAutomaton, Transitions, Match,
usize_bytes, vec_bytes,
};
use super::autiter::Automaton;
/// A complete Aho-Corasick automaton.
///
/// This uses a single transition matrix that permits each input character
/// to move to the next state with a single lookup in the matrix.
///
/// This is as fast as it gets, but it is guaranteed to use a lot of memory.
/// Namely, it will use at least `4 * 256 * #states`, where the number of
/// states is capped at length of all patterns concatenated.
#[derive(Clone)]
pub struct FullAcAutomaton<P> {
pats: Vec<P>,
trans: Vec<StateIdx>, // row-major, where states are rows
out: Vec<Vec<usize>>, // indexed by StateIdx
start_bytes: Vec<u8>,
}
impl<P: AsRef<[u8]>> FullAcAutomaton<P> {
/// Build a new expanded Aho-Corasick automaton from an existing
/// Aho-Corasick automaton.
pub fn new<T: Transitions>(ac: AcAutomaton<P, T>) -> FullAcAutomaton<P> {
let mut fac = FullAcAutomaton {
pats: vec![],
trans: vec![FAIL_STATE; 256 * ac.states.len()],
out: vec![vec![]; ac.states.len()],
start_bytes: vec![],
};
fac.build_matrix(&ac);
fac.pats = ac.pats;
fac.start_bytes = ac.start_bytes;
fac
}
#[doc(hidden)]
pub fn memory_usage(&self) -> usize {
self.pats.iter()
.map(|p| vec_bytes() + p.as_ref().len())
.fold(0, |a, b| a + b)
+ (4 * self.trans.len())
+ self.out.iter()
.map(|v| vec_bytes() + (usize_bytes() * v.len()))
.fold(0, |a, b| a + b)
+ self.start_bytes.len()
}
#[doc(hidden)]
pub fn heap_bytes(&self) -> usize {
self.pats.iter()
.map(|p| mem::size_of::<P>() + p.as_ref().len())
.fold(0, |a, b| a + b)
+ (4 * self.trans.len())
+ self.out.iter()
.map(|v| vec_bytes() + (usize_bytes() * v.len()))
.fold(0, |a, b| a + b)
+ self.start_bytes.len()
}
fn set(&mut self, si: StateIdx, i: u8, goto: StateIdx) {
let ns = self.num_states();
self.trans[i as usize * ns + si as usize] = goto;
}
#[doc(hidden)]
#[inline]
pub fn num_states(&self) -> usize {
self.out.len()
}
}
impl<P: AsRef<[u8]>> Automaton<P> for FullAcAutomaton<P> {
#[inline]
fn next_state(&self, si: StateIdx, i: u8) -> StateIdx {
let at = i as usize * self.num_states() + si as usize;
unsafe { *self.trans.get_unchecked(at) }
}
#[inline]
fn get_match(&self, si: StateIdx, outi: usize, texti: usize) -> Match {
let pati = self.out[si as usize][outi];
let patlen = self.pats[pati].as_ref().len();
let start = texti + 1 - patlen;
Match {
pati: pati,
start: start,
end: start + patlen,
}
}
#[inline]
fn has_match(&self, si: StateIdx, outi: usize) -> bool {
unsafe { outi < self.out.get_unchecked(si as usize).len() }
}
#[inline]
fn start_bytes(&self) -> &[u8] {
&self.start_bytes
}
#[inline]
fn patterns(&self) -> &[P] {
&self.pats
}
#[inline]
fn pattern(&self, i: usize) -> &P {
&self.pats[i]
}
}
impl<P: AsRef<[u8]>> FullAcAutomaton<P> {
fn build_matrix<T: Transitions>(&mut self, ac: &AcAutomaton<P, T>) {
for (si, s) in ac.states.iter().enumerate().skip(1) {
for b in (0..256).map(|b| b as u8) {
self.set(si as StateIdx, b, ac.next_state(si as StateIdx, b));
}
for &pati in &s.out {
self.out[si].push(pati);
}
}
}
}
impl<P: AsRef<[u8]> + fmt::Debug> fmt::Debug for FullAcAutomaton<P> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "FullAcAutomaton({:?})", self.pats)
}
}

View File

@ -1,925 +0,0 @@
/*!
An implementation of the
[Aho-Corasick string search algorithm](https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_string_matching_algorithm).
The Aho-Corasick algorithm is principally useful when you need to search many
large texts for a fixed (possibly large) set of keywords. In particular, the
Aho-Corasick algorithm preprocesses the set of keywords by constructing a
finite state machine. The search phase is then a quick linear scan through the
text. Each character in the search text causes a state transition in the
automaton. Matches are reported when the automaton enters a match state.
# Examples
The main type exposed by this crate is `AcAutomaton`, which can be constructed
from an iterator of pattern strings:
```rust
use aho_corasick::{Automaton, AcAutomaton};
let aut = AcAutomaton::new(vec!["apple", "maple"]);
// AcAutomaton also implements `FromIterator`:
let aut: AcAutomaton<&str> = ["apple", "maple"].iter().cloned().collect();
```
Finding matches can be done with `find`:
```rust
use aho_corasick::{Automaton, AcAutomaton, Match};
let aut = AcAutomaton::new(vec!["apple", "maple"]);
let mut it = aut.find("I like maple apples.");
assert_eq!(it.next(), Some(Match {
pati: 1,
start: 7,
end: 12,
}));
assert_eq!(it.next(), Some(Match {
pati: 0,
start: 13,
end: 18,
}));
assert_eq!(it.next(), None);
```
Use `find_overlapping` if you want to report all matches, even if they
overlap with each other.
```rust
use aho_corasick::{Automaton, AcAutomaton, Match};
let aut = AcAutomaton::new(vec!["abc", "a"]);
let matches: Vec<_> = aut.find_overlapping("abc").collect();
assert_eq!(matches, vec![
Match { pati: 1, start: 0, end: 1}, Match { pati: 0, start: 0, end: 3 },
]);
// Regular `find` will report only one match:
let matches: Vec<_> = aut.find("abc").collect();
assert_eq!(matches, vec![Match { pati: 1, start: 0, end: 1}]);
```
Finally, there are also methods for finding matches on *streams*. Namely, the
search text does not have to live in memory. It's useful to run this on files
that can't fit into memory:
```no_run
use std::fs::File;
use aho_corasick::{Automaton, AcAutomaton};
let aut = AcAutomaton::new(vec!["foo", "bar", "baz"]);
let rdr = File::open("search.txt").unwrap();
for m in aut.stream_find(rdr) {
let m = m.unwrap(); // could be an IO error
println!("Pattern '{}' matched at: ({}, {})",
aut.pattern(m.pati), m.start, m.end);
}
```
There is also `stream_find_overlapping`, which is just like `find_overlapping`,
but it operates on streams.
Please see `dict-search.rs` in this crate's `examples` directory for a more
complete example. It creates a large automaton from a dictionary and can do a
streaming match over arbitrarily large data.
# Memory usage
A key aspect of an Aho-Corasick implementation is how the state transitions
are represented. The easiest way to make the automaton fast is to store a
sparse 256-slot map in each state. It maps an input byte to a state index.
This makes the matching loop extremely fast, since it translates to a simple
pointer read.
The problem is that as the automaton accumulates more states, you end up paying
a `256 * 4` (`4` is for the `u32` state index) byte penalty for every state
regardless of how many transitions it has.
To solve this, only states near the root of the automaton have this sparse
map representation. States near the leaves of the automaton use a dense mapping
that requires a linear scan.
(The specific limit currently set is `3`, so that states with a depth less than
or equal to `3` are less memory efficient. The result is that the memory usage
of the automaton stops growing rapidly past ~60MB, even for automatons with
thousands of patterns.)
If you'd like to opt for the less-memory-efficient-but-faster version, then
you can construct an `AcAutomaton` with a `Sparse` transition strategy:
```rust
use aho_corasick::{Automaton, AcAutomaton, Match, Sparse};
let aut = AcAutomaton::<&str, Sparse>::with_transitions(vec!["abc", "a"]);
let matches: Vec<_> = aut.find("abc").collect();
assert_eq!(matches, vec![Match { pati: 1, start: 0, end: 1}]);
```
*/
#![deny(missing_docs)]
extern crate memchr;
#[cfg(test)] extern crate quickcheck;
#[cfg(test)] extern crate rand;
use std::collections::VecDeque;
use std::fmt;
use std::iter::FromIterator;
use std::mem;
pub use self::autiter::{
Automaton, Match,
Matches, MatchesOverlapping, StreamMatches, StreamMatchesOverlapping,
};
pub use self::full::FullAcAutomaton;
// We're specifying paths explicitly so that we can use
// these modules simultaneously from `main.rs`.
// Should probably make just make `main.rs` a separate crate.
#[path = "autiter.rs"]
mod autiter;
#[path = "full.rs"]
mod full;
/// The integer type used for the state index.
///
/// Limiting this to 32 bit integers can have a big impact on memory usage
/// when using the `Sparse` transition representation.
pub type StateIdx = u32;
// Constants for special state indexes.
const FAIL_STATE: u32 = 0;
const ROOT_STATE: u32 = 1;
// Limit the depth at which we use a sparse alphabet map. Once the limit is
// reached, a dense set is used (and lookup becomes O(n)).
//
// This does have a performance hit, but the (straight forward) alternative
// is to have a `256 * 4` byte overhead for every state.
// Given that Aho-Corasick is typically used for dictionary searching, this
// can lead to dramatic memory bloat.
//
// This limit should only be increased at your peril. Namely, in the worst
// case, `256^DENSE_DEPTH_THRESHOLD * 4` corresponds to the memory usage in
// bytes. A value of `1` gives us a good balance. This is also a happy point
// in the benchmarks. A value of `0` gives considerably worse times on certain
// benchmarks (e.g., `ac_ten_one_prefix_byte_every_match`) than even a value
// of `1`. A value of `2` is slightly better than `1` and it looks like gains
// level off at that point with not much observable difference when set to
// `3`.
//
// Why not make this user configurable? Well, it doesn't make much sense
// because we pay for it with case analysis in the matching loop. Increasing it
// doesn't have much impact on performance (outside of pathological cases?).
//
// N.B. Someone else seems to have discovered an alternative, but I haven't
// grokked it yet: https://github.com/mischasan/aho-corasick
const DENSE_DEPTH_THRESHOLD: u32 = 1;
/// An Aho-Corasick finite automaton.
///
/// The type parameter `P` is the type of the pattern that was used to
/// construct this AcAutomaton.
#[derive(Clone)]
pub struct AcAutomaton<P, T=Dense> {
pats: Vec<P>,
states: Vec<State<T>>,
start_bytes: Vec<u8>,
}
#[derive(Clone)]
struct State<T> {
out: Vec<usize>,
fail: StateIdx,
goto: T,
depth: u32,
}
impl<P: AsRef<[u8]>> AcAutomaton<P> {
/// Create a new automaton from an iterator of patterns.
///
/// The patterns must be convertible to bytes (`&[u8]`) via the `AsRef`
/// trait.
pub fn new<I>(pats: I) -> AcAutomaton<P, Dense>
where I: IntoIterator<Item=P> {
AcAutomaton::with_transitions(pats)
}
}
impl<P: AsRef<[u8]>, T: Transitions> AcAutomaton<P, T> {
/// Create a new automaton from an iterator of patterns.
///
/// This constructor allows one to choose the transition representation.
///
/// The patterns must be convertible to bytes (`&[u8]`) via the `AsRef`
/// trait.
pub fn with_transitions<I>(pats: I) -> AcAutomaton<P, T>
where I: IntoIterator<Item=P> {
AcAutomaton {
pats: vec![], // filled in later, avoid wrath of borrow checker
states: vec![State::new(0), State::new(0)], // empty and root
start_bytes: vec![], // also filled in later
}.build(pats.into_iter().collect())
}
/// Build out the entire automaton into a single matrix.
///
/// This will make searching as fast as possible at the expense of using
/// at least `4 * 256 * #states` bytes of memory.
pub fn into_full(self) -> FullAcAutomaton<P> {
FullAcAutomaton::new(self)
}
#[doc(hidden)]
pub fn num_states(&self) -> usize {
self.states.len()
}
#[doc(hidden)]
pub fn heap_bytes(&self) -> usize {
self.pats.iter()
.map(|p| mem::size_of::<P>() + p.as_ref().len())
.fold(0, |a, b| a + b)
+ self.states.iter()
.map(|s| mem::size_of::<State<T>>() + s.heap_bytes())
.fold(0, |a, b| a + b)
+ self.start_bytes.len()
}
}
impl<P: AsRef<[u8]>, T: Transitions> Automaton<P> for AcAutomaton<P, T> {
#[inline]
fn next_state(&self, mut si: StateIdx, b: u8) -> StateIdx {
loop {
let maybe_si = self.states[si as usize].goto(b);
if maybe_si != FAIL_STATE {
si = maybe_si;
break;
} else {
si = self.states[si as usize].fail;
}
}
si
}
#[inline]
fn get_match(&self, si: StateIdx, outi: usize, texti: usize) -> Match {
let pati = self.states[si as usize].out[outi];
let patlen = self.pats[pati].as_ref().len();
let start = texti + 1 - patlen;
Match {
pati: pati,
start: start,
end: start + patlen,
}
}
#[inline]
fn has_match(&self, si: StateIdx, outi: usize) -> bool {
outi < self.states[si as usize].out.len()
}
#[inline]
fn start_bytes(&self) -> &[u8] {
&self.start_bytes
}
#[inline]
fn patterns(&self) -> &[P] {
&self.pats
}
#[inline]
fn pattern(&self, i: usize) -> &P {
&self.pats[i]
}
}
// Below contains code for *building* the automaton. It's a reasonably faithful
// translation of the description/psuedo-code from:
// http://www.cs.uku.fi/~kilpelai/BSA05/lectures/slides04.pdf
impl<P: AsRef<[u8]>, T: Transitions> AcAutomaton<P, T> {
// This is the first phase and builds the initial keyword tree.
fn build(mut self, pats: Vec<P>) -> AcAutomaton<P, T> {
for (pati, pat) in pats.iter().enumerate() {
if pat.as_ref().is_empty() {
continue;
}
let mut previ = ROOT_STATE;
for &b in pat.as_ref() {
if self.states[previ as usize].goto(b) != FAIL_STATE {
previ = self.states[previ as usize].goto(b);
} else {
let depth = self.states[previ as usize].depth + 1;
let nexti = self.add_state(State::new(depth));
self.states[previ as usize].set_goto(b, nexti);
previ = nexti;
}
}
self.states[previ as usize].out.push(pati);
}
for c in (0..256).into_iter().map(|c| c as u8) {
if self.states[ROOT_STATE as usize].goto(c) == FAIL_STATE {
self.states[ROOT_STATE as usize].set_goto(c, ROOT_STATE);
} else {
self.start_bytes.push(c);
}
}
// If any of the start bytes are non-ASCII, then remove them all,
// because we don't want to be calling memchr on non-ASCII bytes.
// (Well, we could, but it requires being more clever. Simply using
// the prefix byte isn't good enough.)
if self.start_bytes.iter().any(|&b| b > 0x7F) {
self.start_bytes.clear();
}
self.pats = pats;
self.fill()
}
// The second phase that fills in the back links.
fn fill(mut self) -> AcAutomaton<P, T> {
// Fill up the queue with all non-root transitions out of the root
// node. Then proceed by breadth first traversal.
let mut q = VecDeque::new();
for c in (0..256).into_iter().map(|c| c as u8) {
let si = self.states[ROOT_STATE as usize].goto(c);
if si != ROOT_STATE {
q.push_front(si);
}
}
while let Some(si) = q.pop_back() {
for c in (0..256).into_iter().map(|c| c as u8) {
let u = self.states[si as usize].goto(c);
if u != FAIL_STATE {
q.push_front(u);
let mut v = self.states[si as usize].fail;
while self.states[v as usize].goto(c) == FAIL_STATE {
v = self.states[v as usize].fail;
}
let ufail = self.states[v as usize].goto(c);
self.states[u as usize].fail = ufail;
let ufail_out = self.states[ufail as usize].out.clone();
self.states[u as usize].out.extend(ufail_out);
}
}
}
self
}
fn add_state(&mut self, state: State<T>) -> StateIdx {
let i = self.states.len();
self.states.push(state);
i as StateIdx
}
}
impl<T: Transitions> State<T> {
fn new(depth: u32) -> State<T> {
State {
out: vec![],
fail: 1,
goto: Transitions::new(depth),
depth: depth,
}
}
fn goto(&self, b: u8) -> StateIdx {
self.goto.goto(b)
}
fn set_goto(&mut self, b: u8, si: StateIdx) {
self.goto.set_goto(b, si);
}
fn heap_bytes(&self) -> usize {
(self.out.len() * usize_bytes())
+ self.goto.heap_bytes()
}
}
/// An abstraction over state transition strategies.
///
/// This is an attempt to let the caller choose the space/time trade offs
/// used for state transitions.
///
/// (It's possible that this interface is merely good enough for just the two
/// implementations in this crate.)
pub trait Transitions {
/// Return a new state at the given depth.
fn new(depth: u32) -> Self;
/// Return the next state index given the next character.
fn goto(&self, alpha: u8) -> StateIdx;
/// Set the next state index for the character given.
fn set_goto(&mut self, alpha: u8, si: StateIdx);
/// The memory use in bytes (on the heap) of this set of transitions.
fn heap_bytes(&self) -> usize;
}
/// State transitions that can be stored either sparsely or densely.
///
/// This uses less space but at the expense of slower matching.
#[derive(Clone, Debug)]
pub struct Dense(DenseChoice);
#[derive(Clone, Debug)]
enum DenseChoice {
Sparse(Vec<StateIdx>), // indexed by alphabet
Dense(Vec<(u8, StateIdx)>),
}
impl Transitions for Dense {
fn new(depth: u32) -> Dense {
if depth <= DENSE_DEPTH_THRESHOLD {
Dense(DenseChoice::Sparse(vec![0; 256]))
} else {
Dense(DenseChoice::Dense(vec![]))
}
}
fn goto(&self, b1: u8) -> StateIdx {
match self.0 {
DenseChoice::Sparse(ref m) => m[b1 as usize],
DenseChoice::Dense(ref m) => {
for &(b2, si) in m {
if b1 == b2 {
return si;
}
}
FAIL_STATE
}
}
}
fn set_goto(&mut self, b: u8, si: StateIdx) {
match self.0 {
DenseChoice::Sparse(ref mut m) => m[b as usize] = si,
DenseChoice::Dense(ref mut m) => m.push((b, si)),
}
}
fn heap_bytes(&self) -> usize {
match self.0 {
DenseChoice::Sparse(ref m) => m.len() * 4,
DenseChoice::Dense(ref m) => m.len() * (1 + 4),
}
}
}
/// State transitions that are always sparse.
///
/// This can use enormous amounts of memory when there are many patterns,
/// but matching is very fast.
#[derive(Clone, Debug)]
pub struct Sparse(Vec<StateIdx>);
impl Transitions for Sparse {
fn new(_: u32) -> Sparse {
Sparse(vec![0; 256])
}
#[inline]
fn goto(&self, b: u8) -> StateIdx {
self.0[b as usize]
}
fn set_goto(&mut self, b: u8, si: StateIdx) {
self.0[b as usize] = si;
}
fn heap_bytes(&self) -> usize {
self.0.len() * 4
}
}
impl<S: AsRef<[u8]>> FromIterator<S> for AcAutomaton<S> {
/// Create an automaton from an iterator of strings.
fn from_iter<T>(it: T) -> AcAutomaton<S> where T: IntoIterator<Item=S> {
AcAutomaton::new(it)
}
}
// Provide some question debug impls for viewing automatons.
// The custom impls mostly exist for special showing of sparse maps.
impl<P: AsRef<[u8]> + fmt::Debug, T: Transitions>
fmt::Debug for AcAutomaton<P, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use std::iter::repeat;
try!(writeln!(f, "{}", repeat('-').take(79).collect::<String>()));
try!(writeln!(f, "Patterns: {:?}", self.pats));
for (i, state) in self.states.iter().enumerate().skip(1) {
try!(writeln!(f, "{:3}: {}", i, state.debug(i == 1)));
}
write!(f, "{}", repeat('-').take(79).collect::<String>())
}
}
impl<T: Transitions> State<T> {
fn debug(&self, root: bool) -> String {
format!("State {{ depth: {:?}, out: {:?}, fail: {:?}, goto: {{{}}} }}",
self.depth, self.out, self.fail, self.goto_string(root))
}
fn goto_string(&self, root: bool) -> String {
use std::char::from_u32;
let mut goto = vec![];
for b in (0..256).map(|b| b as u8) {
let si = self.goto(b);
if (!root && si == FAIL_STATE) || (root && si == ROOT_STATE) {
continue;
}
goto.push(format!("{} => {}", from_u32(b as u32).unwrap(), si));
}
goto.join(", ")
}
}
impl<T: Transitions> fmt::Debug for State<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.debug(false))
}
}
impl<T: Transitions> AcAutomaton<String, T> {
#[doc(hidden)]
pub fn dot(&self) -> String {
use std::fmt::Write;
let mut out = String::new();
macro_rules! w {
($w:expr, $($tt:tt)*) => { {write!($w, $($tt)*)}.unwrap() }
}
w!(out, r#"
digraph automaton {{
label=<<FONT POINT-SIZE="20">{}</FONT>>;
labelloc="l";
labeljust="l";
rankdir="LR";
"#, self.pats.join(", "));
for (i, s) in self.states.iter().enumerate().skip(1) {
let i = i as u32;
if s.out.len() == 0 {
w!(out, " {};\n", i);
} else {
w!(out, " {} [peripheries=2];\n", i);
}
w!(out, " {} -> {} [style=dashed];\n", i, s.fail);
for b in (0..256).map(|b| b as u8) {
let si = s.goto(b);
if si == FAIL_STATE || (i == ROOT_STATE && si == ROOT_STATE) {
continue;
}
w!(out, " {} -> {} [label={}];\n", i, si, b as char);
}
}
w!(out, "}}");
out
}
}
fn vec_bytes() -> usize {
usize_bytes() * 3
}
fn usize_bytes() -> usize {
let bits = usize::max_value().count_ones() as usize;
bits / 8
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use std::io;
use quickcheck::{Arbitrary, Gen, quickcheck};
use super::{Automaton, AcAutomaton, Match};
fn aut_find<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
AcAutomaton::new(xs.to_vec()).find(&haystack).collect()
}
fn aut_finds<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
let cur = io::Cursor::new(haystack.as_bytes());
AcAutomaton::new(xs.to_vec())
.stream_find(cur).map(|r| r.unwrap()).collect()
}
fn aut_findf<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
AcAutomaton::new(xs.to_vec()).into_full().find(haystack).collect()
}
fn aut_findfs<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
let cur = io::Cursor::new(haystack.as_bytes());
AcAutomaton::new(xs.to_vec())
.into_full()
.stream_find(cur).map(|r| r.unwrap()).collect()
}
fn aut_findo<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
AcAutomaton::new(xs.to_vec()).find_overlapping(haystack).collect()
}
fn aut_findos<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
let cur = io::Cursor::new(haystack.as_bytes());
AcAutomaton::new(xs.to_vec())
.stream_find_overlapping(cur).map(|r| r.unwrap()).collect()
}
fn aut_findfo<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
AcAutomaton::new(xs.to_vec())
.into_full().find_overlapping(haystack).collect()
}
fn aut_findfos<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + AsRef<[u8]> {
let cur = io::Cursor::new(haystack.as_bytes());
AcAutomaton::new(xs.to_vec())
.into_full()
.stream_find_overlapping(cur).map(|r| r.unwrap()).collect()
}
#[test]
fn one_pattern_one_match() {
let ns = vec!["a"];
let hay = "za";
let matches = vec![
Match { pati: 0, start: 1, end: 2 },
];
assert_eq!(&aut_find(&ns, hay), &matches);
assert_eq!(&aut_finds(&ns, hay), &matches);
assert_eq!(&aut_findf(&ns, hay), &matches);
assert_eq!(&aut_findfs(&ns, hay), &matches);
}
#[test]
fn one_pattern_many_match() {
let ns = vec!["a"];
let hay = "zazazzzza";
let matches = vec![
Match { pati: 0, start: 1, end: 2 },
Match { pati: 0, start: 3, end: 4 },
Match { pati: 0, start: 8, end: 9 },
];
assert_eq!(&aut_find(&ns, hay), &matches);
assert_eq!(&aut_finds(&ns, hay), &matches);
assert_eq!(&aut_findf(&ns, hay), &matches);
assert_eq!(&aut_findfs(&ns, hay), &matches);
}
#[test]
fn one_longer_pattern_one_match() {
let ns = vec!["abc"];
let hay = "zazabcz";
let matches = vec![ Match { pati: 0, start: 3, end: 6 } ];
assert_eq!(&aut_find(&ns, hay), &matches);
assert_eq!(&aut_finds(&ns, hay), &matches);
assert_eq!(&aut_findf(&ns, hay), &matches);
assert_eq!(&aut_findfs(&ns, hay), &matches);
}
#[test]
fn one_longer_pattern_many_match() {
let ns = vec!["abc"];
let hay = "zazabczzzzazzzabc";
let matches = vec![
Match { pati: 0, start: 3, end: 6 },
Match { pati: 0, start: 14, end: 17 },
];
assert_eq!(&aut_find(&ns, hay), &matches);
assert_eq!(&aut_finds(&ns, hay), &matches);
assert_eq!(&aut_findf(&ns, hay), &matches);
assert_eq!(&aut_findfs(&ns, hay), &matches);
}
#[test]
fn many_pattern_one_match() {
let ns = vec!["a", "b"];
let hay = "zb";
let matches = vec![ Match { pati: 1, start: 1, end: 2 } ];
assert_eq!(&aut_find(&ns, hay), &matches);
assert_eq!(&aut_finds(&ns, hay), &matches);
assert_eq!(&aut_findf(&ns, hay), &matches);
assert_eq!(&aut_findfs(&ns, hay), &matches);
}
#[test]
fn many_pattern_many_match() {
let ns = vec!["a", "b"];
let hay = "zbzazzzzb";
let matches = vec![
Match { pati: 1, start: 1, end: 2 },
Match { pati: 0, start: 3, end: 4 },
Match { pati: 1, start: 8, end: 9 },
];
assert_eq!(&aut_find(&ns, hay), &matches);
assert_eq!(&aut_finds(&ns, hay), &matches);
assert_eq!(&aut_findf(&ns, hay), &matches);
assert_eq!(&aut_findfs(&ns, hay), &matches);
}
#[test]
fn many_longer_pattern_one_match() {
let ns = vec!["abc", "xyz"];
let hay = "zazxyzz";
let matches = vec![ Match { pati: 1, start: 3, end: 6 } ];
assert_eq!(&aut_find(&ns, hay), &matches);
assert_eq!(&aut_finds(&ns, hay), &matches);
assert_eq!(&aut_findf(&ns, hay), &matches);
assert_eq!(&aut_findfs(&ns, hay), &matches);
}
#[test]
fn many_longer_pattern_many_match() {
let ns = vec!["abc", "xyz"];
let hay = "zazxyzzzzzazzzabcxyz";
let matches = vec![
Match { pati: 1, start: 3, end: 6 },
Match { pati: 0, start: 14, end: 17 },
Match { pati: 1, start: 17, end: 20 },
];
assert_eq!(&aut_find(&ns, hay), &matches);
assert_eq!(&aut_finds(&ns, hay), &matches);
assert_eq!(&aut_findf(&ns, hay), &matches);
assert_eq!(&aut_findfs(&ns, hay), &matches);
}
#[test]
fn many_longer_pattern_overlap_one_match() {
let ns = vec!["abc", "bc"];
let hay = "zazabcz";
let matches = vec![
Match { pati: 0, start: 3, end: 6 },
Match { pati: 1, start: 4, end: 6 },
];
assert_eq!(&aut_findo(&ns, hay), &matches);
assert_eq!(&aut_findos(&ns, hay), &matches);
assert_eq!(&aut_findfo(&ns, hay), &matches);
assert_eq!(&aut_findfos(&ns, hay), &matches);
}
#[test]
fn many_longer_pattern_overlap_one_match_reverse() {
let ns = vec!["abc", "bc"];
let hay = "xbc";
let matches = vec![ Match { pati: 1, start: 1, end: 3 } ];
assert_eq!(&aut_findo(&ns, hay), &matches);
assert_eq!(&aut_findos(&ns, hay), &matches);
assert_eq!(&aut_findfo(&ns, hay), &matches);
assert_eq!(&aut_findfos(&ns, hay), &matches);
}
#[test]
fn many_longer_pattern_overlap_many_match() {
let ns = vec!["abc", "bc", "c"];
let hay = "zzzabczzzbczzzc";
let matches = vec![
Match { pati: 0, start: 3, end: 6 },
Match { pati: 1, start: 4, end: 6 },
Match { pati: 2, start: 5, end: 6 },
Match { pati: 1, start: 9, end: 11 },
Match { pati: 2, start: 10, end: 11 },
Match { pati: 2, start: 14, end: 15 },
];
assert_eq!(&aut_findo(&ns, hay), &matches);
assert_eq!(&aut_findos(&ns, hay), &matches);
assert_eq!(&aut_findfo(&ns, hay), &matches);
assert_eq!(&aut_findfos(&ns, hay), &matches);
}
#[test]
fn many_longer_pattern_overlap_many_match_reverse() {
let ns = vec!["abc", "bc", "c"];
let hay = "zzzczzzbczzzabc";
let matches = vec![
Match { pati: 2, start: 3, end: 4 },
Match { pati: 1, start: 7, end: 9 },
Match { pati: 2, start: 8, end: 9 },
Match { pati: 0, start: 12, end: 15 },
Match { pati: 1, start: 13, end: 15 },
Match { pati: 2, start: 14, end: 15 },
];
assert_eq!(&aut_findo(&ns, hay), &matches);
assert_eq!(&aut_findos(&ns, hay), &matches);
assert_eq!(&aut_findfo(&ns, hay), &matches);
assert_eq!(&aut_findfos(&ns, hay), &matches);
}
#[test]
fn pattern_returns_original_type() {
let aut = AcAutomaton::new(vec!["apple", "maple"]);
// Explicitly given this type to assert that the thing returned
// from the function is our original type.
let pat: &str = aut.pattern(0);
assert_eq!(pat, "apple");
// Also check the return type of the `patterns` function.
let pats: &[&str] = aut.patterns();
assert_eq!(pats, &["apple", "maple"]);
}
// Quickcheck time.
// This generates very small ascii strings, which makes them more likely
// to interact in interesting ways with larger haystack strings.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct SmallAscii(String);
impl Arbitrary for SmallAscii {
fn arbitrary<G: Gen>(g: &mut G) -> SmallAscii {
use std::char::from_u32;
SmallAscii((0..2)
.map(|_| from_u32(g.gen_range(97, 123)).unwrap())
.collect())
}
fn shrink(&self) -> Box<Iterator<Item=SmallAscii>> {
Box::new(self.0.shrink().map(SmallAscii))
}
}
impl From<SmallAscii> for String {
fn from(s: SmallAscii) -> String { s.0 }
}
impl AsRef<[u8]> for SmallAscii {
fn as_ref(&self) -> &[u8] { self.0.as_ref() }
}
// This is the same arbitrary impl as `String`, except it has a bias toward
// ASCII characters.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct BiasAscii(String);
impl Arbitrary for BiasAscii {
fn arbitrary<G: Gen>(g: &mut G) -> BiasAscii {
use std::char::from_u32;
let size = { let s = g.size(); g.gen_range(0, s) };
let mut s = String::with_capacity(size);
for _ in 0..size {
if g.gen_weighted_bool(3) {
s.push(char::arbitrary(g));
} else {
for _ in 0..5 {
s.push(from_u32(g.gen_range(97, 123)).unwrap());
}
}
}
BiasAscii(s)
}
fn shrink(&self) -> Box<Iterator<Item=BiasAscii>> {
Box::new(self.0.shrink().map(BiasAscii))
}
}
fn naive_find<S>(xs: &[S], haystack: &str) -> Vec<Match>
where S: Clone + Into<String> {
let needles: Vec<String> =
xs.to_vec().into_iter().map(Into::into).collect();
let mut matches = vec![];
for hi in 0..haystack.len() {
for (pati, needle) in needles.iter().enumerate() {
let needle = needle.as_bytes();
if needle.len() == 0 || needle.len() > haystack.len() - hi {
continue;
}
if needle == &haystack.as_bytes()[hi..hi+needle.len()] {
matches.push(Match {
pati: pati,
start: hi,
end: hi + needle.len(),
});
}
}
}
matches
}
#[test]
fn qc_ac_equals_naive() {
fn prop(needles: Vec<SmallAscii>, haystack: BiasAscii) -> bool {
let aut_matches = aut_findo(&needles, &haystack.0);
let naive_matches = naive_find(&needles, &haystack.0);
// Ordering isn't always the same. I don't think we care, so do
// an unordered comparison.
let aset: HashSet<Match> = aut_matches.iter().cloned().collect();
let nset: HashSet<Match> = naive_matches.iter().cloned().collect();
aset == nset
}
quickcheck(prop as fn(Vec<SmallAscii>, BiasAscii) -> bool);
}
}

View File

@ -1,13 +0,0 @@
extern crate memchr;
use std::env;
use lib::AcAutomaton;
#[allow(dead_code)]
mod lib;
fn main() {
let aut = AcAutomaton::new(env::args().skip(1));
println!("{}", aut.dot().trim());
}

View File

@ -1 +0,0 @@
{"files":{"Cargo.toml":"79fbb792e6c1d05c44188c808ef7120c592e50291a706fe0f669b8ac9a2ad5e5","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"0dd882e53de11566d50f8e8e2d5a651bcf3fabee4987d70f306233cf39094ba7","README.md":"b2da2fd61c4f3abf45127d183b61eb2dabc1c97cd191854987aa0328549a663f","benches/benchmarks.rs":"f0469f65f901c3e92fa506c0deb277fd136a7f968cae7cc4f030c0c15e52322f","examples/make_tables.rs":"3c80f2a8cdb204168cc1b60f8904d544b2da067b9e6a7b40ade5fb4a994b4175","src/lib.rs":"a3ac363513ae99a9b0049c19c92bc46b57ac9a0ebfd3317b7b387c5fbaa16a8a","src/tables.rs":"378743892907cde87c1a92e6afee2df36ce590311e61381b2cc0404b3e018039","tests/tests.rs":"dc2c293bae576cc596bdfb6ef783dc1d24d3bf992bf532caaebe1738cb0608cc"},"package":"30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"}

View File

@ -1,21 +0,0 @@
[package]
name = "base64"
version = "0.5.2"
authors = ["Alice Maz <alice@alicemaz.com>", "Marshall Pierce <marshall@mpierce.org>"]
description = "encodes and decodes base64 as bytes or utf8"
repository = "https://github.com/alicemaz/rust-base64"
documentation = "https://github.com/alicemaz/rust-base64/blob/master/README.md"
readme = "README.md"
keywords = ["base64", "utf8", "encode", "decode"]
categories = ["encoding"]
license = "MIT"
[dependencies]
byteorder = "1.0.0"
[dev-dependencies]
rand = "=0.3.15"
[profile.bench]
# Uncomment when using `perf record`
#debug = true

View File

@ -1,21 +0,0 @@
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.

View File

@ -1,112 +0,0 @@
[base64](https://crates.io/crates/base64)
===
It's base64. What more could anyone want?
Example
---
In Cargo.toml: `base64 = "~0.5.0"`
```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()[..]);
}
```
API
---
base64 exposes six functions:
```rust
fn encode<T: ?Sized + AsRef<[u8]>>(&T) -> String;
fn decode<T: ?Sized + AsRef<[u8]>>(&T) -> Result<Vec<u8>, DecodeError>;
fn encode_config<T: ?Sized + AsRef<[u8]>>(&T, Config) -> String;
fn encode_config_buf<T: ?Sized + AsRef<[u8]>>(&T, Config, &mut String);
fn decode_config<T: ?Sized + AsRef<[u8]>>(&T, Config) -> Result<Vec<u8>, DecodeError>;
fn decode_config_buf<T: ?Sized + AsRef<[u8]>>(&T, Config, &mut Vec<u8>) -> Result<(), DecodeError>;
```
`STANDARD`, `URL_SAFE`, `URL_SAFE_NO_PAD`, and `MIME` configuation structs are provided for convenience. `encode` and `decode` are convenience wrappers for the `_config` functions called with the `STANDARD` config, and they are themselves wrappers of the `_buf` functions that allocate on the user's behalf. Encode produces valid padding absent a config that states otherwise; decode produces the same output for valid or omitted padding in all cases, but errors on invalid (superfluous) padding. Whitespace in the input to decode is an error for all modes except `MIME`, which disregards it ("whitespace" according to POSIX-locale `isspace`, meaning \n \r \f \t \v and space).
`Config` exposes a constructor to allow custom combinations of character set, output padding, input whitespace permissiveness, linewrapping, and line ending character(s). The vast majority of usecases should be covered by the four provided, however.
Purpose
---
I have a fondness for small dependency footprints, ecosystems where you can pick and choose what functionality you need, and no more. Unix philosophy sort of thing I guess, many tiny utilities interoperating across a common interface. One time making a Twitter bot, I ran into the need to correctly pluralize arbitrary words. I found on npm a module that did nothing but pluralize words. Nothing else, just a couple of functions. I'd like for this to be that "just a couple of functions."
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. First, enable debug symbols in Cargo.toml. Don't commit this change, though, since it's usually not what you want (and costs some performance):
```
[profile.release]
debug = true
```
Then compile the benchmarks. (Just re-run them and ^C once the benchmarks start running; all that's needed is to recompile them.)
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>
```
License
---
This project is dual-licensed under MIT and Apache 2.0.

View File

@ -1,230 +0,0 @@
#![feature(test)]
extern crate base64;
extern crate test;
extern crate rand;
use base64::{decode, decode_config_buf, encode, encode_config_buf, STANDARD};
use test::Bencher;
use rand::Rng;
#[bench]
fn encode_3b(b: &mut Bencher) {
do_encode_bench(b, 3)
}
#[bench]
fn encode_3b_reuse_buf(b: &mut Bencher) {
do_encode_bench_reuse_buf(b, 3)
}
#[bench]
fn encode_50b(b: &mut Bencher) {
do_encode_bench(b, 50)
}
#[bench]
fn encode_50b_reuse_buf(b: &mut Bencher) {
do_encode_bench_reuse_buf(b, 50)
}
#[bench]
fn encode_100b(b: &mut Bencher) {
do_encode_bench(b, 100)
}
#[bench]
fn encode_100b_reuse_buf(b: &mut Bencher) {
do_encode_bench_reuse_buf(b, 100)
}
#[bench]
fn encode_500b(b: &mut Bencher) {
do_encode_bench(b, 500)
}
#[bench]
fn encode_500b_reuse_buf(b: &mut Bencher) {
do_encode_bench_reuse_buf(b, 500)
}
#[bench]
fn encode_3kib(b: &mut Bencher) {
do_encode_bench(b, 3 * 1024)
}
#[bench]
fn encode_3kib_reuse_buf(b: &mut Bencher) {
do_encode_bench_reuse_buf(b, 3 * 1024)
}
#[bench]
fn encode_3mib(b: &mut Bencher) {
do_encode_bench(b, 3 * 1024 * 1024)
}
#[bench]
fn encode_3mib_reuse_buf(b: &mut Bencher) {
do_encode_bench_reuse_buf(b, 3 * 1024 * 1024)
}
#[bench]
fn encode_10mib(b: &mut Bencher) {
do_encode_bench(b, 10 * 1024 * 1024)
}
#[bench]
fn encode_10mib_reuse_buf(b: &mut Bencher) {
do_encode_bench_reuse_buf(b, 10 * 1024 * 1024)
}
#[bench]
fn encode_30mib(b: &mut Bencher) {
do_encode_bench(b, 30 * 1024 * 1024)
}
#[bench]
fn encode_30mib_reuse_buf(b: &mut Bencher) {
do_encode_bench_reuse_buf(b, 30 * 1024 * 1024)
}
#[bench]
fn decode_3b(b: &mut Bencher) {
do_decode_bench(b, 3)
}
#[bench]
fn decode_3b_reuse_buf(b: &mut Bencher) {
do_decode_bench_reuse_buf(b, 3)
}
#[bench]
fn decode_50b(b: &mut Bencher) {
do_decode_bench(b, 50)
}
#[bench]
fn decode_50b_reuse_buf(b: &mut Bencher) {
do_decode_bench_reuse_buf(b, 50)
}
#[bench]
fn decode_100b(b: &mut Bencher) {
do_decode_bench(b, 100)
}
#[bench]
fn decode_100b_reuse_buf(b: &mut Bencher) {
do_decode_bench_reuse_buf(b, 100)
}
#[bench]
fn decode_500b(b: &mut Bencher) {
do_decode_bench(b, 500)
}
#[bench]
fn decode_500b_reuse_buf(b: &mut Bencher) {
do_decode_bench_reuse_buf(b, 500)
}
#[bench]
fn decode_3kib(b: &mut Bencher) {
do_decode_bench(b, 3 * 1024)
}
#[bench]
fn decode_3kib_reuse_buf(b: &mut Bencher) {
do_decode_bench_reuse_buf(b, 3 * 1024)
}
#[bench]
fn decode_3mib(b: &mut Bencher) {
do_decode_bench(b, 3 * 1024 * 1024)
}
#[bench]
fn decode_3mib_reuse_buf(b: &mut Bencher) {
do_decode_bench_reuse_buf(b, 3 * 1024 * 1024)
}
#[bench]
fn decode_10mib(b: &mut Bencher) {
do_decode_bench(b, 10 * 1024 * 1024)
}
#[bench]
fn decode_10mib_reuse_buf(b: &mut Bencher) {
do_decode_bench_reuse_buf(b, 10 * 1024 * 1024)
}
#[bench]
fn decode_30mib(b: &mut Bencher) {
do_decode_bench(b, 30 * 1024 * 1024)
}
#[bench]
fn decode_30mib_reuse_buf(b: &mut Bencher) {
do_decode_bench_reuse_buf(b, 30 * 1024 * 1024)
}
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.bytes = encoded.len() as u64;
b.iter(|| {
let orig = decode(&encoded);
test::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.bytes = encoded.len() as u64;
b.iter(|| {
decode_config_buf(&encoded, STANDARD, &mut buf).unwrap();
test::black_box(&buf);
buf.clear();
});
}
fn do_encode_bench(b: &mut Bencher, size: usize) {
let mut v: Vec<u8> = Vec::with_capacity(size);
fill(&mut v);
b.bytes = v.len() as u64;
b.iter(|| {
let e = encode(&v);
test::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.bytes = v.len() as u64;
b.iter(|| {
let e = encode_config_buf(&v, STANDARD, &mut buf);
test::black_box(&e);
buf.clear();
});
}
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::weak_rng();
while v.len() < cap {
v.push(r.gen::<u8>());
}
}

View File

@ -1,73 +0,0 @@
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);
}
fn print_encode_table(alphabet: &[u8], const_name: &str, indent_depth: usize) {
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!("{: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);
}

View File

@ -1,675 +0,0 @@
extern crate byteorder;
use std::{fmt, error, str};
use byteorder::{BigEndian, ByteOrder};
mod tables;
/// Available encoding character sets
#[derive(Clone, Copy, Debug)]
pub enum CharacterSet {
/// The standard character set (uses `+` and `/`)
Standard,
/// The URL safe character set (uses `-` and `_`)
UrlSafe
}
#[derive(Clone, Copy, Debug)]
pub enum LineEnding {
LF,
CRLF,
}
#[derive(Clone, Copy, Debug)]
pub enum LineWrap {
NoWrap,
Wrap(usize, LineEnding)
}
/// 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,
/// Remove whitespace before decoding, at the cost of an allocation
strip_whitespace: bool,
/// ADT signifying whether to linewrap output, and if so by how many characters and with what ending
line_wrap: LineWrap,
}
impl Config {
pub fn new(char_set: CharacterSet,
pad: bool,
strip_whitespace: bool,
input_line_wrap: LineWrap) -> Config {
let line_wrap = match input_line_wrap {
LineWrap::Wrap(0, _) => LineWrap::NoWrap,
_ => input_line_wrap,
};
Config {
char_set: char_set,
pad: pad,
strip_whitespace: strip_whitespace,
line_wrap: line_wrap,
}
}
}
pub static STANDARD: Config = Config {
char_set: CharacterSet::Standard,
pad: true,
strip_whitespace: false,
line_wrap: LineWrap::NoWrap,
};
pub static MIME: Config = Config {
char_set: CharacterSet::Standard,
pad: true,
strip_whitespace: true,
line_wrap: LineWrap::Wrap(76, LineEnding::CRLF),
};
pub static URL_SAFE: Config = Config {
char_set: CharacterSet::UrlSafe,
pad: true,
strip_whitespace: false,
line_wrap: LineWrap::NoWrap,
};
pub static URL_SAFE_NO_PAD: Config = Config {
char_set: CharacterSet::UrlSafe,
pad: false,
strip_whitespace: false,
line_wrap: LineWrap::NoWrap,
};
#[derive(Debug, PartialEq, Eq)]
pub enum DecodeError {
InvalidByte(usize, u8),
InvalidLength,
}
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.")
}
}
}
impl error::Error for DecodeError {
fn description(&self) -> &str {
match *self {
DecodeError::InvalidByte(_, _) => "invalid byte",
DecodeError::InvalidLength => "invalid length"
}
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
///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)
}
///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)
}
///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) => String::with_capacity(n),
None => panic!("integer overflow when calculating buffer size")
};
encode_config_buf(input, config, &mut buf);
buf
}
/// calculate the base64 encoded string size, including padding
fn encoded_size(bytes_len: usize, config: Config) -> Option<usize> {
let printing_output_chars = bytes_len
.checked_add(2)
.map(|x| x / 3)
.and_then(|x| x.checked_mul(4));
//TODO this is subtly wrong but in a not dangerous way
//pushing patch with identical to previous behavior, then fixing
let line_ending_output_chars = match config.line_wrap {
LineWrap::NoWrap => Some(0),
LineWrap::Wrap(n, LineEnding::CRLF) =>
printing_output_chars.map(|y| y / n).and_then(|y| y.checked_mul(2)),
LineWrap::Wrap(n, LineEnding::LF) =>
printing_output_chars.map(|y| y / n),
};
printing_output_chars.and_then(|x|
line_ending_output_chars.and_then(|y| x.checked_add(y))
)
}
///Encode arbitrary octets as base64.
///Writes into the supplied buffer to avoid allocations.
///
///# 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 ref charset = match config.char_set {
CharacterSet::Standard => tables::STANDARD_ENCODE,
CharacterSet::UrlSafe => tables::URL_SAFE_ENCODE,
};
// reserve to make sure the memory we'll be writing to with unsafe is allocated
let resv_size = match encoded_size(input_bytes.len(), config) {
Some(n) => n,
None => panic!("integer overflow when calculating buffer size"),
};
buf.reserve(resv_size);
let orig_buf_len = buf.len();
let mut fast_loop_output_buf_len = orig_buf_len;
let input_chunk_len = 6;
let last_fast_index = input_bytes.len().saturating_sub(8);
// we're only going to insert valid utf8
let mut raw = unsafe { buf.as_mut_vec() };
// start at the first free part of the output buf
let mut output_ptr = unsafe { raw.as_mut_ptr().offset(orig_buf_len as isize) };
let mut input_index: usize = 0;
if input_bytes.len() >= 8 {
while input_index <= last_fast_index {
let input_chunk = BigEndian::read_u64(&input_bytes[input_index..(input_index + 8)]);
// strip off 6 bits at a time for the first 6 bytes
unsafe {
std::ptr::write(output_ptr, charset[((input_chunk >> 58) & 0x3F) as usize]);
std::ptr::write(output_ptr.offset(1), charset[((input_chunk >> 52) & 0x3F) as usize]);
std::ptr::write(output_ptr.offset(2), charset[((input_chunk >> 46) & 0x3F) as usize]);
std::ptr::write(output_ptr.offset(3), charset[((input_chunk >> 40) & 0x3F) as usize]);
std::ptr::write(output_ptr.offset(4), charset[((input_chunk >> 34) & 0x3F) as usize]);
std::ptr::write(output_ptr.offset(5), charset[((input_chunk >> 28) & 0x3F) as usize]);
std::ptr::write(output_ptr.offset(6), charset[((input_chunk >> 22) & 0x3F) as usize]);
std::ptr::write(output_ptr.offset(7), charset[((input_chunk >> 16) & 0x3F) as usize]);
output_ptr = output_ptr.offset(8);
}
input_index += input_chunk_len;
fast_loop_output_buf_len += 8;
}
}
unsafe {
// expand len to include the bytes we just wrote
raw.set_len(fast_loop_output_buf_len);
}
// encode the 0 to 7 bytes left after the fast loop
let rem = input_bytes.len() % 3;
let start_of_rem = input_bytes.len() - rem;
// start at the first index not handled by fast loop, which may be 0.
let mut leftover_index = input_index;
while leftover_index < start_of_rem {
raw.push(charset[(input_bytes[leftover_index] >> 2) as usize]);
raw.push(charset[((input_bytes[leftover_index] << 4 | input_bytes[leftover_index + 1] >> 4) & 0x3f) as usize]);
raw.push(charset[((input_bytes[leftover_index + 1] << 2 | input_bytes[leftover_index + 2] >> 6) & 0x3f) as usize]);
raw.push(charset[(input_bytes[leftover_index + 2] & 0x3f) as usize]);
leftover_index += 3;
}
if rem == 2 {
raw.push(charset[(input_bytes[start_of_rem] >> 2) as usize]);
raw.push(charset[((input_bytes[start_of_rem] << 4 | input_bytes[start_of_rem + 1] >> 4) & 0x3f) as usize]);
raw.push(charset[(input_bytes[start_of_rem + 1] << 2 & 0x3f) as usize]);
} else if rem == 1 {
raw.push(charset[(input_bytes[start_of_rem] >> 2) as usize]);
raw.push(charset[(input_bytes[start_of_rem] << 4 & 0x3f) as usize]);
}
if config.pad {
for _ in 0..((3 - rem) % 3) {
raw.push(0x3d);
}
}
//TODO FIXME this does the wrong thing for nonempty buffers
if orig_buf_len == 0 {
if let LineWrap::Wrap(line_size, line_end) = config.line_wrap {
let len = raw.len();
let mut i = 0;
let mut j = 0;
while i < len {
if i > 0 && i % line_size == 0 {
match line_end {
LineEnding::LF => { raw.insert(j, b'\n'); j += 1; }
LineEnding::CRLF => { raw.insert(j, b'\r'); raw.insert(j + 1, b'\n'); j += 2; }
}
}
i += 1;
j += 1;
}
}
}
}
///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 mut input_copy;
let input_bytes = if config.strip_whitespace {
input_copy = Vec::<u8>::with_capacity(input.as_ref().len());
input_copy.extend(input.as_ref().iter().filter(|b| !b" \n\t\r\x0b\x0c".contains(b)));
input_copy.as_ref()
} else {
input.as_ref()
};
let ref decode_table = match config.char_set {
CharacterSet::Standard => tables::STANDARD_DECODE,
CharacterSet::UrlSafe => tables::URL_SAFE_DECODE,
};
buffer.reserve(input_bytes.len() * 3 / 4);
// the fast loop only handles complete chunks of 8 input bytes without padding
let chunk_len = 8;
let decoded_chunk_len = 6;
let remainder_len = input_bytes.len() % chunk_len;
let trailing_bytes_to_skip = if remainder_len == 0 {
// if input is a multiple of the chunk size, ignore the last chunk as it may have padding
chunk_len
} else {
remainder_len
};
let length_of_full_chunks = input_bytes.len().saturating_sub(trailing_bytes_to_skip);
let starting_output_index = buffer.len();
// Resize to hold decoded output from fast loop. Need the extra two bytes because
// we write a full 8 bytes for the last 6-byte decoded chunk and then truncate off two
let new_size = starting_output_index
+ length_of_full_chunks / chunk_len * decoded_chunk_len
+ (chunk_len - decoded_chunk_len);
buffer.resize(new_size, 0);
let mut output_index = starting_output_index;
{
let buffer_slice = buffer.as_mut_slice();
let mut input_index = 0;
// initial value is never used; always set if fast loop breaks
let mut bad_byte_index: usize = 0;
// a non-invalid value means it's not an error if fast loop never runs
let mut morsel: u8 = 0;
// 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];
if morsel == tables::INVALID_VALUE {
bad_byte_index = input_index;
break;
};
accum = (morsel as u64) << 58;
morsel = decode_table[(input_chunk >> 48 & 0xFF) as usize];
if morsel == tables::INVALID_VALUE {
bad_byte_index = input_index + 1;
break;
};
accum |= (morsel as u64) << 52;
morsel = decode_table[(input_chunk >> 40 & 0xFF) as usize];
if morsel == tables::INVALID_VALUE {
bad_byte_index = input_index + 2;
break;
};
accum |= (morsel as u64) << 46;
morsel = decode_table[(input_chunk >> 32 & 0xFF) as usize];
if morsel == tables::INVALID_VALUE {
bad_byte_index = input_index + 3;
break;
};
accum |= (morsel as u64) << 40;
morsel = decode_table[(input_chunk >> 24 & 0xFF) as usize];
if morsel == tables::INVALID_VALUE {
bad_byte_index = input_index + 4;
break;
};
accum |= (morsel as u64) << 34;
morsel = decode_table[(input_chunk >> 16 & 0xFF) as usize];
if morsel == tables::INVALID_VALUE {
bad_byte_index = input_index + 5;
break;
};
accum |= (morsel as u64) << 28;
morsel = decode_table[(input_chunk >> 8 & 0xFF) as usize];
if morsel == tables::INVALID_VALUE {
bad_byte_index = input_index + 6;
break;
};
accum |= (morsel as u64) << 22;
morsel = decode_table[(input_chunk & 0xFF) as usize];
if morsel == tables::INVALID_VALUE {
bad_byte_index = input_index + 7;
break;
};
accum |= (morsel as u64) << 16;
BigEndian::write_u64(&mut buffer_slice[(output_index)..(output_index + 8)],
accum);
output_index += 6;
input_index += chunk_len;
};
if morsel == tables::INVALID_VALUE {
// we got here from a break
return Err(DecodeError::InvalidByte(bad_byte_index, input_bytes[bad_byte_index]));
}
}
// Truncate off the last two bytes from writing the last u64.
// Unconditional because we added on the extra 2 bytes in the resize before the loop,
// so it will never underflow.
let new_len = buffer.len() - (chunk_len - decoded_chunk_len);
buffer.truncate(new_len);
// handle leftovers (at most 8 bytes, decoded to 6).
// Use a u64 as a stack-resident 8 bytes 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;
for (i, b) in input_bytes[length_of_full_chunks..].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.
// TODO InvalidPadding error
return Err(DecodeError::InvalidByte(length_of_full_chunks + i, *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(
length_of_full_chunks + first_padding_index, 0x3D));
};
// 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, cannot overflow from a u8 index
let morsel = decode_table[*b as usize];
if morsel == tables::INVALID_VALUE {
return Err(DecodeError::InvalidByte(length_of_full_chunks + i, *b));
};
leftover_bits |= (morsel as u64) << shift;
morsels_in_leftover += 1;
};
let leftover_bits_ready_to_append = match morsels_in_leftover {
0 => 0,
1 => return Err(DecodeError::InvalidLength),
2 => 8,
3 => 16,
4 => 24,
5 => return Err(DecodeError::InvalidLength),
6 => 32,
7 => 40,
8 => 48,
_ => panic!("Impossible: must only have 0 to 4 input bytes in last quad")
};
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;
buffer.push(selected_bits);
leftover_bits_appended_to_buf += 8;
};
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encoded_size_correct() {
assert_eq!(Some(0), encoded_size(0, STANDARD));
assert_eq!(Some(4), encoded_size(1, STANDARD));
assert_eq!(Some(4), encoded_size(2, STANDARD));
assert_eq!(Some(4), encoded_size(3, STANDARD));
assert_eq!(Some(8), encoded_size(4, STANDARD));
assert_eq!(Some(8), encoded_size(5, STANDARD));
assert_eq!(Some(8), encoded_size(6, STANDARD));
assert_eq!(Some(12), encoded_size(7, STANDARD));
assert_eq!(Some(12), encoded_size(8, STANDARD));
assert_eq!(Some(12), encoded_size(9, STANDARD));
assert_eq!(Some(72), encoded_size(54, STANDARD));
assert_eq!(Some(76), encoded_size(55, STANDARD));
assert_eq!(Some(76), encoded_size(56, STANDARD));
assert_eq!(Some(76), encoded_size(57, STANDARD));
assert_eq!(Some(80), encoded_size(58, STANDARD));
}
#[test]
fn encoded_size_correct_mime() {
assert_eq!(Some(0), encoded_size(0, MIME));
assert_eq!(Some(4), encoded_size(1, MIME));
assert_eq!(Some(4), encoded_size(2, MIME));
assert_eq!(Some(4), encoded_size(3, MIME));
assert_eq!(Some(8), encoded_size(4, MIME));
assert_eq!(Some(8), encoded_size(5, MIME));
assert_eq!(Some(8), encoded_size(6, MIME));
assert_eq!(Some(12), encoded_size(7, MIME));
assert_eq!(Some(12), encoded_size(8, MIME));
assert_eq!(Some(12), encoded_size(9, MIME));
assert_eq!(Some(72), encoded_size(54, MIME));
assert_eq!(Some(78), encoded_size(55, MIME));
assert_eq!(Some(78), encoded_size(56, MIME));
assert_eq!(Some(78), encoded_size(57, MIME));
assert_eq!(Some(82), encoded_size(58, MIME));
}
#[test]
fn encoded_size_correct_lf() {
let config = Config::new(
CharacterSet::Standard,
true,
false,
LineWrap::Wrap(76, LineEnding::LF)
);
assert_eq!(Some(0), encoded_size(0, config));
assert_eq!(Some(4), encoded_size(1, config));
assert_eq!(Some(4), encoded_size(2, config));
assert_eq!(Some(4), encoded_size(3, config));
assert_eq!(Some(8), encoded_size(4, config));
assert_eq!(Some(8), encoded_size(5, config));
assert_eq!(Some(8), encoded_size(6, config));
assert_eq!(Some(12), encoded_size(7, config));
assert_eq!(Some(12), encoded_size(8, config));
assert_eq!(Some(12), encoded_size(9, config));
assert_eq!(Some(72), encoded_size(54, config));
assert_eq!(Some(77), encoded_size(55, config));
assert_eq!(Some(77), encoded_size(56, config));
assert_eq!(Some(77), encoded_size(57, config));
assert_eq!(Some(81), encoded_size(58, config));
}
#[test]
fn encoded_size_overflow() {
assert_eq!(None, encoded_size(std::usize::MAX, STANDARD));
}
}

View File

@ -1,649 +0,0 @@
pub const INVALID_VALUE: u8 = 255;
pub const STANDARD_ENCODE: &'static [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)
];
pub const STANDARD_DECODE: &'static [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)
];
pub const URL_SAFE_ENCODE: &'static [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)
];
pub const URL_SAFE_DECODE: &'static [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)
];

View File

@ -1,696 +0,0 @@
extern crate base64;
extern crate rand;
use rand::Rng;
use base64::*;
fn compare_encode(expected: &str, target: &[u8]) {
assert_eq!(expected, encode(target));
}
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());
}
fn compare_decode_mime(expected: &str, target: &str) {
assert_eq!(expected, String::from_utf8(decode_config(target, MIME).unwrap()).unwrap());
}
fn push_rand(buf: &mut Vec<u8>, len: usize) {
let mut r = rand::weak_rng();
for _ in 0..len {
buf.push(r.gen::<u8>());
}
}
// generate every possible byte string recursively and test encode/decode roundtrip
fn roundtrip_append_recurse(byte_buf: &mut Vec<u8>, str_buf: &mut String, remaining_bytes: usize) {
let orig_length = byte_buf.len();
for b in 0..256 {
byte_buf.push(b as u8);
if remaining_bytes > 1 {
roundtrip_append_recurse(byte_buf, str_buf, remaining_bytes - 1)
} else {
encode_config_buf(&byte_buf, STANDARD, str_buf);
let roundtrip_bytes = decode_config(&str_buf, STANDARD).unwrap();
assert_eq!(*byte_buf, roundtrip_bytes);
str_buf.clear();
}
byte_buf.truncate(orig_length);
}
}
// generate every possible byte string recursively and test encode/decode roundtrip with
// padding removed
fn roundtrip_append_recurse_strip_padding(byte_buf: &mut Vec<u8>, str_buf: &mut String,
remaining_bytes: usize) {
let orig_length = byte_buf.len();
for b in 0..256 {
byte_buf.push(b as u8);
if remaining_bytes > 1 {
roundtrip_append_recurse_strip_padding(byte_buf, str_buf, remaining_bytes - 1)
} else {
encode_config_buf(&byte_buf, STANDARD, str_buf);
{
let trimmed = str_buf.trim_right_matches('=');
let roundtrip_bytes = decode_config(&trimmed, STANDARD).unwrap();
assert_eq!(*byte_buf, roundtrip_bytes);
}
str_buf.clear();
}
byte_buf.truncate(orig_length);
}
}
// generate random contents of the specified length and test encode/decode roundtrip
fn roundtrip_random(byte_buf: &mut Vec<u8>, str_buf: &mut String, byte_len: usize,
approx_values_per_byte: u8, max_rounds: u64) {
let num_rounds = calculate_number_of_rounds(byte_len, approx_values_per_byte, max_rounds);
let mut r = rand::weak_rng();
for _ in 0..num_rounds {
byte_buf.clear();
str_buf.clear();
while byte_buf.len() < byte_len {
byte_buf.push(r.gen::<u8>());
}
encode_config_buf(&byte_buf, STANDARD, str_buf);
let roundtrip_bytes = decode_config(&str_buf, STANDARD).unwrap();
assert_eq!(*byte_buf, roundtrip_bytes);
}
}
// generate random contents of the specified length and test encode/decode roundtrip
fn roundtrip_random_strip_padding(byte_buf: &mut Vec<u8>, str_buf: &mut String, 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::weak_rng();
for _ in 0..num_rounds {
byte_buf.clear();
str_buf.clear();
while byte_buf.len() < byte_len {
byte_buf.push(r.gen::<u8>());
}
encode_config_buf(&byte_buf, STANDARD, str_buf);
let trimmed = str_buf.trim_right_matches('=');
let roundtrip_bytes = decode_config(&trimmed, STANDARD).unwrap();
assert_eq!(*byte_buf, roundtrip_bytes);
}
}
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);
}
return prod;
}
//-------
//decode
#[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");
}
//this is a MAY in the rfc: https://tools.ietf.org/html/rfc4648#section-3.3
#[test]
fn decode_pad_inside_fast_loop_chunk_error() {
// can't PartialEq Base64Error, so we do this the hard way
match decode("YWxpY2U=====").unwrap_err() {
DecodeError::InvalidByte(offset, byte) => {
// since the first 8 bytes are handled in the fast loop, the
// padding is an error. Could argue that the *next* padding
// byte is technically the first erroneous one, but reporting
// that accurately is more complex and probably nobody cares
assert_eq!(7, offset);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
}
}
#[test]
fn decode_extra_pad_after_fast_loop_chunk_error() {
match decode("YWxpY2UABB===").unwrap_err() {
DecodeError::InvalidByte(offset, byte) => {
// extraneous third padding byte
assert_eq!(12, offset);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
};
}
//same
#[test]
fn decode_absurd_pad_error() {
match decode("==Y=Wx===pY=2U=====").unwrap_err() {
DecodeError::InvalidByte(size, byte) => {
assert_eq!(0, size);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
}
}
#[test]
fn decode_starts_with_padding_single_quad_error() {
match decode("====").unwrap_err() {
DecodeError::InvalidByte(offset, byte) => {
// with no real input, first padding byte is bogus
assert_eq!(0, offset);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
}
}
#[test]
fn decode_extra_padding_in_trailing_quad_returns_error() {
match decode("zzz==").unwrap_err() {
DecodeError::InvalidByte(size, byte) => {
// first unneeded padding byte
assert_eq!(4, size);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
}
}
#[test]
fn decode_extra_padding_in_trailing_quad_2_returns_error() {
match decode("zz===").unwrap_err() {
DecodeError::InvalidByte(size, byte) => {
// first unneeded padding byte
assert_eq!(4, size);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
}
}
#[test]
fn decode_start_second_quad_with_padding_returns_error() {
match decode("zzzz=").unwrap_err() {
DecodeError::InvalidByte(size, byte) => {
// first unneeded padding byte
assert_eq!(4, size);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
}
}
#[test]
fn decode_padding_in_last_quad_followed_by_non_padding_returns_error() {
match decode("zzzz==z").unwrap_err() {
DecodeError::InvalidByte(size, byte) => {
assert_eq!(4, size);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
}
}
#[test]
fn decode_too_short_with_padding_error() {
match decode("z==").unwrap_err() {
DecodeError::InvalidByte(size, byte) => {
// first unneeded padding byte
assert_eq!(1, size);
assert_eq!(0x3D, byte);
}
_ => assert!(false)
}
}
#[test]
fn decode_too_short_without_padding_error() {
match decode("z").unwrap_err() {
DecodeError::InvalidLength => {}
_ => assert!(false)
}
}
#[test]
fn decode_too_short_second_quad_without_padding_error() {
match decode("zzzzX").unwrap_err() {
DecodeError::InvalidLength => {}
_ => assert!(false)
}
}
#[test]
fn decode_error_for_bogus_char_in_right_position() {
for length in 1..25 {
for error_position in 0_usize..length {
let prefix: String = std::iter::repeat("A").take(error_position).collect();
let suffix: String = std::iter::repeat("B").take(length - error_position - 1).collect();
let input = prefix + "%" + &suffix;
assert_eq!(length, input.len(),
"length {} error position {}", length, error_position);
match decode(&input).unwrap_err() {
DecodeError::InvalidByte(size, byte) => {
assert_eq!(error_position, size,
"length {} error position {}", length, error_position);
assert_eq!(0x25, byte);
}
_ => assert!(false)
}
}
}
}
#[test]
fn decode_into_nonempty_buffer_doesnt_clobber_existing_contents() {
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();
for encoded_length in 0_usize..26 {
if encoded_length % 4 == 1 {
// can't have a lone byte in a quad of input
continue;
};
let raw_data_byte_triples = encoded_length / 4;
// 4 base64 bytes -> 3 input bytes, 3 -> 2, 2 -> 1, 0 -> 0
let raw_data_byte_leftovers = (encoded_length % 4).saturating_sub(1);
// we'll borrow buf to make some data to encode
orig_data.clear();
push_rand(&mut orig_data, raw_data_byte_triples * 3 + raw_data_byte_leftovers);
encoded_data.clear();
encode_config_buf(&orig_data, STANDARD, &mut encoded_data);
assert_eq!(encoded_length, encoded_data.trim_right_matches('=').len());
for prefix_length in 1..26 {
decoded_with_prefix.clear();
decoded_without_prefix.clear();
prefix.clear();
// fill the buf with a prefix
push_rand(&mut prefix, prefix_length);
decoded_with_prefix.resize(prefix_length, 0);
decoded_with_prefix.copy_from_slice(&prefix);
// decode into the non-empty buf
decode_config_buf(&encoded_data, STANDARD, &mut decoded_with_prefix).unwrap();
// also decode into the empty buf
decode_config_buf(&encoded_data, STANDARD, &mut decoded_without_prefix).unwrap();
assert_eq!(prefix_length + decoded_without_prefix.len(), decoded_with_prefix.len());
// append plain decode onto prefix
prefix.append(&mut decoded_without_prefix);
assert_eq!(prefix, decoded_with_prefix);
}
}
}
#[test]
fn roundtrip_random_no_fast_loop() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
for input_len in 0..9 {
roundtrip_random(&mut byte_buf, &mut str_buf, input_len, 4, 10000);
}
}
#[test]
fn roundtrip_random_with_fast_loop() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
for input_len in 9..26 {
roundtrip_random(&mut byte_buf, &mut str_buf, input_len, 4, 100000);
}
}
#[test]
fn roundtrip_random_no_fast_loop_no_padding() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
for input_len in 0..9 {
roundtrip_random_strip_padding(&mut byte_buf, &mut str_buf, input_len, 4, 10000);
}
}
#[test]
fn roundtrip_random_with_fast_loop_no_padding() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
for input_len in 9..26 {
roundtrip_random_strip_padding(&mut byte_buf, &mut str_buf, input_len, 4, 100000);
}
}
#[test]
fn roundtrip_all_1_byte() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
roundtrip_append_recurse(&mut byte_buf, &mut str_buf, 1);
}
#[test]
fn roundtrip_all_1_byte_no_padding() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
roundtrip_append_recurse_strip_padding(&mut byte_buf, &mut str_buf, 1);
}
#[test]
fn roundtrip_all_2_byte() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
roundtrip_append_recurse(&mut byte_buf, &mut str_buf, 2);
}
#[test]
fn roundtrip_all_2_byte_no_padding() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
roundtrip_append_recurse_strip_padding(&mut byte_buf, &mut str_buf, 2);
}
#[test]
fn roundtrip_all_3_byte() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
roundtrip_append_recurse(&mut byte_buf, &mut str_buf, 3);
}
#[test]
fn roundtrip_random_4_byte() {
let mut byte_buf: Vec<u8> = Vec::new();
let mut str_buf = String::new();
roundtrip_random(&mut byte_buf, &mut str_buf, 4, 48, 10000);
}
//TODO like, write a thing to test every ascii val lol
//prolly just yankput the 64 array and a 256 one later
//is there a way to like, not have to write a fn every time
//"hi test harness this should panic 192 times" would be nice
//oh well whatever this is better done by a fuzzer
//strip yr whitespace kids
#[test]
#[should_panic]
fn decode_reject_space() {
assert!(decode("YWx pY2U=").is_ok());
}
#[test]
#[should_panic]
fn decode_reject_tab() {
assert!(decode("YWx\tpY2U=").is_ok());
}
#[test]
#[should_panic]
fn decode_reject_ff() {
assert!(decode("YWx\x0cpY2U=").is_ok());
}
#[test]
#[should_panic]
fn decode_reject_vtab() {
assert!(decode("YWx\x0bpY2U=").is_ok());
}
#[test]
#[should_panic]
fn decode_reject_nl() {
assert!(decode("YWx\npY2U=").is_ok());
}
#[test]
#[should_panic]
fn decode_reject_crnl() {
assert!(decode("YWx\r\npY2U=").is_ok());
}
#[test]
#[should_panic]
fn decode_reject_null() {
assert!(decode("YWx\0pY2U=").is_ok());
}
#[test]
fn decode_mime_allow_space() {
assert!(decode_config("YWx pY2U=", MIME).is_ok());
}
#[test]
fn decode_mime_allow_tab() {
assert!(decode_config("YWx\tpY2U=", MIME).is_ok());
}
#[test]
fn decode_mime_allow_ff() {
assert!(decode_config("YWx\x0cpY2U=", MIME).is_ok());
}
#[test]
fn decode_mime_allow_vtab() {
assert!(decode_config("YWx\x0bpY2U=", MIME).is_ok());
}
#[test]
fn decode_mime_allow_nl() {
assert!(decode_config("YWx\npY2U=", MIME).is_ok());
}
#[test]
fn decode_mime_allow_crnl() {
assert!(decode_config("YWx\r\npY2U=", MIME).is_ok());
}
#[test]
#[should_panic]
fn decode_mime_reject_null() {
assert!(decode_config("YWx\0pY2U=", MIME).is_ok());
}
#[test]
fn decode_mime_absurd_whitespace() {
compare_decode_mime("how could you let this happen",
"\n aG93I\n\nG\x0bNvd\r\nWxkI HlvdSB \tsZXQgdGh\rpcyBo\x0cYXBwZW4 = ");
}
//-------
//encode
#[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("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+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("AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+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-jp6uvs7e7v8PHy8_T19vf4-fr7_P3-_w==", encode_config(&bytes, URL_SAFE));
}
#[test]
fn encode_into_nonempty_buffer_doesnt_clobber_existing_contents() {
let mut orig_data = Vec::new();
let mut encoded_with_prefix = String::new();
let mut encoded_without_prefix = String::new();
let mut prefix = String::new();
for orig_data_length in 0_usize..26 {
// we'll borrow buf to make some data to encode
orig_data.clear();
push_rand(&mut orig_data, orig_data_length);
for prefix_length in 1..26 {
encoded_with_prefix.clear();
encoded_without_prefix.clear();
prefix.clear();
for _ in 0..prefix_length {
prefix.push('~');
}
encoded_with_prefix.push_str(&prefix);
// encode into the non-empty buf
encode_config_buf(&orig_data, STANDARD, &mut encoded_with_prefix);
// also encode into the empty buf
encode_config_buf(&orig_data, STANDARD, &mut encoded_without_prefix);
assert_eq!(prefix_length + encoded_without_prefix.len(), encoded_with_prefix.len());
// append plain decode onto prefix
prefix.push_str(&mut encoded_without_prefix);
assert_eq!(prefix, encoded_with_prefix);
}
}
}
#[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_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");
}

File diff suppressed because one or more lines are too long

View File

@ -1,26 +1,32 @@
[package]
# 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 = "bzip2-sys"
version = "0.1.5"
version = "0.1.6"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
links = "bzip2"
build = "build.rs"
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/bzip2-rs"
links = "bzip2"
description = "Bindings to libbzip2 for bzip2 compression and decompression exposed as\nReader/Writer streams.\n"
homepage = "https://github.com/alexcrichton/bzip2-rs"
documentation = "https://docs.rs/bzip2-sys"
description = """
Bindings to libbzip2 for bzip2 compression and decompression exposed as
Reader/Writer streams.
"""
categories = ["external-ffi-bindings"]
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/bzip2-rs"
[lib]
name = "bzip2_sys"
path = "lib.rs"
[dependencies]
libc = "0.2"
[build-dependencies]
gcc = "0.3"
[dependencies.libc]
version = "0.2"
[build-dependencies.cc]
version = "1.0"

View File

@ -1,9 +1,10 @@
extern crate gcc;
extern crate cc;
use std::env;
fn main() {
let mut cfg = gcc::Config::new();
let mut cfg = cc::Build::new();
cfg.warnings(false);
if env::var("TARGET").unwrap().contains("windows") {
cfg.define("_WIN32", None);

View File

@ -48,7 +48,7 @@ macro_rules! abi_compat {
extern "system" {
$(pub fn $name($($arg: $t),*) -> $ret;)*
}
#[cfg(unix)]
#[cfg(any(unix, target_os = "redox"))]
extern {
$(pub fn $name($($arg: $t),*) -> $ret;)*
}

View File

@ -0,0 +1 @@
{"files":{".travis.yml":"3ca887c3f17391bd915265fbb6015b403263fbb13b9dedc8ea9ab8aa5c5ef163","Cargo.toml":"abe25a5fc35c2fe24c2f93b00f9488886310a3b7a25975984cc7030e826bb2e5","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"d73ba5602263665e2d3987db4378787f6aa1e449b699a2530cee478acc7586dc","appveyor.yml":"ab45bfdcf2596f357225a54e730c34d518a8f3ad56c2ed33af682cfd45bddc02","src/bin/gcc-shim.rs":"d6be9137cb48b86891e7b263adbf492e1193ffe682db9ba4a88eb1079b874b58","src/com.rs":"0cb06f5db0fb70d27db0e5917ca337de6e7032119e6aabfea1bad9c719f5f34b","src/lib.rs":"164a360f504c7614be1f51dd9c76ef84bf8195e94ea5f1a914a90c1a036db6b8","src/registry.rs":"3876ef9573e3bbc050aef41a684b9a510cc1a91b15ae874fe032cf4377b4d116","src/setup_config.rs":"1a3eeb11c6847c31f2a4685b62ab35c76f0b6d5a17f7ed99e9df164283a771f7","src/winapi.rs":"cb5e6cab3eb570b0f97c660ca448ccfb5024262c0c7b245c181daad91a79f211","src/windows_registry.rs":"6de548aa94215e449f0e58e9a3b1702939d7c2f7b63a9040901c948bf138201d","tests/cc_env.rs":"7402315eea7ffa23b29b393c1de8e236294ede9de562ff0a562704a157135341","tests/support/mod.rs":"092551f9f6e3a999fa0aa02f93314aac0bda2b09268f948c423df56a43575e0b","tests/test.rs":"b1164258714e13173f3861126e97bedf1e29aa24618993c4eb0edd57c431dcc7"},"package":"a9b13a57efd6b30ecd6598ebdb302cca617930b5470647570468a65d12ef9719"}

View File

@ -6,7 +6,7 @@ rust:
matrix:
include:
# Minimum version supported
- rust: 1.6.0
- rust: 1.13.0
install:
script: cargo build
@ -14,17 +14,16 @@ sudo: false
install:
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then OS=unknown-linux-gnu; else OS=apple-darwin; fi
- export TARGET=$ARCH-$OS
- curl https://static.rust-lang.org/rustup.sh |
sh -s -- --add-target=$TARGET --disable-sudo -y --prefix=`rustc --print sysroot`
- if [ -z "$NO_ADD" ]; then rustup target add $TARGET; fi
before_script:
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
script:
- cargo build --verbose
- cargo test --verbose
- cargo test --verbose --features parallel
- cargo test --manifest-path gcc-test/Cargo.toml --target $TARGET
- cargo test --manifest-path gcc-test/Cargo.toml --target $TARGET --features parallel
- cargo test --manifest-path gcc-test/Cargo.toml --target $TARGET --release
- cargo test --manifest-path cc-test/Cargo.toml --target $TARGET
- cargo test --manifest-path cc-test/Cargo.toml --target $TARGET --features parallel
- cargo test --manifest-path cc-test/Cargo.toml --target $TARGET --release
- cargo doc
- cargo clean && cargo build
- rustdoc --test README.md -L target/debug -L target/debug/deps
@ -35,7 +34,7 @@ env:
secure: "CBtqrudgE0PS8x3kTr44jKbC2D4nfnmdYVecooNm0qnER4B4TSvZpZSQoCgKK6k4BYQuOSyFTOwYx6M79w39ZMOgyCP9ytB+tyMWL0/+ZuUQL04yVg4M5vd3oJMkOaXbvG56ncgPyFrseY+FPDg+mXAzvJk/nily37YXjkQj2D0="
matrix:
- ARCH=x86_64
- ARCH=x86_64 NO_ADD=1
- ARCH=i686
notifications:
email:

37
third_party/rust/cc/Cargo.toml vendored Normal file
View File

@ -0,0 +1,37 @@
# 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 = "cc"
version = "1.0.3"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
description = "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n"
homepage = "https://github.com/alexcrichton/cc-rs"
documentation = "https://docs.rs/cc"
readme = "README.md"
keywords = ["build-dependencies"]
categories = ["development-tools"]
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/cc-rs"
[dependencies.rayon]
version = "0.8"
optional = true
[dev-dependencies.tempdir]
version = "0.3"
[features]
parallel = ["rayon"]
[badges.travis-ci]
repository = "alexcrichton/cc-rs"
[badges.appveyor]
repository = "alexcrichton/cc-rs"

View File

@ -1,30 +1,27 @@
# gcc-rs
# cc-rs
A library to compile C/C++ code into a Rust library/application.
A library to compile C/C++/assembly into a Rust library/application.
[![Build Status](https://travis-ci.org/alexcrichton/gcc-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/gcc-rs)
[![Build status](https://ci.appveyor.com/api/projects/status/onu270iw98h81nwv?svg=true)](https://ci.appveyor.com/project/alexcrichton/gcc-rs)
[![Build Status](https://travis-ci.org/alexcrichton/cc-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/cc-rs)
[![Build status](https://ci.appveyor.com/api/projects/status/onu270iw98h81nwv?svg=true)](https://ci.appveyor.com/project/alexcrichton/cc-rs)
[Documentation](https://docs.rs/gcc)
[Documentation](https://docs.rs/cc)
A simple library meant to be used as a build dependency with Cargo packages in
order to build a set of C/C++ files into a static archive. Note that while this
crate is called "gcc", it actually calls out to the most relevant compile for
a platform, for example using `cl` on MSVC. That is, this crate does indeed work
on MSVC!
order to build a set of C/C++ files into a static archive. This crate calls out
to the most relevant compiler for a platform, for example using `cl` on MSVC.
## Using gcc-rs
> **Note**: this crate was recently renamed from the `gcc` crate, so if you're
> looking for the `gcc` crate you're in the right spot!
## Using cc-rs
First, you'll want to both add a build script for your crate (`build.rs`) and
also add this crate to your `Cargo.toml` via:
```toml
[package]
# ...
build = "build.rs"
[build-dependencies]
gcc = "0.3"
cc = "1.0"
```
Next up, you'll want to write a build script like so:
@ -32,16 +29,20 @@ Next up, you'll want to write a build script like so:
```rust,no_run
// build.rs
extern crate gcc;
extern crate cc;
fn main() {
gcc::compile_library("libfoo.a", &["foo.c", "bar.c"]);
cc::Build::new()
.file("foo.c")
.file("bar.c")
.compile("foo");
}
```
And that's it! Running `cargo build` should take care of the rest and your Rust
application will now have the C files `foo.c` and `bar.c` compiled into it. You
can call the functions in Rust by declaring functions in your Rust code like so:
application will now have the C files `foo.c` and `bar.c` compiled into a file
named libfoo.a. You can call the functions in Rust by declaring functions in
your Rust code like so:
```
extern {
@ -66,7 +67,7 @@ fn main() {
To control the programs and flags used for building, the builder can set a
number of different environment variables.
* `CFLAGS` - a series of space separated flags passed to "gcc". Note that
* `CFLAGS` - a series of space separated flags passed to compilers. Note that
individual flags cannot currently contain spaces, so doing
something like: "-L=foo\ bar" is not possible.
* `CC` - the actual C compiler used. Note that this is used as an exact
@ -85,9 +86,9 @@ in the following prioritized order:
3. `<build-kind>_<var>` - for example, `HOST_CC` or `TARGET_CFLAGS`
4. `<var>` - a plain `CC`, `AR` as above.
If none of these variables exist, gcc-rs uses built-in defaults
If none of these variables exist, cc-rs uses built-in defaults
In addition to the the above optional environment variables, `gcc-rs` has some
In addition to the the above optional environment variables, `cc-rs` has some
functions with hard requirements on some variables supplied by [cargo's
build-script driver][cargo] that it has the `TARGET`, `OUT_DIR`, `OPT_LEVEL`,
and `HOST` variables.
@ -96,17 +97,21 @@ and `HOST` variables.
## Optional features
Currently gcc-rs supports parallel compilation (think `make -jN`) but this
feature is turned off by default. To enable gcc-rs to compile C/C++ in parallel,
### Parallel
Currently cc-rs supports parallel compilation (think `make -jN`) but this
feature is turned off by default. To enable cc-rs to compile C/C++ in parallel,
you can change your dependency to:
```toml
[build-dependencies]
gcc = { version = "0.3", features = ["parallel"] }
cc = { version = "1.0", features = ["parallel"] }
```
By default gcc-rs will limit parallelism to `$NUM_JOBS`, or if not present it
will limit it to the number of cpus on the machine.
By default cc-rs will limit parallelism to `$NUM_JOBS`, or if not present it
will limit it to the number of cpus on the machine. If you are using cargo,
use `-jN` option of `build`, `test` and `run` commands as `$NUM_JOBS`
is supplied by cargo.
## Compile-time Requirements
@ -115,13 +120,13 @@ is being run. This crate does not ship a C compiler with it. The compiler
required varies per platform, but there are three broad categories:
* Unix platforms require `cc` to be the C compiler. This can be found by
installing gcc/clang on Linux distributions and Xcode on OSX, for example.
installing cc/clang on Linux distributions and Xcode on OSX, for example.
* Windows platforms targeting MSVC (e.g. your target triple ends in `-msvc`)
require `cl.exe` to be available and in `PATH`. This is typically found in
standard Visual Studio installations and the `PATH` can be set up by running
the appropriate developer tools shell.
* Windows platforms targeting MinGW (e.g. your target triple ends in `-gnu`)
require `gcc` to be available in `PATH`. We recommend the
require `cc` to be available in `PATH`. We recommend the
[MinGW-w64](http://mingw-w64.org) distribution, which is using the
[Win-builds](http://win-builds.org) installation system.
You may also acquire it via
@ -134,14 +139,14 @@ required varies per platform, but there are three broad categories:
## C++ support
`gcc-rs` supports C++ libraries compilation by using the `cpp` method on
`Config`:
`cc-rs` supports C++ libraries compilation by using the `cpp` method on
`Build`:
```rust,no_run
extern crate gcc;
extern crate cc;
fn main() {
gcc::Config::new()
cc::Build::new()
.cpp(true) // Switch to C++ library compilation.
.file("foo.cpp")
.compile("libfoo.a");
@ -154,7 +159,7 @@ linked to the crate target.
## License
`gcc-rs` is primarily distributed under the terms of both the MIT license and
`cc-rs` is primarily distributed under the terms of both the MIT license and
the Apache License (Version 2.0), with portions covered by various BSD-like
licenses.

55
third_party/rust/cc/appveyor.yml vendored Normal file
View File

@ -0,0 +1,55 @@
environment:
# At the time this was added AppVeyor was having troubles with checking
# revocation of SSL certificates of sites like static.rust-lang.org and what
# we think is crates.io. The libcurl HTTP client by default checks for
# revocation on Windows and according to a mailing list [1] this can be
# disabled.
#
# The `CARGO_HTTP_CHECK_REVOKE` env var here tells cargo to disable SSL
# revocation checking on Windows in libcurl. Note, though, that rustup, which
# we're using to download Rust here, also uses libcurl as the default backend.
# Unlike Cargo, however, rustup doesn't have a mechanism to disable revocation
# checking. To get rustup working we set `RUSTUP_USE_HYPER` which forces it to
# use the Hyper instead of libcurl backend. Both Hyper and libcurl use
# schannel on Windows but it appears that Hyper configures it slightly
# differently such that revocation checking isn't turned on by default.
#
# [1]: https://curl.haxx.se/mail/lib-2016-03/0202.html
RUSTUP_USE_HYPER: 1
CARGO_HTTP_CHECK_REVOKE: false
matrix:
- TARGET: x86_64-pc-windows-msvc
ARCH: amd64
VS: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat
- TARGET: x86_64-pc-windows-msvc
ARCH: amd64
VS: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
- TARGET: i686-pc-windows-msvc
ARCH: x86
VS: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\vcvarsall.bat
- TARGET: i686-pc-windows-msvc
ARCH: x86
VS: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
- TARGET: x86_64-pc-windows-gnu
MSYS_BITS: 64
- TARGET: i686-pc-windows-gnu
MSYS_BITS: 32
install:
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- if defined VS call "%VS%" %ARCH%
- set PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- if defined MSYS_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS_BITS%\bin
- rustc -V
- cargo -V
build: false
test_script:
- cargo test --target %TARGET%
- cargo test --features parallel --target %TARGET%
- cargo test --manifest-path cc-test/Cargo.toml --target %TARGET%
- cargo test --manifest-path cc-test/Cargo.toml --features parallel --target %TARGET%
- cargo test --manifest-path cc-test/Cargo.toml --release --target %TARGET%

125
third_party/rust/cc/src/com.rs vendored Normal file
View File

@ -0,0 +1,125 @@
// Copyright © 2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
#![allow(unused)]
use std::ffi::{OsStr, OsString};
use std::mem::forget;
use std::ops::Deref;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::ptr::null_mut;
use std::slice::from_raw_parts;
use winapi::Interface;
use winapi::BSTR;
use winapi::CoInitializeEx;
use winapi::COINIT_MULTITHREADED;
use winapi::{SysFreeString, SysStringLen};
use winapi::IUnknown;
use winapi::{S_OK, S_FALSE, HRESULT};
pub fn initialize() -> Result<(), HRESULT> {
let err = unsafe { CoInitializeEx(null_mut(), COINIT_MULTITHREADED) };
if err != S_OK && err != S_FALSE {
// S_FALSE just means COM is already initialized
return Err(err);
}
Ok(())
}
pub struct ComPtr<T>(*mut T) where T: Interface;
impl<T> ComPtr<T> where T: Interface {
/// Creates a `ComPtr` to wrap a raw pointer.
/// It takes ownership over the pointer which means it does __not__ call `AddRef`.
/// `T` __must__ be a COM interface that inherits from `IUnknown`.
pub unsafe fn from_raw(ptr: *mut T) -> ComPtr<T> {
assert!(!ptr.is_null());
ComPtr(ptr)
}
/// Casts up the inheritance chain
pub fn up<U>(self) -> ComPtr<U> where T: Deref<Target=U>, U: Interface {
ComPtr(self.into_raw() as *mut U)
}
/// Extracts the raw pointer.
/// You are now responsible for releasing it yourself.
pub fn into_raw(self) -> *mut T {
let p = self.0;
forget(self);
p
}
/// For internal use only.
fn as_unknown(&self) -> &IUnknown {
unsafe { &*(self.0 as *mut IUnknown) }
}
/// Performs QueryInterface fun.
pub fn cast<U>(&self) -> Result<ComPtr<U>, i32> where U: Interface {
let mut obj = null_mut();
let err = unsafe { self.as_unknown().QueryInterface(&U::uuidof(), &mut obj) };
if err < 0 { return Err(err); }
Ok(unsafe { ComPtr::from_raw(obj as *mut U) })
}
}
impl<T> Deref for ComPtr<T> where T: Interface {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.0 }
}
}
impl<T> Clone for ComPtr<T> where T: Interface {
fn clone(&self) -> Self {
unsafe {
self.as_unknown().AddRef();
ComPtr::from_raw(self.0)
}
}
}
impl<T> Drop for ComPtr<T> where T: Interface {
fn drop(&mut self) {
unsafe { self.as_unknown().Release(); }
}
}
pub struct BStr(BSTR);
impl BStr {
pub unsafe fn from_raw(s: BSTR) -> BStr {
BStr(s)
}
pub fn to_osstring(&self) -> OsString {
let len = unsafe { SysStringLen(self.0) };
let slice = unsafe { from_raw_parts(self.0, len as usize) };
OsStringExt::from_wide(slice)
}
}
impl Drop for BStr {
fn drop(&mut self) {
unsafe { SysFreeString(self.0) };
}
}
pub trait ToWide {
fn to_wide(&self) -> Vec<u16>;
fn to_wide_null(&self) -> Vec<u16>;
}
impl<T> ToWide for T where T: AsRef<OsStr> {
fn to_wide(&self) -> Vec<u16> {
self.as_ref().encode_wide().collect()
}
fn to_wide_null(&self) -> Vec<u16> {
self.as_ref().encode_wide().chain(Some(0)).collect()
}
}
pub trait FromWide where Self: Sized {
fn from_wide(wide: &[u16]) -> Self;
fn from_wide_null(wide: &[u16]) -> Self {
let len = wide.iter().take_while(|&&c| c != 0).count();
Self::from_wide(&wide[..len])
}
}
impl FromWide for OsString {
fn from_wide(wide: &[u16]) -> OsString {
OsStringExt::from_wide(wide)
}
}

1888
third_party/rust/cc/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

257
third_party/rust/cc/src/setup_config.rs vendored Normal file
View File

@ -0,0 +1,257 @@
// Copyright © 2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
#![allow(bad_style)]
#![allow(unused)]
use std::ffi::OsString;
use std::ptr::null_mut;
use winapi::Interface;
use winapi::{LPFILETIME, ULONG};
use winapi::S_FALSE;
use winapi::BSTR;
use winapi::LPCOLESTR;
use winapi::{CLSCTX_ALL, CoCreateInstance};
use winapi::LPSAFEARRAY;
use winapi::{IUnknown, IUnknownVtbl};
use winapi::{HRESULT, LCID, LPCWSTR, PULONGLONG};
use com::{BStr, ComPtr};
// Bindings to the Setup.Configuration stuff
pub type InstanceState = u32;
pub const eNone: InstanceState = 0;
pub const eLocal: InstanceState = 1;
pub const eRegistered: InstanceState = 2;
pub const eNoRebootRequired: InstanceState = 4;
pub const eComplete: InstanceState = -1i32 as u32;
RIDL!{#[uuid(0xb41463c3, 0x8866, 0x43b5, 0xbc, 0x33, 0x2b, 0x06, 0x76, 0xf7, 0xf4, 0x2e)]
interface ISetupInstance(ISetupInstanceVtbl): IUnknown(IUnknownVtbl) {
fn GetInstanceId(
pbstrInstanceId: *mut BSTR,
) -> HRESULT,
fn GetInstallDate(
pInstallDate: LPFILETIME,
) -> HRESULT,
fn GetInstallationName(
pbstrInstallationName: *mut BSTR,
) -> HRESULT,
fn GetInstallationPath(
pbstrInstallationPath: *mut BSTR,
) -> HRESULT,
fn GetInstallationVersion(
pbstrInstallationVersion: *mut BSTR,
) -> HRESULT,
fn GetDisplayName(
lcid: LCID,
pbstrDisplayName: *mut BSTR,
) -> HRESULT,
fn GetDescription(
lcid: LCID,
pbstrDescription: *mut BSTR,
) -> HRESULT,
fn ResolvePath(
pwszRelativePath: LPCOLESTR,
pbstrAbsolutePath: *mut BSTR,
) -> HRESULT,
}}
RIDL!{#[uuid(0x89143c9a, 0x05af, 0x49b0, 0xb7, 0x17, 0x72, 0xe2, 0x18, 0xa2, 0x18, 0x5c)]
interface ISetupInstance2(ISetupInstance2Vtbl): ISetupInstance(ISetupInstanceVtbl) {
fn GetState(
pState: *mut InstanceState,
) -> HRESULT,
fn GetPackages(
ppsaPackages: *mut LPSAFEARRAY,
) -> HRESULT,
fn GetProduct(
ppPackage: *mut *mut ISetupPackageReference,
) -> HRESULT,
fn GetProductPath(
pbstrProductPath: *mut BSTR,
) -> HRESULT,
}}
RIDL!{#[uuid(0x6380bcff, 0x41d3, 0x4b2e, 0x8b, 0x2e, 0xbf, 0x8a, 0x68, 0x10, 0xc8, 0x48)]
interface IEnumSetupInstances(IEnumSetupInstancesVtbl): IUnknown(IUnknownVtbl) {
fn Next(
celt: ULONG,
rgelt: *mut *mut ISetupInstance,
pceltFetched: *mut ULONG,
) -> HRESULT,
fn Skip(
celt: ULONG,
) -> HRESULT,
fn Reset() -> HRESULT,
fn Clone(
ppenum: *mut *mut IEnumSetupInstances,
) -> HRESULT,
}}
RIDL!{#[uuid(0x42843719, 0xdb4c, 0x46c2, 0x8e, 0x7c, 0x64, 0xf1, 0x81, 0x6e, 0xfd, 0x5b)]
interface ISetupConfiguration(ISetupConfigurationVtbl): IUnknown(IUnknownVtbl) {
fn EnumInstances(
ppEnumInstances: *mut *mut IEnumSetupInstances,
) -> HRESULT,
fn GetInstanceForCurrentProcess(
ppInstance: *mut *mut ISetupInstance,
) -> HRESULT,
fn GetInstanceForPath(
wzPath: LPCWSTR,
ppInstance: *mut *mut ISetupInstance,
) -> HRESULT,
}}
RIDL!{#[uuid(0x26aab78c, 0x4a60, 0x49d6, 0xaf, 0x3b, 0x3c, 0x35, 0xbc, 0x93, 0x36, 0x5d)]
interface ISetupConfiguration2(ISetupConfiguration2Vtbl):
ISetupConfiguration(ISetupConfigurationVtbl) {
fn EnumAllInstances(
ppEnumInstances: *mut *mut IEnumSetupInstances,
) -> HRESULT,
}}
RIDL!{#[uuid(0xda8d8a16, 0xb2b6, 0x4487, 0xa2, 0xf1, 0x59, 0x4c, 0xcc, 0xcd, 0x6b, 0xf5)]
interface ISetupPackageReference(ISetupPackageReferenceVtbl): IUnknown(IUnknownVtbl) {
fn GetId(
pbstrId: *mut BSTR,
) -> HRESULT,
fn GetVersion(
pbstrVersion: *mut BSTR,
) -> HRESULT,
fn GetChip(
pbstrChip: *mut BSTR,
) -> HRESULT,
fn GetLanguage(
pbstrLanguage: *mut BSTR,
) -> HRESULT,
fn GetBranch(
pbstrBranch: *mut BSTR,
) -> HRESULT,
fn GetType(
pbstrType: *mut BSTR,
) -> HRESULT,
fn GetUniqueId(
pbstrUniqueId: *mut BSTR,
) -> HRESULT,
}}
RIDL!{#[uuid(0x42b21b78, 0x6192, 0x463e, 0x87, 0xbf, 0xd5, 0x77, 0x83, 0x8f, 0x1d, 0x5c)]
interface ISetupHelper(ISetupHelperVtbl): IUnknown(IUnknownVtbl) {
fn ParseVersion(
pwszVersion: LPCOLESTR,
pullVersion: PULONGLONG,
) -> HRESULT,
fn ParseVersionRange(
pwszVersionRange: LPCOLESTR,
pullMinVersion: PULONGLONG,
pullMaxVersion: PULONGLONG,
) -> HRESULT,
}}
DEFINE_GUID!{CLSID_SetupConfiguration,
0x177f0c4a, 0x1cd3, 0x4de7, 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d}
// Safe wrapper around the COM interfaces
pub struct SetupConfiguration(ComPtr<ISetupConfiguration>);
impl SetupConfiguration {
pub fn new() -> Result<SetupConfiguration, i32> {
let mut obj = null_mut();
let err = unsafe { CoCreateInstance(
&CLSID_SetupConfiguration, null_mut(), CLSCTX_ALL,
&ISetupConfiguration::uuidof(), &mut obj,
) };
if err < 0 { return Err(err); }
let obj = unsafe { ComPtr::from_raw(obj as *mut ISetupConfiguration) };
Ok(SetupConfiguration(obj))
}
pub fn get_instance_for_current_process(&self) -> Result<SetupInstance, i32> {
let mut obj = null_mut();
let err = unsafe { self.0.GetInstanceForCurrentProcess(&mut obj) };
if err < 0 { return Err(err); }
Ok(unsafe { SetupInstance::from_raw(obj) })
}
pub fn enum_instances(&self) -> Result<EnumSetupInstances, i32> {
let mut obj = null_mut();
let err = unsafe { self.0.EnumInstances(&mut obj) };
if err < 0 { return Err(err); }
Ok(unsafe { EnumSetupInstances::from_raw(obj) })
}
pub fn enum_all_instances(&self) -> Result<EnumSetupInstances, i32> {
let mut obj = null_mut();
let this = try!(self.0.cast::<ISetupConfiguration2>());
let err = unsafe { this.EnumAllInstances(&mut obj) };
if err < 0 { return Err(err); }
Ok(unsafe { EnumSetupInstances::from_raw(obj) })
}
}
pub struct SetupInstance(ComPtr<ISetupInstance>);
impl SetupInstance {
pub unsafe fn from_raw(obj: *mut ISetupInstance) -> SetupInstance {
SetupInstance(ComPtr::from_raw(obj))
}
pub fn instance_id(&self) -> Result<OsString, i32> {
let mut s = null_mut();
let err = unsafe { self.0.GetInstanceId(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); }
Ok(bstr.to_osstring())
}
pub fn installation_name(&self) -> Result<OsString, i32> {
let mut s = null_mut();
let err = unsafe { self.0.GetInstallationName(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); }
Ok(bstr.to_osstring())
}
pub fn installation_path(&self) -> Result<OsString, i32> {
let mut s = null_mut();
let err = unsafe { self.0.GetInstallationPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); }
Ok(bstr.to_osstring())
}
pub fn installation_version(&self) -> Result<OsString, i32> {
let mut s = null_mut();
let err = unsafe { self.0.GetInstallationVersion(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); }
Ok(bstr.to_osstring())
}
pub fn product_path(&self) -> Result<OsString, i32> {
let mut s = null_mut();
let this = try!(self.0.cast::<ISetupInstance2>());
let err = unsafe { this.GetProductPath(&mut s) };
let bstr = unsafe { BStr::from_raw(s) };
if err < 0 { return Err(err); }
Ok(bstr.to_osstring())
}
}
pub struct EnumSetupInstances(ComPtr<IEnumSetupInstances>);
impl EnumSetupInstances {
pub unsafe fn from_raw(obj: *mut IEnumSetupInstances) -> EnumSetupInstances {
EnumSetupInstances(ComPtr::from_raw(obj))
}
}
impl Iterator for EnumSetupInstances {
type Item = Result<SetupInstance, i32>;
fn next(&mut self) -> Option<Result<SetupInstance, i32>> {
let mut obj = null_mut();
let err = unsafe { self.0.Next(1, &mut obj, null_mut()) };
if err < 0 { return Some(Err(err)); }
if err == S_FALSE { return None; }
Some(Ok(unsafe { SetupInstance::from_raw(obj) }))
}
}

214
third_party/rust/cc/src/winapi.rs vendored Normal file
View File

@ -0,0 +1,214 @@
// Copyright © 2015-2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
#![allow(bad_style)]
use std::os::raw;
pub type wchar_t = u16;
pub type UINT = raw::c_uint;
pub type LPUNKNOWN = *mut IUnknown;
pub type REFIID = *const IID;
pub type IID = GUID;
pub type REFCLSID = *const IID;
pub type PVOID = *mut raw::c_void;
pub type USHORT = raw::c_ushort;
pub type ULONG = raw::c_ulong;
pub type LONG = raw::c_long;
pub type DWORD = u32;
pub type LPVOID = *mut raw::c_void;
pub type HRESULT = raw::c_long;
pub type LPFILETIME = *mut FILETIME;
pub type BSTR = *mut OLECHAR;
pub type OLECHAR = WCHAR;
pub type WCHAR = wchar_t;
pub type LPCOLESTR = *const OLECHAR;
pub type LCID = DWORD;
pub type LPCWSTR = *const WCHAR;
pub type PULONGLONG = *mut ULONGLONG;
pub type ULONGLONG = u64;
pub const S_OK: HRESULT = 0;
pub const S_FALSE: HRESULT = 1;
pub const COINIT_MULTITHREADED: u32 = 0x0;
pub type CLSCTX = u32;
pub const CLSCTX_INPROC_SERVER: CLSCTX = 0x1;
pub const CLSCTX_INPROC_HANDLER: CLSCTX = 0x2;
pub const CLSCTX_LOCAL_SERVER: CLSCTX = 0x4;
pub const CLSCTX_REMOTE_SERVER: CLSCTX = 0x10;
pub const CLSCTX_ALL: CLSCTX = CLSCTX_INPROC_SERVER |
CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER;
#[repr(C)]
#[derive(Copy, Clone)]
pub struct GUID {
pub Data1: raw::c_ulong,
pub Data2: raw::c_ushort,
pub Data3: raw::c_ushort,
pub Data4: [raw::c_uchar; 8],
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct FILETIME {
pub dwLowDateTime: DWORD,
pub dwHighDateTime: DWORD,
}
pub trait Interface {
fn uuidof() -> GUID;
}
#[link(name = "ole32")]
#[link(name = "oleaut32")]
extern { }
extern "system" {
pub fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) -> HRESULT;
pub fn CoCreateInstance(rclsid: REFCLSID, pUnkOuter: LPUNKNOWN,
dwClsContext: DWORD, riid: REFIID,
ppv: *mut LPVOID) -> HRESULT;
pub fn SysFreeString(bstrString: BSTR);
pub fn SysStringLen(pbstr: BSTR) -> UINT;
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct SAFEARRAYBOUND {
pub cElements: ULONG,
pub lLbound: LONG,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct SAFEARRAY {
pub cDims: USHORT,
pub fFeatures: USHORT,
pub cbElements: ULONG,
pub cLocks: ULONG,
pub pvData: PVOID,
pub rgsabound: [SAFEARRAYBOUND; 1],
}
pub type LPSAFEARRAY = *mut SAFEARRAY;
macro_rules! DEFINE_GUID {
(
$name:ident, $l:expr, $w1:expr, $w2:expr,
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
) => {
pub const $name: $crate::winapi::GUID = $crate::winapi::GUID {
Data1: $l,
Data2: $w1,
Data3: $w2,
Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
};
}
}
macro_rules! RIDL {
(#[uuid($($uuid:expr),+)]
interface $interface:ident ($vtbl:ident) {$(
fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
)+}) => (
#[repr(C)]
pub struct $vtbl {
$(pub $method: unsafe extern "system" fn(
This: *mut $interface,
$($p: $t),*
) -> $rtr,)+
}
#[repr(C)]
pub struct $interface {
pub lpVtbl: *const $vtbl,
}
RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}}
RIDL!{@uuid $interface $($uuid),+}
);
(#[uuid($($uuid:expr),+)]
interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {
}) => (
#[repr(C)]
pub struct $vtbl {
pub parent: $pvtbl,
}
#[repr(C)]
pub struct $interface {
pub lpVtbl: *const $vtbl,
}
RIDL!{@deref $interface $pinterface}
RIDL!{@uuid $interface $($uuid),+}
);
(#[uuid($($uuid:expr),+)]
interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) {$(
fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
)+}) => (
#[repr(C)]
pub struct $vtbl {
pub parent: $pvtbl,
$(pub $method: unsafe extern "system" fn(
This: *mut $interface,
$($p: $t,)*
) -> $rtr,)+
}
#[repr(C)]
pub struct $interface {
pub lpVtbl: *const $vtbl,
}
RIDL!{@impl $interface {$(fn $method($($p: $t,)*) -> $rtr,)+}}
RIDL!{@deref $interface $pinterface}
RIDL!{@uuid $interface $($uuid),+}
);
(@deref $interface:ident $pinterface:ident) => (
impl ::std::ops::Deref for $interface {
type Target = $pinterface;
#[inline]
fn deref(&self) -> &$pinterface {
unsafe { &*(self as *const $interface as *const $pinterface) }
}
}
);
(@impl $interface:ident {$(
fn $method:ident($($p:ident : $t:ty,)*) -> $rtr:ty,
)+}) => (
impl $interface {
$(#[inline] pub unsafe fn $method(&self, $($p: $t,)*) -> $rtr {
((*self.lpVtbl).$method)(self as *const _ as *mut _, $($p,)*)
})+
}
);
(@uuid $interface:ident
$l:expr, $w1:expr, $w2:expr,
$b1:expr, $b2:expr, $b3:expr, $b4:expr, $b5:expr, $b6:expr, $b7:expr, $b8:expr
) => (
impl $crate::winapi::Interface for $interface {
#[inline]
fn uuidof() -> $crate::winapi::GUID {
$crate::winapi::GUID {
Data1: $l,
Data2: $w1,
Data3: $w2,
Data4: [$b1, $b2, $b3, $b4, $b5, $b6, $b7, $b8],
}
}
}
);
}
RIDL!{#[uuid(0x00000000, 0x0000, 0x0000, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46)]
interface IUnknown(IUnknownVtbl) {
fn QueryInterface(
riid: REFIID,
ppvObject: *mut *mut raw::c_void,
) -> HRESULT,
fn AddRef() -> ULONG,
fn Release() -> ULONG,
}}

View File

@ -15,6 +15,7 @@ use std::process::Command;
use Tool;
#[cfg(windows)]
macro_rules! otry {
($expr:expr) => (match $expr {
Some(val) => val,
@ -50,10 +51,121 @@ pub fn find_tool(_target: &str, _tool: &str) -> Option<Tool> {
#[cfg(windows)]
pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
use std::env;
// This logic is all tailored for MSVC, if we're not that then bail out
// early.
if !target.contains("msvc") {
return None;
}
// Looks like msbuild isn't located in the same location as other tools like
// cl.exe and lib.exe. To handle this we probe for it manually with
// dedicated registry keys.
if tool.contains("msbuild") {
return impl_::find_msbuild(target);
}
// If VCINSTALLDIR is set, then someone's probably already run vcvars and we
// should just find whatever that indicates.
if env::var_os("VCINSTALLDIR").is_some() {
return env::var_os("PATH")
.and_then(|path| env::split_paths(&path).map(|p| p.join(tool)).find(|p| p.exists()))
.map(|path| Tool::new(path.into()));
}
// Ok, if we're here, now comes the fun part of the probing. Default shells
// or shells like MSYS aren't really configured to execute `cl.exe` and the
// various compiler tools shipped as part of Visual Studio. Here we try to
// first find the relevant tool, then we also have to be sure to fill in
// environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
// the tool is actually usable.
return impl_::find_msvc_15(tool, target)
.or_else(|| impl_::find_msvc_14(tool, target))
.or_else(|| impl_::find_msvc_12(tool, target))
.or_else(|| impl_::find_msvc_11(tool, target));
}
/// A version of Visual Studio
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum VsVers {
/// Visual Studio 12 (2013)
Vs12,
/// Visual Studio 14 (2015)
Vs14,
/// Visual Studio 15 (2017)
Vs15,
/// Hidden variant that should not be matched on. Callers that want to
/// handle an enumeration of `VsVers` instances should always have a default
/// case meaning that it's a VS version they don't understand.
#[doc(hidden)]
#[allow(bad_style)]
__Nonexhaustive_do_not_match_this_or_your_code_will_break,
}
/// Find the most recent installed version of Visual Studio
///
/// This is used by the cmake crate to figure out the correct
/// generator.
#[cfg(not(windows))]
pub fn find_vs_version() -> Result<VsVers, String> {
Err(format!("not windows"))
}
/// Documented above
#[cfg(windows)]
pub fn find_vs_version() -> Result<VsVers, String> {
use std::env;
match env::var("VisualStudioVersion") {
Ok(version) => {
match &version[..] {
"15.0" => Ok(VsVers::Vs15),
"14.0" => Ok(VsVers::Vs14),
"12.0" => Ok(VsVers::Vs12),
vers => Err(format!("\n\n\
unsupported or unknown VisualStudio version: {}\n\
if another version is installed consider running \
the appropriate vcvars script before building this \
crate\n\
", vers)),
}
}
_ => {
// Check for the presense of a specific registry key
// that indicates visual studio is installed.
if impl_::has_msbuild_version("15.0") {
Ok(VsVers::Vs15)
} else if impl_::has_msbuild_version("14.0") {
Ok(VsVers::Vs14)
} else if impl_::has_msbuild_version("12.0") {
Ok(VsVers::Vs12)
} else {
Err(format!("\n\n\
couldn't determine visual studio generator\n\
if VisualStudio is installed, however, consider \
running the appropriate vcvars script before building \
this crate\n\
"))
}
}
}
}
#[cfg(windows)]
mod impl_ {
use std::env;
use std::ffi::OsString;
use std::mem;
use std::path::{Path, PathBuf};
use std::fs::File;
use std::io::Read;
use registry::{RegistryKey, LOCAL_MACHINE};
use com;
use setup_config::{SetupConfiguration, SetupInstance};
use Tool;
struct MsvcTool {
tool: PathBuf,
@ -82,47 +194,102 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
}
}
// This logic is all tailored for MSVC, if we're not that then bail out
// early.
if !target.contains("msvc") {
return None;
// In MSVC 15 (2017) MS once again changed the scheme for locating
// the tooling. Now we must go through some COM interfaces, which
// is super fun for Rust.
//
// Note that much of this logic can be found [online] wrt paths, COM, etc.
//
// [online]: https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/
pub fn find_msvc_15(tool: &str, target: &str) -> Option<Tool> {
otry!(com::initialize().ok());
let config = otry!(SetupConfiguration::new().ok());
let iter = otry!(config.enum_all_instances().ok());
for instance in iter {
let instance = otry!(instance.ok());
let tool = tool_from_vs15_instance(tool, target, &instance);
if tool.is_some() {
return tool;
}
}
None
}
// Looks like msbuild isn't located in the same location as other tools like
// cl.exe and lib.exe. To handle this we probe for it manually with
// dedicated registry keys.
if tool.contains("msbuild") {
return find_msbuild(target);
fn tool_from_vs15_instance(tool: &str, target: &str,
instance: &SetupInstance) -> Option<Tool> {
let (bin_path, host_dylib_path, lib_path, include_path) = otry!(vs15_vc_paths(target, instance));
let tool_path = bin_path.join(tool);
if !tool_path.exists() { return None };
let mut tool = MsvcTool::new(tool_path);
tool.path.push(host_dylib_path);
tool.libs.push(lib_path);
tool.include.push(include_path);
if let Some((atl_lib_path, atl_include_path)) = atl_paths(target, &bin_path) {
tool.libs.push(atl_lib_path);
tool.include.push(atl_include_path);
}
otry!(add_sdks(&mut tool, target));
Some(tool.into_tool())
}
// If VCINSTALLDIR is set, then someone's probably already run vcvars and we
// should just find whatever that indicates.
if env::var_os("VCINSTALLDIR").is_some() {
return env::var_os("PATH")
.and_then(|path| env::split_paths(&path).map(|p| p.join(tool)).find(|p| p.exists()))
.map(|path| Tool::new(path.into()));
fn vs15_vc_paths(target: &str, instance: &SetupInstance) -> Option<(PathBuf, PathBuf, PathBuf, PathBuf)> {
let instance_path: PathBuf = otry!(instance.installation_path().ok()).into();
let version_path = instance_path.join(r"VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt");
let mut version_file = otry!(File::open(version_path).ok());
let mut version = String::new();
otry!(version_file.read_to_string(&mut version).ok());
let version = version.trim();
let host = match host_arch() {
X86 => "X86",
X86_64 => "X64",
_ => return None,
};
let target = otry!(lib_subdir(target));
// The directory layout here is MSVC/bin/Host$host/$target/
let path = instance_path.join(r"VC\Tools\MSVC").join(version);
// This is the path to the toolchain for a particular target, running
// on a given host
let bin_path = path.join("bin").join(&format!("Host{}", host)).join(&target);
// But! we also need PATH to contain the target directory for the host
// architecture, because it contains dlls like mspdb140.dll compiled for
// the host architecture.
let host_dylib_path = path.join("bin").join(&format!("Host{}", host)).join(&host.to_lowercase());
let lib_path = path.join("lib").join(&target);
let include_path = path.join("include");
Some((bin_path, host_dylib_path, lib_path, include_path))
}
// Ok, if we're here, now comes the fun part of the probing. Default shells
// or shells like MSYS aren't really configured to execute `cl.exe` and the
// various compiler tools shipped as part of Visual Studio. Here we try to
// first find the relevant tool, then we also have to be sure to fill in
// environment variables like `LIB`, `INCLUDE`, and `PATH` to ensure that
// the tool is actually usable.
fn atl_paths(target: &str, path: &Path) -> Option<(PathBuf, PathBuf)> {
let atl_path = path.join("atlfmc");
let sub = otry!(lib_subdir(target));
if atl_path.exists() {
Some((atl_path.join("lib").join(sub), atl_path.join("include")))
} else {
None
}
}
return find_msvc_latest(tool, target, "15.0")
.or_else(|| find_msvc_latest(tool, target, "14.0"))
.or_else(|| find_msvc_12(tool, target))
.or_else(|| find_msvc_11(tool, target));
// For MSVC 14 or newer we need to find the Universal CRT as well as either
// For MSVC 14 we need to find the Universal CRT as well as either
// the Windows 10 SDK or Windows 8.1 SDK.
fn find_msvc_latest(tool: &str, target: &str, ver: &str) -> Option<Tool> {
let vcdir = otry!(get_vc_dir(ver));
pub fn find_msvc_14(tool: &str, target: &str) -> Option<Tool> {
let vcdir = otry!(get_vc_dir("14.0"));
let mut tool = otry!(get_tool(tool, &vcdir, target));
otry!(add_sdks(&mut tool, target));
Some(tool.into_tool())
}
fn add_sdks(tool: &mut MsvcTool, target: &str) -> Option<()> {
let sub = otry!(lib_subdir(target));
let (ucrt, ucrt_version) = otry!(get_ucrt_dir());
tool.path.push(ucrt.join("bin").join(&ucrt_version).join(sub));
let ucrt_include = ucrt.join("include").join(&ucrt_version);
tool.include.push(ucrt_include.join("ucrt"));
@ -145,14 +312,13 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
tool.include.push(sdk_include.join("um"));
tool.include.push(sdk_include.join("winrt"));
tool.include.push(sdk_include.join("shared"));
} else {
return None;
}
Some(tool.into_tool())
Some(())
}
// For MSVC 12 we need to find the Windows 8.1 SDK.
fn find_msvc_12(tool: &str, target: &str) -> Option<Tool> {
pub fn find_msvc_12(tool: &str, target: &str) -> Option<Tool> {
let vcdir = otry!(get_vc_dir("12.0"));
let mut tool = otry!(get_tool(tool, &vcdir, target));
let sub = otry!(lib_subdir(target));
@ -168,7 +334,7 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
}
// For MSVC 11 we need to find the Windows 8 SDK.
fn find_msvc_11(tool: &str, target: &str) -> Option<Tool> {
pub fn find_msvc_11(tool: &str, target: &str) -> Option<Tool> {
let vcdir = otry!(get_vc_dir("11.0"));
let mut tool = otry!(get_tool(tool, &vcdir, target));
let sub = otry!(lib_subdir(target));
@ -403,8 +569,52 @@ pub fn find_tool(target: &str, tool: &str) -> Option<Tool> {
max_key
}
pub fn has_msbuild_version(version: &str) -> bool {
match version {
"15.0" => {
find_msbuild_vs15("x86_64-pc-windows-msvc").is_some() ||
find_msbuild_vs15("i686-pc-windows-msvc").is_some()
}
"12.0" | "14.0" => {
LOCAL_MACHINE.open(
&OsString::from(format!("SOFTWARE\\Microsoft\\MSBuild\\ToolsVersions\\{}",
version))).is_ok()
}
_ => false
}
}
// see http://stackoverflow.com/questions/328017/path-to-msbuild
fn find_msbuild(target: &str) -> Option<Tool> {
pub fn find_msbuild(target: &str) -> Option<Tool> {
// VS 15 (2017) changed how to locate msbuild
if let Some(r) = find_msbuild_vs15(target) {
return Some(r);
} else {
find_old_msbuild(target)
}
}
fn find_msbuild_vs15(target: &str) -> Option<Tool> {
// Seems like this could also go through SetupConfiguration,
// or that find_msvc_15 could just use this registry key
// instead of the COM interface.
let key = r"SOFTWARE\WOW6432Node\Microsoft\VisualStudio\SxS\VS7";
LOCAL_MACHINE.open(key.as_ref())
.ok()
.and_then(|key| {
key.query_str("15.0").ok()
})
.map(|path| {
let path = PathBuf::from(path).join(r"MSBuild\15.0\Bin\MSBuild.exe");
let mut tool = Tool::new(path);
if target.contains("x86_64") {
tool.env.push(("Platform".into(), "X64".into()));
}
tool
})
}
fn find_old_msbuild(target: &str) -> Option<Tool> {
let key = r"SOFTWARE\Microsoft\MSBuild\ToolsVersions";
LOCAL_MACHINE.open(key.as_ref())
.ok()

73
third_party/rust/cc/tests/cc_env.rs vendored Normal file
View File

@ -0,0 +1,73 @@
extern crate cc;
extern crate tempdir;
use std::env;
use std::path::Path;
use std::ffi::OsString;
mod support;
use support::Test;
#[test]
fn main() {
ccache();
distcc();
ccache_spaces();
ccache_env_flags();
}
fn ccache() {
let test = Test::gnu();
env::set_var("CC", "ccache cc");
let compiler = test.gcc().file("foo.c").get_compiler();
assert_eq!(compiler.path(), Path::new("cc"));
}
fn ccache_spaces() {
let test = Test::gnu();
test.shim("ccache");
env::set_var("CC", "ccache cc");
let compiler = test.gcc().file("foo.c").get_compiler();
assert_eq!(compiler.path(), Path::new("cc"));
}
fn distcc() {
let test = Test::gnu();
test.shim("distcc");
env::set_var("CC", "distcc cc");
let compiler = test.gcc().file("foo.c").get_compiler();
assert_eq!(compiler.path(), Path::new("cc"));
}
fn ccache_env_flags() {
let test = Test::gnu();
test.shim("ccache");
env::set_var("CC", "ccache lol-this-is-not-a-compiler");
let compiler = test.gcc().file("foo.c").get_compiler();
assert_eq!(compiler.path(), Path::new("lol-this-is-not-a-compiler"));
assert_eq!(
compiler.cc_env(),
OsString::from("ccache lol-this-is-not-a-compiler")
);
assert!(
compiler
.cflags_env()
.into_string()
.unwrap()
.contains("ccache") == false
);
assert!(
compiler
.cflags_env()
.into_string()
.unwrap()
.contains(" lol-this-is-not-a-compiler") == false
);
env::set_var("CC", "");
}

View File

@ -6,7 +6,7 @@ use std::fs::{self, File};
use std::io::prelude::*;
use std::path::PathBuf;
use gcc;
use cc;
use tempdir::TempDir;
pub struct Test {
@ -36,7 +36,7 @@ impl Test {
pub fn gnu() -> Test {
let t = Test::new();
t.shim("cc").shim("ar");
t.shim("cc").shim("c++").shim("ar");
t
}
@ -55,8 +55,8 @@ impl Test {
self
}
pub fn gcc(&self) -> gcc::Config {
let mut cfg = gcc::Config::new();
pub fn gcc(&self) -> cc::Build {
let mut cfg = cc::Build::new();
let mut path = env::split_paths(&env::var_os("PATH").unwrap()).collect::<Vec<_>>();
path.insert(0, self.td.path().to_owned());
let target = if self.msvc {
@ -109,4 +109,14 @@ impl Execution {
pub fn has(&self, p: &OsStr) -> bool {
self.args.iter().any(|arg| OsStr::new(arg) == p)
}
pub fn must_have_in_order(&self, before: &str, after: &str) -> &Execution {
let before_position = self.args.iter().rposition(|x| OsStr::new(x) == OsStr::new(before));
let after_position = self.args.iter().rposition(|x| OsStr::new(x) == OsStr::new(after));
match (before_position, after_position) {
(Some(b), Some(a)) if b < a => {},
(b, a) => { panic!("{:?} (last position: {:?}) did not appear before {:?} (last position: {:?})", before, b, after, a) },
};
self
}
}

View File

@ -1,4 +1,4 @@
extern crate gcc;
extern crate cc;
extern crate tempdir;
use support::Test;
@ -10,7 +10,7 @@ fn gnu_smoke() {
let test = Test::gnu();
test.gcc()
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0)
.must_have("-O2")
@ -28,7 +28,7 @@ fn gnu_opt_level_1() {
test.gcc()
.opt_level(1)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0)
.must_have("-O1")
@ -41,7 +41,7 @@ fn gnu_opt_level_s() {
test.gcc()
.opt_level_str("s")
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0)
.must_have("-Os")
@ -57,10 +57,46 @@ fn gnu_debug() {
test.gcc()
.debug(true)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_have("-g");
}
#[test]
fn gnu_warnings_into_errors() {
let test = Test::gnu();
test.gcc()
.warnings_into_errors(true)
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("-Werror");
}
#[test]
fn gnu_warnings() {
let test = Test::gnu();
test.gcc()
.warnings(true)
.flag("-Wno-missing-field-initializers")
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("-Wall")
.must_have("-Wextra");
}
#[test]
fn gnu_warnings_overridable() {
let test = Test::gnu();
test.gcc()
.warnings(true)
.flag("-Wno-missing-field-initializers")
.file("foo.c")
.compile("foo");
test.cmd(0).must_have_in_order("-Wall", "-Wno-missing-field-initializers");
}
#[test]
fn gnu_x86_64() {
for vendor in &["unknown-linux-gnu", "apple-darwin"] {
@ -70,7 +106,7 @@ fn gnu_x86_64() {
.target(&target)
.host(&target)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0)
.must_have("-fPIC")
@ -88,7 +124,7 @@ fn gnu_x86_64_no_pic() {
.target(&target)
.host(&target)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_not_have("-fPIC");
}
@ -103,10 +139,9 @@ fn gnu_i686() {
.target(&target)
.host(&target)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0)
.must_not_have("-fPIC")
.must_have("-m32");
}
}
@ -121,7 +156,7 @@ fn gnu_i686_pic() {
.target(&target)
.host(&target)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_have("-fPIC");
}
@ -133,7 +168,7 @@ fn gnu_set_stdlib() {
test.gcc()
.cpp_set_stdlib(Some("foo"))
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_not_have("-stdlib=foo");
}
@ -144,7 +179,7 @@ fn gnu_include() {
test.gcc()
.include("foo/bar")
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_have("-I").must_have("foo/bar");
}
@ -153,10 +188,10 @@ fn gnu_include() {
fn gnu_define() {
let test = Test::gnu();
test.gcc()
.define("FOO", Some("bar"))
.define("FOO", "bar")
.define("BAR", None)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_have("-DFOO=bar").must_have("-DBAR");
}
@ -166,22 +201,86 @@ fn gnu_compile_assembly() {
let test = Test::gnu();
test.gcc()
.file("foo.S")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_have("foo.S");
}
#[test]
fn gnu_shared() {
let test = Test::gnu();
test.gcc()
.file("foo.c")
.shared_flag(true)
.static_flag(false)
.compile("foo");
test.cmd(0)
.must_have("-shared")
.must_not_have("-static");
}
#[test]
fn gnu_flag_if_supported() {
if cfg!(windows) {
return
}
let test = Test::gnu();
test.gcc()
.file("foo.c")
.flag_if_supported("-Wall")
.flag_if_supported("-Wflag-does-not-exist")
.flag_if_supported("-std=c++11")
.compile("foo");
test.cmd(0)
.must_have("-Wall")
.must_not_have("-Wflag-does-not-exist")
.must_not_have("-std=c++11");
}
#[test]
fn gnu_flag_if_supported_cpp() {
if cfg!(windows) {
return
}
let test = Test::gnu();
test.gcc()
.cpp(true)
.file("foo.cpp")
.flag_if_supported("-std=c++11")
.compile("foo");
test.cmd(0)
.must_have("-std=c++11");
}
#[test]
fn gnu_static() {
let test = Test::gnu();
test.gcc()
.file("foo.c")
.shared_flag(false)
.static_flag(true)
.compile("foo");
test.cmd(0)
.must_have("-static")
.must_not_have("-shared");
}
#[test]
fn msvc_smoke() {
let test = Test::msvc();
test.gcc()
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0)
.must_have("/O2")
.must_have("foo.c")
.must_not_have("/Z7")
.must_have("/c");
.must_have("/c")
.must_have("/MD");
test.cmd(1).must_have(test.td.path().join("foo.o"));
}
@ -191,7 +290,7 @@ fn msvc_opt_level_0() {
test.gcc()
.opt_level(0)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_not_have("/O2");
}
@ -202,7 +301,7 @@ fn msvc_debug() {
test.gcc()
.debug(true)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_have("/Z7");
}
@ -212,7 +311,7 @@ fn msvc_include() {
test.gcc()
.include("foo/bar")
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_have("/I").must_have("foo/bar");
}
@ -221,10 +320,32 @@ fn msvc_include() {
fn msvc_define() {
let test = Test::msvc();
test.gcc()
.define("FOO", Some("bar"))
.define("FOO", "bar")
.define("BAR", None)
.file("foo.c")
.compile("libfoo.a");
.compile("foo");
test.cmd(0).must_have("/DFOO=bar").must_have("/DBAR");
}
#[test]
fn msvc_static_crt() {
let test = Test::msvc();
test.gcc()
.static_crt(true)
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("/MT");
}
#[test]
fn msvc_no_static_crt() {
let test = Test::msvc();
test.gcc()
.static_crt(false)
.file("foo.c")
.compile("foo");
test.cmd(0).must_have("/MD");
}

View File

@ -1 +0,0 @@
{"files":{".clog.toml":"f691701bd51b5f311931d0d8f05fa3d78c00dda8d60f3313e21011309c736ff1",".github/CONTRIBUTING.md":"f7eff737f3aa25294802fefb233e3758a64b248781dbbf3262532d693f340a87",".github/ISSUE_TEMPLATE.md":"681afbd64b3603e3e82789ceb6841d851eaa7333caec5769173462bab1b5d82b",".travis.yml":"2975b3159624d4ecc4dd29577f378e9d4fa27f1991bfd5042ac3c267fb2cdd38","CHANGELOG.md":"82b23419a6964c8f80993b399c9dded5b7fd809ba51f5f806c2a139d3c6270a4","CONTRIBUTORS.md":"5d7dbafaff6879bbfbb01b22cca299953ec163872d8d624bbf99e20851ca0165","Cargo.toml":"94e3789815bfd001abf96cb0d10fa95a4b4576bc679539e79a531d0010e2ccdd","LICENSE-MIT":"6725d1437fc6c77301f2ff0e7d52914cf4f9509213e1078dc77d9356dbe6eac5","README.md":"031031971829f165ed7ffd8375c2249ce96336a9ed7f207d4722df05563d2d7e","appveyor.yml":"303c64c2cc84c24b3c9ac0b4cd66b98d6bb25dec7d030ed53e5cb6ab3f13ebd1","clap-test.rs":"a0b0d9ca9106a52bf9dc41cf52b0b87c98209dca3490caa6ec1452bd1fec5c4c","index.html":"36f9ce4465266f3af9a259444b01c4239200473cabfc848f789f75b322a3ea8f","justfile":"811b2dec57aec46e570aeeb9945018cf87fe65f6d5b27cdb9ffca79d906910f6","rustfmt.toml":"8fd2d63119df515fd5f44e530c709b19d66b09fbc2e22a640bf4b64c57e7d6b3","src/app/help.rs":"da53217886fb1ea289b9057d4b5e94dce74ce81a7b7006d216370aad709bff77","src/app/macros.rs":"0205c461041d917aecb4a15212f89908e72902b961f47014a645f2b061de5998","src/app/meta.rs":"a56d28bb466a8ba68155b3f2883e85228b4b74cf25658f62fc050e07cff2dc85","src/app/mod.rs":"d0e1843ae1f77c1da4179cebdd8fb1ea55803002fb1ae96087de3a8cdcedf6fc","src/app/parser.rs":"66b08057b7bc19f6c2c94671de7fc20ec623368e04f92d2a6775991d37430fc2","src/app/settings.rs":"cf9f4a1a9d3799ac30d1d98cf23987cc884435ad912a0dfd853b101ce86c97cc","src/app/usage.rs":"ecaeab6c7980544e9a2d35cc41f2797df8bc9c09f5da67e96286631a116c0ccc","src/app/validator.rs":"f19d876ac673019ed5fdd4b9f76ba598fa790aa5e64d482696ca1e45dce5f28b","src/args/any_arg.rs":"b082385eeff2505ced7b747bd44d20a3fb6fd9d4bd14be9e99870699c43ea072","src/args/arg.rs":"673de3f1957eccb1b116255bac9638fe24c0da54ccb358d958446c8ed54c9621","src/args/arg_builder/base.rs":"8b99a9ab811df3e0bdcfba8c0994042b0bcd06d8ddf794ab559baaf9a490ba59","src/args/arg_builder/flag.rs":"4007a950869789b1f4d5f953107aee228477e2d5fe82515d3b895286c65522c6","src/args/arg_builder/mod.rs":"7a32c8fd85b48f7b60e5f2c13dc70fa9100aa65cd933ba419300d28d682bf722","src/args/arg_builder/option.rs":"d5e5243e3a72d2c820c8fad4e1efc4b985881c6f60f3a72757b33a9054a87e99","src/args/arg_builder/positional.rs":"39615d22b586e744a0bdeb8490dbe43df7df66ed793abf8f50ed2037ec0fb90c","src/args/arg_builder/switched.rs":"61f5121b0ec746461215a47e1b7a4d699a37a3f181172820e0615f68d5f6f0ef","src/args/arg_builder/valued.rs":"19368a03e046d6b63451c3d04dff6e51d49f140ed45330f82879539c6d1b28dd","src/args/arg_matcher.rs":"27829739ae12ac7800a26109e751ce9f8c3d26e262d41de161a38baf5c421167","src/args/arg_matches.rs":"9d72a388053ef0c31fe2516df9ea791a4d0f6c0b5e9758eb61886f1ac8df89ab","src/args/group.rs":"3f72a6ecc6ff71c96dd9cd8098e4fb6f7c4e6207e9bd0b67a50b104f5dfdb23d","src/args/macros.rs":"0dd7ae4c6e26ed78044c3ef90e21259816e544f724dcb09e6a0d92d4fcbc4b1a","src/args/matched_arg.rs":"1ed8d338869ecc3b5fa426ef4cf42f4c9c3b1dd538cdea1fe0489169345536f7","src/args/mod.rs":"c155cd989fa4ca1f8de6a79115afbf5086f092adcb854ff9698b9100f45fc323","src/args/settings.rs":"e6bbfb49c2e38fcedb67481bcbf0eb887ee510031639be8134411121a9363f7e","src/args/subcommand.rs":"e1ad9638c33785f1301675de1795b0a4f4b079452aa11f7526d263c2a1179432","src/completions/bash.rs":"116c6830ee2b6310f299a69924f5b1e39b05ebec2b5f7b0ffe3b6938b7fa5514","src/completions/fish.rs":"63975f8beea9af6bef66c7dd7938bfa61c6f871995a74dbc1545daa9fbc1f2d0","src/completions/macros.rs":"ebad5037e6e63401b1a54498e09d3bd93d1a3a06f045c2990902d47eb9a73774","src/completions/mod.rs":"5d4a734df6a21e6c1e0831a2f7be50a45d2e7bdaf7475589ea78b978643229cd","src/completions/powershell.rs":"4267818aaa60583c055d7a276a7535309e5162c94467f3003799b6a8a7f6d6b0","src/completions/shell.rs":"c7995ca229fd0d8671761da0aca0513c4f740165f02d06cd97aa0ae881c22cd4","src/completions/zsh.rs":"8ac4576e1cb3b1403dbb35ce146159aa8b29864e1d8201776200d999052b422d","src/errors.rs":"5d0ab536ea62614a6cf88d175a5b5e9c2777a35958e1d4598ac1ec4a6f451593","src/fmt.rs":"42459e7f42f5495c005d2de3eaf8d7b5619bf4b8d245ecb76e583f08ecaa3869","src/lib.rs":"3471c5b046df081afecb4e541d4e55dc7afa34bf7fe8f369f301f6471887e930","src/macros.rs":"c1d40220947c62b0364eedd2c40ca2c414daccc334a1e04e029a884e782bf2b0","src/osstringext.rs":"a87a5a0685dd8310f6329d5f8e8f54c0fac68eb75595a835aeb1c36208efd5f9","src/strext.rs":"d4418d396069e9c05804f92c042ba7192a4244e46059e2edc98670b45cd2daee","src/suggestions.rs":"ca352c62cdcc1b6071c50e39f39e8f5f6cd11c318229cc6cf16511dfde43c5c7","src/usage_parser.rs":"a04143bba42a6506746091a3f898c38e2c7409bacefed21fa8194c90961ca390"},"package":"6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f"}

View File

@ -1,83 +0,0 @@
[package]
name = "clap"
version = "2.24.2"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
repository = "https://github.com/kbknapp/clap-rs.git"
documentation = "https://docs.rs/clap/"
homepage = "https://clap.rs/"
readme = "README.md"
license = "MIT"
keywords = ["argument", "command", "arg", "parser", "parse"]
categories = ["command-line-interface"]
description = """
A simple to use, efficient, and full featured Command Line Argument Parser
"""
[dependencies]
bitflags = "0.8.0"
vec_map = "0.8"
unicode-width = "0.1.4"
unicode-segmentation = "1.0.1"
strsim = { version = "0.6.0", optional = true }
ansi_term = { version = "0.9.0", optional = true }
term_size = { version = "0.3.0", optional = true }
yaml-rust = { version = "0.3.5", optional = true }
clippy = { version = "~0.0.131", optional = true }
atty = { version = "0.2.2", optional = true }
[dev-dependencies]
regex = "0.2"
lazy_static = "0.2"
[features]
default = ["suggestions", "color", "wrap_help"]
suggestions = ["strsim"]
color = ["ansi_term", "atty"]
wrap_help = ["term_size"]
yaml = ["yaml-rust"]
unstable = [] # for building with unstable clap features (doesn't require nightly Rust) (currently none)
nightly = [] # for building with unstable Rust features (currently none)
lints = ["clippy"] # Requires nightly Rust
debug = [] # Enables debug messages
no_cargo = [] # Enable if you're not using Cargo, disables Cargo-env-var-dependent macros
[profile.release]
opt-level = 3
debug = false
rpath = false
lto = true
debug-assertions = false
# codegen-units ignored with lto=true
[profile.dev]
opt-level = 0
debug = true
rpath = false
lto = false
debug-assertions = true
codegen-units = 4
[profile.test]
opt-level = 1
debug = true
rpath = false
lto = false
debug-assertions = true
codegen-units = 4
[profile.bench]
opt-level = 3
debug = false
rpath = false
lto = true
debug-assertions = false
[profile.doc]
opt-level = 0
debug = true
rpath = false
lto = false
debug-assertions = true
codegen-units = 4

View File

@ -1,12 +0,0 @@
install:
- ps: Start-FileDownload 'https://static.rust-lang.org/dist/rust-nightly-i686-pc-windows-gnu.exe'
- rust-nightly-i686-pc-windows-gnu.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
- rustc -V
- cargo -V
build: false
test_script:
- cargo build --features yaml
- cargo test --features yaml

View File

@ -0,0 +1,17 @@
environment:
matrix:
- TARGET: x86_64-pc-windows-msvc
- TARGET: i686-pc-windows-msvc
- TARGET: x86_64-pc-windows-gnu
- TARGET: i686-pc-windows-gnu
RUST_BACKTRACE: full
install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs/
- rustup-init.exe -y --default-host %TARGET%
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -vV
- cargo -vV
build: false
test_script:
- cargo build --verbose --features yaml
- cargo test --verbose --features yaml

View File

@ -0,0 +1 @@
{"files":{".appveyor.yml":"38fb7e583271029caad727c9123a2b2679b7c59971de418f16dc5136dbebaeb5",".clog.toml":"f691701bd51b5f311931d0d8f05fa3d78c00dda8d60f3313e21011309c736ff1",".github/CONTRIBUTING.md":"f7eff737f3aa25294802fefb233e3758a64b248781dbbf3262532d693f340a87",".github/ISSUE_TEMPLATE.md":"681afbd64b3603e3e82789ceb6841d851eaa7333caec5769173462bab1b5d82b",".mention-bot":"51790ab49f43ed86a4a7c3d2e468aa5fa526ca5e2ac6af20432a2cb5b2fdbe84",".travis.yml":"2975b3159624d4ecc4dd29577f378e9d4fa27f1991bfd5042ac3c267fb2cdd38","CHANGELOG.md":"89936cd672f43681351e1b76622cc44d11c69f60440dd101f7b9db1cf2f184f7","CONTRIBUTORS.md":"5d7dbafaff6879bbfbb01b22cca299953ec163872d8d624bbf99e20851ca0165","Cargo.toml":"f12c622fae3f6582168616219b6e07e2ec99943e9515b76f97974e75227b9aa0","LICENSE-MIT":"6725d1437fc6c77301f2ff0e7d52914cf4f9509213e1078dc77d9356dbe6eac5","README.md":"bce567fb1e3e57129f3d0f58f5a1e9e07dc7414731644a47939c50b76f9976bb","clap-test.rs":"995a9d41ef372a814616113f4a58c1e580043678e54527afc2ebee7e8e1d3ef5","index.html":"36f9ce4465266f3af9a259444b01c4239200473cabfc848f789f75b322a3ea8f","justfile":"811b2dec57aec46e570aeeb9945018cf87fe65f6d5b27cdb9ffca79d906910f6","rustfmt.toml":"8fd2d63119df515fd5f44e530c709b19d66b09fbc2e22a640bf4b64c57e7d6b3","src/app/help.rs":"515f3ec638e3df8323b44c906073c07657122ec1b34bdadbdc47661c5d97ad1d","src/app/macros.rs":"44610b6522cedbcb140670aac27c796379f562ce77fcf5e8d104b038aadf52ec","src/app/meta.rs":"a56d28bb466a8ba68155b3f2883e85228b4b74cf25658f62fc050e07cff2dc85","src/app/mod.rs":"d0e1843ae1f77c1da4179cebdd8fb1ea55803002fb1ae96087de3a8cdcedf6fc","src/app/parser.rs":"1bae4cecf4fc798efdc2ad8d237f17536dafd7524e2d9a366f30f72a8a275846","src/app/settings.rs":"c6b87d4da01891123edddee9e28b048cba0c19d8c1db34ad5ad3e85b657c6b97","src/app/usage.rs":"703cec975c53e7f01b14b4593de41c518910ab347bc4c54efe79367a704ffc4c","src/app/validator.rs":"bc2291f6231a63456acab9e7743d1f881f1d5dfc17955394fa856703a8e80086","src/args/any_arg.rs":"b082385eeff2505ced7b747bd44d20a3fb6fd9d4bd14be9e99870699c43ea072","src/args/arg.rs":"673de3f1957eccb1b116255bac9638fe24c0da54ccb358d958446c8ed54c9621","src/args/arg_builder/base.rs":"8b99a9ab811df3e0bdcfba8c0994042b0bcd06d8ddf794ab559baaf9a490ba59","src/args/arg_builder/flag.rs":"4007a950869789b1f4d5f953107aee228477e2d5fe82515d3b895286c65522c6","src/args/arg_builder/mod.rs":"7a32c8fd85b48f7b60e5f2c13dc70fa9100aa65cd933ba419300d28d682bf722","src/args/arg_builder/option.rs":"d5e5243e3a72d2c820c8fad4e1efc4b985881c6f60f3a72757b33a9054a87e99","src/args/arg_builder/positional.rs":"f103a22803d9fb7f7c8f37f705fe214fdaad46903439964fc13740ec6f647eb8","src/args/arg_builder/switched.rs":"61f5121b0ec746461215a47e1b7a4d699a37a3f181172820e0615f68d5f6f0ef","src/args/arg_builder/valued.rs":"19368a03e046d6b63451c3d04dff6e51d49f140ed45330f82879539c6d1b28dd","src/args/arg_matcher.rs":"27829739ae12ac7800a26109e751ce9f8c3d26e262d41de161a38baf5c421167","src/args/arg_matches.rs":"9d72a388053ef0c31fe2516df9ea791a4d0f6c0b5e9758eb61886f1ac8df89ab","src/args/group.rs":"7fe5e2f0dd24faf1765410a9336d85976875e964d7f246e1fa216c4808d88dde","src/args/macros.rs":"57f248e2694f9413cbbaf9087813ed4f27064f5f8e29eaf4ec41ec2b274ae806","src/args/matched_arg.rs":"1ed8d338869ecc3b5fa426ef4cf42f4c9c3b1dd538cdea1fe0489169345536f7","src/args/mod.rs":"c155cd989fa4ca1f8de6a79115afbf5086f092adcb854ff9698b9100f45fc323","src/args/settings.rs":"2753ff50046def9ccb7f601b3d9f565348da1ef0253af24ccee94616a2e5c470","src/args/subcommand.rs":"e1ad9638c33785f1301675de1795b0a4f4b079452aa11f7526d263c2a1179432","src/completions/bash.rs":"116c6830ee2b6310f299a69924f5b1e39b05ebec2b5f7b0ffe3b6938b7fa5514","src/completions/fish.rs":"63975f8beea9af6bef66c7dd7938bfa61c6f871995a74dbc1545daa9fbc1f2d0","src/completions/macros.rs":"ebad5037e6e63401b1a54498e09d3bd93d1a3a06f045c2990902d47eb9a73774","src/completions/mod.rs":"5d4a734df6a21e6c1e0831a2f7be50a45d2e7bdaf7475589ea78b978643229cd","src/completions/powershell.rs":"866409e5d0a9b2551d739f86c0e4faf86911e9e7c656fb74b38e6960844233b5","src/completions/shell.rs":"c7995ca229fd0d8671761da0aca0513c4f740165f02d06cd97aa0ae881c22cd4","src/completions/zsh.rs":"8ac4576e1cb3b1403dbb35ce146159aa8b29864e1d8201776200d999052b422d","src/errors.rs":"3c46a4d79d9304ffb152a190528ec9db0cb6c05799bb5211e6df9f7d7abab814","src/fmt.rs":"f205f784268572544ff7e84a89f416c898255404275d4ab1f8fea7e89695daa9","src/lib.rs":"87b3ee49c6389cdbaa23e705732bcc68e7235bb16ff469321c92a89258c21beb","src/macros.rs":"2317a90223c80d8688fea5334b09b783c8aca8894e6c22ec2fd400ce941d301b","src/osstringext.rs":"a87a5a0685dd8310f6329d5f8e8f54c0fac68eb75595a835aeb1c36208efd5f9","src/strext.rs":"d4418d396069e9c05804f92c042ba7192a4244e46059e2edc98670b45cd2daee","src/suggestions.rs":"ad1165a9896382a0f09f73c0f6bf468454c19da207f28c3973e02879f453ad68","src/usage_parser.rs":"a04143bba42a6506746091a3f898c38e2c7409bacefed21fa8194c90961ca390"},"package":"867a885995b4184be051b70a592d4d70e32d7a188db6e8dff626af286a962771"}

View File

@ -0,0 +1,9 @@
{
"findPotentialReviewers": false,
"alwaysNotifyForPaths": [
{
"name": "kbknapp",
"files": ["**/*.rs", "**/*.md", "*"]
}
]
}

View File

@ -1,3 +1,31 @@
<a name="v2.24.2"></a>
### v2.25.0 (2017-06-20)
#### Features
* use textwrap crate for wrapping help texts ([b93870c1](https://github.com/kbknapp/clap-rs/commit/b93870c10ae3bd90d233c586a33e086803117285))
#### Improvements
* **Suggestions:** suggests to use flag after subcommand when applicable ([2671ca72](https://github.com/kbknapp/clap-rs/commit/2671ca7260119d4311d21c4075466aafdd9da734))
* Bumps bitflags crate to v0.9
#### Documentation
* Change `who's` -> `whose` ([53c1ffe8](https://github.com/kbknapp/clap-rs/commit/53c1ffe87f38b05d8804a0f7832412a952845349))
#### Documentation
* **App::template:** adds details about the necessity to use AppSettings::UnifiedHelpMessage when using {unified} tags in the help template ([cbea3d5a](https://github.com/kbknapp/clap-rs/commit/cbea3d5acf3271a7a734498c4d99c709941c331e), closes [#949](https://github.com/kbknapp/clap-rs/issues/949))
* **Arg::allow_hyphen_values:** updates the docs to include warnings for allow_hyphen_values and multiple(true) used together ([f9b0d657](https://github.com/kbknapp/clap-rs/commit/f9b0d657835d3f517f313d70962177dc30acf4a7))
* **README.md:**
* added a warning about using ~ deps ([821929b5](https://github.com/kbknapp/clap-rs/commit/821929b51bd60213955705900a436c9a64fcb79f), closes [#964](https://github.com/kbknapp/clap-rs/issues/964))
* added a warning about using ~ deps ([667697b0](https://github.com/kbknapp/clap-rs/commit/667697b0018369b71da41d02d567ab57df0f4887))
* **clap_app!:** adds using the @group specifier to the macro docs ([826048cb](https://github.com/kbknapp/clap-rs/commit/826048cb3cbc0280169303f1498ff0a2b7395883), closes [#932](https://github.com/kbknapp/clap-rs/issues/932))
<a name="v2.24.2"></a>
### v2.24.2 (2017-05-15)

117
third_party/rust/clap-2.25.0/Cargo.toml vendored Normal file
View File

@ -0,0 +1,117 @@
# 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 = "clap"
version = "2.25.0"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
description = "A simple to use, efficient, and full featured Command Line Argument Parser\n"
homepage = "https://clap.rs/"
documentation = "https://docs.rs/clap/"
readme = "README.md"
keywords = ["argument", "command", "arg", "parser", "parse"]
categories = ["command-line-interface"]
license = "MIT"
repository = "https://github.com/kbknapp/clap-rs.git"
[profile.test]
opt-level = 1
lto = false
codegen-units = 4
debug = true
debug-assertions = true
rpath = false
[profile.doc]
opt-level = 0
lto = false
codegen-units = 4
debug = true
debug-assertions = true
rpath = false
[profile.bench]
opt-level = 3
lto = true
debug = false
debug-assertions = false
rpath = false
[profile.dev]
opt-level = 0
lto = false
codegen-units = 4
debug = true
debug-assertions = true
rpath = false
[profile.release]
opt-level = 3
lto = true
debug = false
debug-assertions = false
rpath = false
[dependencies.ansi_term]
version = "0.9.0"
optional = true
[dependencies.term_size]
version = "0.3.0"
optional = true
[dependencies.textwrap]
version = "0.6.0"
[dependencies.yaml-rust]
version = "0.3.5"
optional = true
[dependencies.unicode-width]
version = "0.1.4"
[dependencies.atty]
version = "0.2.2"
optional = true
[dependencies.vec_map]
version = "0.8"
[dependencies.clippy]
version = "~0.0.131"
optional = true
[dependencies.unicode-segmentation]
version = "~1.1.0"
[dependencies.bitflags]
version = "0.9"
[dependencies.strsim]
version = "0.6.0"
optional = true
[dev-dependencies.regex]
version = "0.2"
[dev-dependencies.lazy_static]
version = "0.2"
[features]
unstable = []
default = ["suggestions", "color", "wrap_help"]
color = ["ansi_term", "atty"]
yaml = ["yaml-rust"]
no_cargo = []
wrap_help = ["term_size"]
suggestions = ["strsim"]
nightly = []
debug = []
lints = ["clippy"]

View File

@ -45,7 +45,13 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
## What's New
Here's the highlights for v2.24.2
Here's the highlights for v2.25.0
* use textwrap crate for wrapping help texts
* suggests to use flag after subcommand when applicable
* Bumps bitflags crate to v0.9
Here's the highlights for v2.21.0 to v2.24.2
* fixes a bug where args that allow values to start with a hyphen couldnt contain a double hyphen -- as a value
* fixes a bug where positional argument help text is misaligned
@ -53,9 +59,6 @@ Here's the highlights for v2.24.2
* **Arg::allow_hyphen_values docs:** updates the docs to include warnings for allow_hyphen_values and multiple(true) used together
* **clap_app! docs:** adds using the @group specifier to the macro docs
* adds a debug assertion to ensure all args added to groups actually exist
Here's the highlights for v2.21.0 to v2.24.1
* fixes a bug where args with last(true) and required(true) set were not being printed in the usage string
* fixes a bug that was printing the arg name, instead of value name when Arg::last(true) was used
* fixes a bug where flags were parsed as flags AND positional values when specific combinations of settings were used
@ -618,6 +621,28 @@ clap = "~2.19.0"
This will cause *only* the patch version to be updated upon a `cargo update` call, and therefore cannot break due to new features, or bumped minimum versions of Rust.
#### Warning about '~' Dependencies
Using `~` can cause issues in certain circumstances.
From @alexcrichton:
Right now Cargo's version resolution is pretty naive, it's just a brute-force search of the solution space, returning the first resolvable graph. This also means that it currently won't terminate until it proves there is not possible resolvable graph. This leads to situations where workspaces with multiple binaries, for example, have two different dependencies such as:
```toml
# In one Cargo.toml
[dependencies]
clap = "~2.19.0"
# In another Cargo.toml
[dependencies]
clap = "2.22"
```
This is inherently an unresolvable crate graph in Cargo right now. Cargo requires there's only one major version of a crate, and being in the same workspace these two crates must share a version. This is impossible in this location, though, as these version constraints cannot be met.
#### Minimum Version of Rust
`clap` will officially support current stable Rust, minus two releases, but may work with prior releases as well. For example, current stable Rust at the time of this writing is 1.13.0, meaning `clap` is guaranteed to compile with 1.11.0 and beyond.

View File

@ -70,6 +70,7 @@ mod test {
.version("0.1")
.author("Kevin K. <kbknapp@gmail.com>")
.arg_from_usage("-o --option [scoption]... 'tests options'")
.arg_from_usage("-s --subcmdarg [subcmdarg] 'tests other args'")
.arg_from_usage("[scpositional] 'tests positionals'"))
}
}

View File

@ -10,14 +10,14 @@ use app::{App, AppSettings};
use app::parser::Parser;
use args::{AnyArg, ArgSettings, DispOrder};
use errors::{Error, Result as ClapResult};
use fmt::{Format, Colorizer};
use fmt::{Format, Colorizer, ColorizerOption};
use app::usage;
// Third Party
use unicode_width::UnicodeWidthStr;
#[cfg(feature = "wrap_help")]
use term_size;
use unicode_segmentation::UnicodeSegmentation;
use textwrap;
use vec_map::VecMap;
#[cfg(not(feature = "wrap_help"))]
@ -25,18 +25,6 @@ mod term_size {
pub fn dimensions() -> Option<(usize, usize)> { None }
}
macro_rules! find_longest {
($help:expr) => {{
let mut lw = 0;
for l in $help.split(' ').map(|s| str_width(s)) {
if l > lw {
lw = l;
}
}
lw
}};
}
fn str_width(s: &str) -> usize { UnicodeWidthStr::width(s) }
const TAB: &'static str = " ";
@ -95,6 +83,7 @@ pub struct Help<'a> {
// Public Functions
impl<'a> Help<'a> {
/// Create a new `Help` instance.
#[cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
pub fn new(w: &'a mut Write,
next_line_help: bool,
hide_pv: bool,
@ -155,10 +144,10 @@ impl<'a> Help<'a> {
let nlh = parser.is_set(AppSettings::NextLineHelp);
let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
let color = parser.is_set(AppSettings::ColoredHelp);
let cizer = Colorizer {
let cizer = Colorizer::new(ColorizerOption {
use_stderr: stderr,
when: parser.color(),
};
});
Self::new(w,
nlh,
hide_v,
@ -176,9 +165,9 @@ impl<'a> Help<'a> {
if let Some(h) = parser.meta.help_str {
try!(write!(self.writer, "{}", h).map_err(Error::from));
} else if let Some(tmpl) = parser.meta.template {
try!(self.write_templated_help(&parser, tmpl));
try!(self.write_templated_help(parser, tmpl));
} else {
try!(self.write_default_help(&parser));
try!(self.write_default_help(parser));
}
Ok(())
}
@ -199,7 +188,7 @@ impl<'a> Help<'a> {
arg.is_set(ArgSettings::NextLineHelp)
}) {
if arg.longest_filter() {
self.longest = cmp::max(self.longest, arg.to_string().len());
self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
}
arg_v.push(arg)
}
@ -232,7 +221,7 @@ impl<'a> Help<'a> {
}) {
if arg.longest_filter() {
debugln!("Help::write_args: Current Longest...{}", self.longest);
self.longest = cmp::max(self.longest, arg.to_string().len());
self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
debugln!("Help::write_args: New Longest...{}", self.longest);
}
let btm = ord_m.entry(arg.disp_ord()).or_insert(BTreeMap::new());
@ -364,7 +353,7 @@ impl<'a> Help<'a> {
debug!("Help::val: next_line...");
if !(nlh || self.force_next_line) {
sdebugln!("No");
let self_len = arg.to_string().len();
let self_len = str_width(arg.to_string().as_str());
// subtract ourself
let mut spcs = self.longest - self_len;
// Since we're writing spaces from the tab point we first need to know if we
@ -383,7 +372,7 @@ impl<'a> Help<'a> {
}
} else if !(nlh || self.force_next_line) {
sdebugln!("No, and not next_line");
write_nspaces!(self.writer, self.longest + 4 - (arg.to_string().len()));
write_nspaces!(self.writer, self.longest + 4 - (str_width(arg.to_string().as_str())));
} else {
sdebugln!("No");
}
@ -392,7 +381,7 @@ impl<'a> Help<'a> {
fn write_before_after_help(&mut self, h: &str) -> io::Result<()> {
debugln!("Help::write_before_after_help;");
let mut help = String::new();
let mut help = String::from(h);
// determine if our help fits or needs to wrap
debugln!("Help::write_before_after_help: Term width...{}",
self.term_w);
@ -401,47 +390,29 @@ impl<'a> Help<'a> {
debug!("Help::write_before_after_help: Too long...");
if too_long || h.contains("{n}") {
sdebugln!("Yes");
help.push_str(h);
debugln!("Help::write_before_after_help: help: {}", help);
debugln!("Help::write_before_after_help: help width: {}",
str_width(&*help));
// Determine how many newlines we need to insert
debugln!("Help::write_before_after_help: Usable space: {}",
self.term_w);
let longest_w = find_longest!(help);
help = help.replace("{n}", "\n");
wrap_help(&mut help, longest_w, self.term_w);
help = wrap_help(&help.replace("{n}", "\n"), self.term_w);
} else {
sdebugln!("No");
}
let help = if !help.is_empty() {
&*help
} else {
help.push_str(h);
&*help
};
if help.contains('\n') {
if let Some(part) = help.lines().next() {
try!(write!(self.writer, "{}", part));
}
for part in help.lines().skip(1) {
try!(write!(self.writer, "\n{}", part));
}
} else {
try!(write!(self.writer, "{}", help));
}
try!(write!(self.writer, "{}", help));
Ok(())
}
/// Writes argument's help to the wrapped stream.
fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, spec_vals: &str) -> io::Result<()> {
debugln!("Help::help;");
let mut help = String::new();
let h = if self.use_long {
arg.long_help().unwrap_or(arg.help().unwrap_or(""))
arg.long_help().unwrap_or_else(|| arg.help().unwrap_or(""))
} else {
arg.help().unwrap_or(arg.long_help().unwrap_or(""))
arg.help().unwrap_or_else(|| arg.long_help().unwrap_or(""))
};
let mut help = String::from(h) + spec_vals;
let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) || self.use_long;
debugln!("Help::help: Next Line...{:?}", nlh);
@ -461,48 +432,31 @@ impl<'a> Help<'a> {
debug!("Help::help: Too long...");
if too_long && spcs <= self.term_w || h.contains("{n}") {
sdebugln!("Yes");
help.push_str(h);
help.push_str(&*spec_vals);
debugln!("Help::help: help...{}", help);
debugln!("Help::help: help width...{}", str_width(&*help));
// Determine how many newlines we need to insert
let avail_chars = self.term_w - spcs;
debugln!("Help::help: Usable space...{}", avail_chars);
let longest_w = find_longest!(help);
help = help.replace("{n}", "\n");
wrap_help(&mut help, longest_w, avail_chars);
help = wrap_help(&help.replace("{n}", "\n"), avail_chars);
} else {
sdebugln!("No");
}
let help = if !help.is_empty() {
&*help
} else if spec_vals.is_empty() {
h
} else {
help.push_str(h);
help.push_str(&*spec_vals);
&*help
};
if help.contains('\n') {
if let Some(part) = help.lines().next() {
try!(write!(self.writer, "{}", part));
}
for part in help.lines().skip(1) {
try!(write!(self.writer, "\n"));
if nlh || self.force_next_line {
try!(write!(self.writer, "{}{}{}", TAB, TAB, TAB));
} else if arg.has_switch() {
write_nspaces!(self.writer, self.longest + 12);
} else {
write_nspaces!(self.writer, self.longest + 8);
}
try!(write!(self.writer, "{}", part));
}
} else if nlh || self.force_next_line {
try!(write!(self.writer, "{}", help));
if let Some(part) = help.lines().next() {
try!(write!(self.writer, "{}", part));
}
for part in help.lines().skip(1) {
try!(write!(self.writer, "\n"));
if nlh || self.force_next_line {
try!(write!(self.writer, "{}{}{}", TAB, TAB, TAB));
} else if arg.has_switch() {
write_nspaces!(self.writer, self.longest + 12);
} else {
write_nspaces!(self.writer, self.longest + 8);
}
try!(write!(self.writer, "{}", part));
}
if !help.contains('\n') && (nlh || self.force_next_line) {
try!(write!(self.writer, "\n"));
} else {
try!(write!(self.writer, "{}", help));
}
Ok(())
}
@ -558,6 +512,7 @@ impl<'a> Help<'a> {
/// Writes help for all arguments (options, flags, args, subcommands)
/// including titles of a Parser Object to the wrapped stream.
#[cfg_attr(feature = "lints", allow(useless_let_if_seq))]
#[cfg_attr(feature = "cargo-clippy", allow(useless_let_if_seq))]
pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> {
debugln!("Help::write_all_args;");
let flags = parser.has_flags();
@ -610,7 +565,7 @@ impl<'a> Help<'a> {
try!(self.writer.write_all(b"\n\n"));
}
try!(color!(self, "SUBCOMMANDS:\n", warning));
try!(self.write_subcommands(&parser));
try!(self.write_subcommands(parser));
}
Ok(())
@ -629,7 +584,8 @@ impl<'a> Help<'a> {
let btm = ord_m
.entry(sc.p.meta.disp_ord)
.or_insert(BTreeMap::new());
self.longest = cmp::max(self.longest, sc.p.meta.name.len());
self.longest = cmp::max(self.longest, str_width(sc.p.meta.name.as_str()));
//self.longest = cmp::max(self.longest, sc.p.meta.name.len());
btm.insert(sc.p.meta.name.clone(), sc.clone());
}
@ -650,7 +606,7 @@ impl<'a> Help<'a> {
/// Writes version of a Parser Object to the wrapped stream.
fn write_version(&mut self, parser: &Parser) -> io::Result<()> {
debugln!("Help::write_version;");
try!(write!(self.writer, "{}", parser.meta.version.unwrap_or("".into())));
try!(write!(self.writer, "{}", parser.meta.version.unwrap_or("")));
Ok(())
}
@ -660,10 +616,8 @@ impl<'a> Help<'a> {
macro_rules! write_name {
() => {{
let mut name = parser.meta.name.clone();
let longest_w = find_longest!(name);
name = name.replace("{n}", "\n");
wrap_help(&mut name, longest_w, self.term_w);
try!(color!(self, &*name, good));
try!(color!(self, wrap_help(&name, self.term_w), good));
}};
}
if let Some(bn) = parser.meta.bin_name.as_ref() {
@ -690,16 +644,15 @@ impl<'a> Help<'a> {
macro_rules! write_thing {
($thing:expr) => {{
let mut owned_thing = $thing.to_owned();
let longest_w = find_longest!(owned_thing);
owned_thing = owned_thing.replace("{n}", "\n");
wrap_help(&mut owned_thing, longest_w, self.term_w);
try!(write!(self.writer, "{}\n", &*owned_thing))
try!(write!(self.writer, "{}\n",
wrap_help(&owned_thing, self.term_w)))
}};
}
// Print the version
try!(self.write_bin_name(&parser));
try!(self.write_bin_name(parser));
try!(self.writer.write_all(b" "));
try!(self.write_version(&parser));
try!(self.write_version(parser));
try!(self.writer.write_all(b"\n"));
if let Some(author) = parser.meta.author {
write_thing!(author)
@ -720,7 +673,7 @@ impl<'a> Help<'a> {
let subcmds = parser.has_subcommands();
if flags || opts || pos || subcmds {
try!(self.write_all_args(&parser));
try!(self.write_all_args(parser));
}
if let Some(h) = parser.meta.more_help {
@ -775,9 +728,9 @@ fn copy_until<R: Read, W: Write>(r: &mut R, w: &mut W, delimiter_byte: u8) -> Co
/// Copies the contents of a reader into a writer until a {tag} is found,
/// copying the tag content to a buffer and returning its size.
/// In addition to errors, there are three possible outputs:
/// - None: The reader was consumed.
/// - Some(Ok(0)): No tag was captured but the reader still contains data.
/// - Some(Ok(length>0)): a tag with `length` was captured to the tag_buffer.
/// - `None`: The reader was consumed.
/// - `Some(Ok(0))`: No tag was captured but the reader still contains data.
/// - `Some(Ok(length>0))`: a tag with `length` was captured to the `tag_buffer`.
fn copy_and_capture<R: Read, W: Write>(r: &mut R,
w: &mut W,
tag_buffer: &mut Cursor<Vec<u8>>)
@ -891,7 +844,7 @@ impl<'a> Help<'a> {
try!(self.writer.write_all(b"Could not decode tag name"));
}
b"bin" => {
try!(self.write_bin_name(&parser));
try!(self.write_bin_name(parser));
}
b"version" => {
try!(write!(self.writer,
@ -912,7 +865,7 @@ impl<'a> Help<'a> {
try!(write!(self.writer, "{}", usage::create_usage_no_title(parser, &[])));
}
b"all-args" => {
try!(self.write_all_args(&parser));
try!(self.write_all_args(parser));
}
b"unified" => {
let opts_flags = parser
@ -931,7 +884,7 @@ impl<'a> Help<'a> {
try!(self.write_args(parser.positionals().map(as_arg_trait)));
}
b"subcommands" => {
try!(self.write_subcommands(&parser));
try!(self.write_subcommands(parser));
}
b"after-help" => {
try!(write!(self.writer,
@ -954,47 +907,12 @@ impl<'a> Help<'a> {
}
}
fn wrap_help(help: &mut String, longest_w: usize, avail_chars: usize) {
debugln!("Help::wrap_help: longest_w={}, avail_chars={}",
longest_w,
avail_chars);
debug!("Help::wrap_help: Enough space to wrap...");
if longest_w < avail_chars {
sdebugln!("Yes");
let mut prev_space = 0;
let mut j = 0;
for (idx, g) in (&*help.clone()).grapheme_indices(true) {
debugln!("Help::wrap_help:iter: idx={}, g={}", idx, g);
if g == "\n" {
debugln!("Help::wrap_help:iter: Newline found...");
debugln!("Help::wrap_help:iter: Still space...{:?}",
str_width(&help[j..idx]) < avail_chars);
if str_width(&help[j..idx]) < avail_chars {
j = idx;
continue;
}
} else if g != " " {
if idx != help.len() - 1 || str_width(&help[j..idx]) < avail_chars {
continue;
}
debugln!("Help::wrap_help:iter: Reached the end of the line and we're over...");
} else if str_width(&help[j..idx]) <= avail_chars {
debugln!("Help::wrap_help:iter: Space found with room...");
prev_space = idx;
continue;
}
debugln!("Help::wrap_help:iter: Adding Newline...");
j = prev_space;
debugln!("Help::wrap_help:iter: prev_space={}, j={}", prev_space, j);
debugln!("Help::wrap_help:iter: Removing...{}", j);
debugln!("Help::wrap_help:iter: Char at {}: {:?}", j, &help[j..j + 1]);
help.remove(j);
help.insert(j, '\n');
prev_space = idx;
}
} else {
sdebugln!("No");
}
fn wrap_help(help: &str, avail_chars: usize) -> String {
let wrapper = textwrap::Wrapper::new(avail_chars).break_words(false);
help.lines()
.map(|line| wrapper.fill(line))
.collect::<Vec<String>>()
.join("\n")
}
#[cfg(test)]
@ -1003,8 +921,7 @@ mod test {
#[test]
fn wrap_help_last_word() {
let mut help = String::from("foo bar baz");
wrap_help(&mut help, 3, 5);
assert_eq!(help, "foo\nbar\nbaz");
let help = String::from("foo bar baz");
assert_eq!(wrap_help(&help, 5), "foo\nbar\nbaz");
}
}

View File

@ -98,9 +98,8 @@ macro_rules! _handle_group_reqs{
($me:ident, $arg:ident) => ({
use args::AnyArg;
debugln!("_handle_group_reqs!;");
for grp in $me.groups.iter() {
for grp in &$me.groups {
let found = if grp.args.contains(&$arg.name()) {
// vec_remove!($me.required, &$arg.name());
if let Some(ref reqs) = grp.requires {
debugln!("_handle_group_reqs!: Adding {:?} to the required list", reqs);
$me.required.extend(reqs);
@ -121,7 +120,11 @@ macro_rules! _handle_group_reqs{
debugln!("_handle_group_reqs!:iter: Adding args from group to blacklist...{:?}", grp.args);
if !grp.multiple {
$me.blacklist.extend(&grp.args);
vec_remove!($me.blacklist, &$arg.name());
debugln!("_handle_group_reqs!: removing {:?} from blacklist", $arg.name());
for i in (0 .. $me.blacklist.len()).rev() {
let should_remove = $me.blacklist[i] == $arg.name();
if should_remove { $me.blacklist.swap_remove(i); }
}
}
}
}

View File

@ -305,11 +305,11 @@ impl<'a, 'b> Parser<'a, 'b>
}
// actually adds the arguments but from a borrow (which means we have to do some clonine)
pub fn add_arg_ref(&mut self, a: &Arg<'a, 'b>) {
debug_assert!(self.debug_asserts(&a));
debug_assert!(self.debug_asserts(a));
self.add_conditional_reqs(a);
self.add_arg_groups(a);
self.add_reqs(a);
self.implied_settings(&a);
self.implied_settings(a);
if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) {
let i = if a.index.is_none() {
(self.positionals.len() + 1)
@ -500,7 +500,7 @@ impl<'a, 'b> Parser<'a, 'b>
// but no 2)
if let Some((idx, p)) = self.positionals.iter().rev().next() {
assert!(!(idx != self.positionals.len()),
"Found positional argument \"{}\" who's index is {} but there \
"Found positional argument \"{}\" whose index is {} but there \
are only {} positional arguments defined",
p.b.name,
idx,
@ -702,7 +702,7 @@ impl<'a, 'b> Parser<'a, 'b>
} else if let Some(c) = sc.subcommands
.iter()
.find(|s| if let Some(ref als) = s.p.meta.aliases {
als.iter().any(|&(a, _)| &a == &&*cmd.to_string_lossy())
als.iter().any(|&(a, _)| a == &*cmd.to_string_lossy())
} else {
false
})
@ -868,8 +868,7 @@ impl<'a, 'b> Parser<'a, 'b>
}
if !starts_new_arg {
match needs_val_of {
ParseResult::Opt(name) => {
if let ParseResult::Opt(name) = needs_val_of {
// Check to see if parsing a value from a previous arg
let arg = self.opts
.iter()
@ -879,43 +878,39 @@ impl<'a, 'b> Parser<'a, 'b>
needs_val_of = try!(self.add_val_to_arg(arg, &arg_os, matcher));
// get the next value from the iterator
continue;
}
}
} else if arg_os.starts_with(b"--") {
needs_val_of = try!(self.parse_long_arg(matcher, &arg_os));
debugln!("Parser:get_matches_with: After parse_long_arg {:?}",
needs_val_of);
match needs_val_of {
ParseResult::Flag |
ParseResult::Opt(..) |
ParseResult::ValuesDone => continue,
_ => (),
}
} else {
if arg_os.starts_with(b"--") {
needs_val_of = try!(self.parse_long_arg(matcher, &arg_os));
debugln!("Parser:get_matches_with: After parse_long_arg {:?}",
needs_val_of);
match needs_val_of {
ParseResult::Flag |
ParseResult::Opt(..) |
ParseResult::ValuesDone => continue,
_ => (),
}
} else if arg_os.starts_with(b"-") && arg_os.len_() != 1 {
// Try to parse short args like normal, if AllowLeadingHyphen or
// AllowNegativeNumbers is set, parse_short_arg will *not* throw
// an error, and instead return Ok(None)
needs_val_of = try!(self.parse_short_arg(matcher, &arg_os));
// If it's None, we then check if one of those two AppSettings was set
debugln!("Parser:get_matches_with: After parse_short_arg {:?}",
needs_val_of);
match needs_val_of {
ParseResult::MaybeNegNum => {
if !(arg_os.to_string_lossy().parse::<i64>().is_ok() ||
arg_os.to_string_lossy().parse::<f64>().is_ok()) {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
&*usage::create_error_usage(self, matcher, None),
self.color()));
}
} else if arg_os.starts_with(b"-") && arg_os.len_() != 1 {
// Try to parse short args like normal, if AllowLeadingHyphen or
// AllowNegativeNumbers is set, parse_short_arg will *not* throw
// an error, and instead return Ok(None)
needs_val_of = try!(self.parse_short_arg(matcher, &arg_os));
// If it's None, we then check if one of those two AppSettings was set
debugln!("Parser:get_matches_with: After parse_short_arg {:?}",
needs_val_of);
match needs_val_of {
ParseResult::MaybeNegNum => {
if !(arg_os.to_string_lossy().parse::<i64>().is_ok() ||
arg_os.to_string_lossy().parse::<f64>().is_ok()) {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
&*usage::create_error_usage(self, matcher, None),
self.color()));
}
ParseResult::Opt(..) |
}
ParseResult::Opt(..) |
ParseResult::Flag |
ParseResult::ValuesDone => continue,
_ => (),
}
_ => (),
}
}
@ -1148,7 +1143,7 @@ impl<'a, 'b> Parser<'a, 'b>
mid_string.push_str(" ");
if let Some(ref mut sc) = self.subcommands
.iter_mut()
.find(|s| &s.p.meta.name == &sc_name) {
.find(|s| s.p.meta.name == sc_name) {
let mut sc_matcher = ArgMatcher::new();
// bin_name should be parent's bin_name + [<reqs>] + the sc's name separated by
// a space
@ -1241,13 +1236,11 @@ impl<'a, 'b> Parser<'a, 'b>
.find(|g| g.name == group)
.expect(INTERNAL_ERROR_MSG)
.args {
if self.groups.iter().any(|g| &g.name == &*n) {
if self.groups.iter().any(|g| g.name == *n) {
args.extend(self.arg_names_in_group(n));
g_vec.push(*n);
} else {
if !args.contains(n) {
args.push(*n);
}
} else if !args.contains(n) {
args.push(*n);
}
}
@ -1344,6 +1337,7 @@ impl<'a, 'b> Parser<'a, 'b>
Ok(())
}
#[cfg_attr(feature = "cargo-clippy", allow(let_and_return))]
fn use_long_help(&self) -> bool {
let ul = self.flags.iter().any(|f| f.b.long_help.is_some()) ||
self.opts.iter().any(|o| o.b.long_help.is_some()) ||
@ -1360,13 +1354,12 @@ impl<'a, 'b> Parser<'a, 'b>
use_long = use_long && self.use_long_help();
let mut buf = vec![];
match Help::write_parser_help(&mut buf, self, use_long) {
Err(e) => return e,
_ => (),
}
Error {
message: unsafe { String::from_utf8_unchecked(buf) },
kind: ErrorKind::HelpDisplayed,
info: None,
Err(e) => e,
_ => Error {
message: unsafe { String::from_utf8_unchecked(buf) },
kind: ErrorKind::HelpDisplayed,
info: None,
}
}
}
@ -1375,13 +1368,12 @@ impl<'a, 'b> Parser<'a, 'b>
let out = io::stdout();
let mut buf_w = BufWriter::new(out.lock());
match self.print_version(&mut buf_w, use_long) {
Err(e) => return e,
_ => (),
}
Error {
message: String::new(),
kind: ErrorKind::VersionDisplayed,
info: None,
Err(e) => e,
_ => Error {
message: String::new(),
kind: ErrorKind::VersionDisplayed,
info: None,
}
}
}
@ -1403,7 +1395,7 @@ impl<'a, 'b> Parser<'a, 'b>
full_arg.trim_left_matches(b'-')
};
if let Some(opt) = find_opt_by_long!(@os self, &arg) {
if let Some(opt) = find_opt_by_long!(@os self, arg) {
debugln!("Parser::parse_long_arg: Found valid opt '{}'",
opt.to_string());
self.settings.set(AS::ValidArgFound);
@ -1414,7 +1406,7 @@ impl<'a, 'b> Parser<'a, 'b>
}
return Ok(ret);
} else if let Some(flag) = find_flag_by_long!(@os self, &arg) {
} else if let Some(flag) = find_flag_by_long!(@os self, arg) {
debugln!("Parser::parse_long_arg: Found valid flag '{}'",
flag.to_string());
self.settings.set(AS::ValidArgFound);
@ -1588,33 +1580,31 @@ impl<'a, 'b> Parser<'a, 'b>
where A: AnyArg<'a, 'b> + Display
{
debugln!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name(), val);
let ret;
debugln!("Parser::add_val_to_arg; trailing_vals={:?}, DontDelimTrailingVals={:?}",
self.is_set(AS::TrailingValues),
self.is_set(AS::DontDelimitTrailingValues));
if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) {
if let Some(delim) = arg.val_delim() {
let mut iret = ParseResult::ValuesDone;
if val.is_empty_() {
iret = try!(self.add_single_val_to_arg(arg, val, matcher));
Ok(try!(self.add_single_val_to_arg(arg, val, matcher)))
} else {
let mut iret = ParseResult::ValuesDone;
for v in val.split(delim as u32 as u8) {
iret = try!(self.add_single_val_to_arg(arg, v, matcher));
}
// If there was a delimiter used, we're not looking for more values
if val.contains_byte(delim as u32 as u8) ||
arg.is_set(ArgSettings::RequireDelimiter) {
iret = ParseResult::ValuesDone;
}
arg.is_set(ArgSettings::RequireDelimiter) {
iret = ParseResult::ValuesDone;
}
Ok(iret)
}
ret = Ok(iret);
} else {
ret = self.add_single_val_to_arg(arg, val, matcher);
self.add_single_val_to_arg(arg, val, matcher)
}
} else {
ret = self.add_single_val_to_arg(arg, val, matcher);
self.add_single_val_to_arg(arg, val, matcher)
}
ret
}
fn add_single_val_to_arg<A>(&self,
@ -1662,19 +1652,18 @@ impl<'a, 'b> Parser<'a, 'b>
}
fn did_you_mean_error(&self, arg: &str, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
// Didn't match a flag or option...maybe it was a typo and close to one
// Didn't match a flag or option
let suffix =
suggestions::did_you_mean_suffix(arg,
longs!(self),
suggestions::DidYouMeanMessageStyle::LongFlag);
suggestions::did_you_mean_flag_suffix(arg, longs!(self), &self.subcommands);
// Add the arg to the matches to build a proper usage string
if let Some(name) = suffix.1 {
if let Some(opt) = find_opt_by_long!(self, &name) {
if let Some(opt) = find_opt_by_long!(self, name) {
self.groups_for_arg(&*opt.b.name)
.and_then(|grps| Some(matcher.inc_occurrences_of(&*grps)));
matcher.insert(&*opt.b.name);
} else if let Some(flg) = find_flag_by_long!(self, &name) {
} else if let Some(flg) = find_flag_by_long!(self, name) {
self.groups_for_arg(&*flg.b.name)
.and_then(|grps| Some(matcher.inc_occurrences_of(&*grps)));
matcher.insert(&*flg.b.name);
@ -1698,11 +1687,11 @@ impl<'a, 'b> Parser<'a, 'b>
let ver = if use_long {
self.meta
.long_version
.unwrap_or(self.meta.version.unwrap_or("".into()))
.unwrap_or_else(|| self.meta.version.unwrap_or(""))
} else {
self.meta
.version
.unwrap_or(self.meta.long_version.unwrap_or("".into()))
.unwrap_or_else(|| self.meta.long_version.unwrap_or(""))
};
if let Some(bn) = self.meta.bin_name.as_ref() {
if bn.contains(' ') {
@ -1817,38 +1806,58 @@ impl<'a, 'b> Parser<'a, 'b>
}
pub fn find_any_arg(&self, name: &str) -> Option<&AnyArg> {
if let Some(f) = find_by_name!(self, &name, flags, iter) {
if let Some(f) = find_by_name!(self, name, flags, iter) {
return Some(f);
}
if let Some(o) = find_by_name!(self, &name, opts, iter) {
if let Some(o) = find_by_name!(self, name, opts, iter) {
return Some(o);
}
if let Some(p) = find_by_name!(self, &name, positionals, values) {
if let Some(p) = find_by_name!(self, name, positionals, values) {
return Some(p);
}
None
}
/// Check is a given string matches the binary name for this parser
fn is_bin_name(&self, value: &str) -> bool {
self.meta.bin_name
.as_ref()
.and_then(|name| Some(value == name))
.unwrap_or(false)
}
/// Check is a given string is an alias for this parser
fn is_alias(&self, value: &str) -> bool {
self.meta.aliases
.as_ref()
.and_then(|aliases| {
for alias in aliases {
if alias.0 == value {
return Some(true);
}
}
Some(false)
})
.unwrap_or(false)
}
// Only used for completion scripts due to bin_name messiness
#[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))]
pub fn find_subcommand(&'b self, sc: &str) -> Option<&'b App<'a, 'b>> {
debugln!("Parser::find_subcommand: sc={}", sc);
debugln!("Parser::find_subcommand: Currently in Parser...{}",
self.meta.bin_name.as_ref().unwrap());
for s in self.subcommands.iter() {
if s.p.meta.bin_name.as_ref().unwrap_or(&String::new()) == sc ||
(s.p.meta.aliases.is_some() &&
s.p
.meta
.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(s, _)| {
s == sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG)
})) {
for s in &self.subcommands {
if s.p.is_bin_name(sc) {
return Some(s);
}
// XXX: why do we split here?
// isn't `sc` supposed to be single word already?
let last = sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG);
if s.p.is_alias(last) {
return Some(s);
}
if let Some(app) = s.p.find_subcommand(sc) {
return Some(app);
}

View File

@ -4,47 +4,47 @@ use std::str::FromStr;
use std::ops::BitOr;
bitflags! {
flags Flags: u64 {
const SC_NEGATE_REQS = 1 << 0,
const SC_REQUIRED = 1 << 1,
const A_REQUIRED_ELSE_HELP = 1 << 2,
const GLOBAL_VERSION = 1 << 3,
const VERSIONLESS_SC = 1 << 4,
const UNIFIED_HELP = 1 << 5,
const WAIT_ON_ERROR = 1 << 6,
const SC_REQUIRED_ELSE_HELP= 1 << 7,
const NEEDS_LONG_HELP = 1 << 8,
const NEEDS_LONG_VERSION = 1 << 9,
const NEEDS_SC_HELP = 1 << 10,
const DISABLE_VERSION = 1 << 11,
const HIDDEN = 1 << 12,
const TRAILING_VARARG = 1 << 13,
const NO_BIN_NAME = 1 << 14,
const ALLOW_UNK_SC = 1 << 15,
const UTF8_STRICT = 1 << 16,
const UTF8_NONE = 1 << 17,
const LEADING_HYPHEN = 1 << 18,
const NO_POS_VALUES = 1 << 19,
const NEXT_LINE_HELP = 1 << 20,
const DERIVE_DISP_ORDER = 1 << 21,
const COLORED_HELP = 1 << 22,
const COLOR_ALWAYS = 1 << 23,
const COLOR_AUTO = 1 << 24,
const COLOR_NEVER = 1 << 25,
const DONT_DELIM_TRAIL = 1 << 26,
const ALLOW_NEG_NUMS = 1 << 27,
const LOW_INDEX_MUL_POS = 1 << 28,
const DISABLE_HELP_SC = 1 << 29,
const DONT_COLLAPSE_ARGS = 1 << 30,
const ARGS_NEGATE_SCS = 1 << 31,
const PROPAGATE_VALS_DOWN = 1 << 32,
const ALLOW_MISSING_POS = 1 << 33,
const TRAILING_VALUES = 1 << 34,
const VALID_NEG_NUM_FOUND = 1 << 35,
const PROPOGATED = 1 << 36,
const VALID_ARG_FOUND = 1 << 37,
const INFER_SUBCOMMANDS = 1 << 38,
const CONTAINS_LAST = 1 << 39,
struct Flags: u64 {
const SC_NEGATE_REQS = 1;
const SC_REQUIRED = 1 << 1;
const A_REQUIRED_ELSE_HELP = 1 << 2;
const GLOBAL_VERSION = 1 << 3;
const VERSIONLESS_SC = 1 << 4;
const UNIFIED_HELP = 1 << 5;
const WAIT_ON_ERROR = 1 << 6;
const SC_REQUIRED_ELSE_HELP= 1 << 7;
const NEEDS_LONG_HELP = 1 << 8;
const NEEDS_LONG_VERSION = 1 << 9;
const NEEDS_SC_HELP = 1 << 10;
const DISABLE_VERSION = 1 << 11;
const HIDDEN = 1 << 12;
const TRAILING_VARARG = 1 << 13;
const NO_BIN_NAME = 1 << 14;
const ALLOW_UNK_SC = 1 << 15;
const UTF8_STRICT = 1 << 16;
const UTF8_NONE = 1 << 17;
const LEADING_HYPHEN = 1 << 18;
const NO_POS_VALUES = 1 << 19;
const NEXT_LINE_HELP = 1 << 20;
const DERIVE_DISP_ORDER = 1 << 21;
const COLORED_HELP = 1 << 22;
const COLOR_ALWAYS = 1 << 23;
const COLOR_AUTO = 1 << 24;
const COLOR_NEVER = 1 << 25;
const DONT_DELIM_TRAIL = 1 << 26;
const ALLOW_NEG_NUMS = 1 << 27;
const LOW_INDEX_MUL_POS = 1 << 28;
const DISABLE_HELP_SC = 1 << 29;
const DONT_COLLAPSE_ARGS = 1 << 30;
const ARGS_NEGATE_SCS = 1 << 31;
const PROPAGATE_VALS_DOWN = 1 << 32;
const ALLOW_MISSING_POS = 1 << 33;
const TRAILING_VALUES = 1 << 34;
const VALID_NEG_NUM_FOUND = 1 << 35;
const PROPOGATED = 1 << 36;
const VALID_ARG_FOUND = 1 << 37;
const INFER_SUBCOMMANDS = 1 << 38;
const CONTAINS_LAST = 1 << 39;
}
}
@ -528,7 +528,7 @@ pub enum AppSettings {
/// This can be useful if there are many values, or they are explained elsewhere.
HidePossibleValuesInHelp,
/// Tries to match unknown args to partial [`subcommands`] or their [aliases]. For example to
/// Tries to match unknown args to partial [`subcommands`] or their [aliases]. For example to
/// match a subcommand named `test`, one could use `t`, `te`, `tes`, and `test`.
///
/// **NOTE:** The match *must not* be ambiguous at all in order to succeed. i.e. to match `te`
@ -536,7 +536,7 @@ pub enum AppSettings {
///
/// **CAUTION:** This setting can interfere with [positional/free arguments], take care when
/// designing CLIs which allow inferred subcommands and have potential positional/free
/// arguments who's values could start with the same characters as subcommands. If this is the
/// arguments whose values could start with the same characters as subcommands. If this is the
/// case, it's recommended to use settings such as [`AppSeettings::ArgsNegateSubcommands`] in
/// conjuction with this setting.
///

View File

@ -26,9 +26,9 @@ pub fn create_error_usage<'a, 'b>(p: &Parser<'a, 'b>,
let mut args: Vec<_> = matcher.arg_names()
.iter()
.filter(|n| {
if let Some(o) = find_by_name!(p, *n, opts, iter) {
if let Some(o) = find_by_name!(p, **n, opts, iter) {
!o.b.is_set(ArgSettings::Required) && !o.b.is_set(ArgSettings::Hidden)
} else if let Some(p) = find_by_name!(p, *n, positionals, values) {
} else if let Some(p) = find_by_name!(p, **n, positionals, values) {
!p.b.is_set(ArgSettings::Required) && p.b.is_set(ArgSettings::Hidden)
} else {
true // flags can't be required, so they're always true
@ -244,7 +244,7 @@ fn get_args_tag(p: &Parser, incl_reqs: bool) -> Option<String> {
None
})
.max()
.unwrap_or(p.positionals.len());
.unwrap_or_else(|| p.positionals.len());
return Some(p.positionals
.iter()
.filter_map(|(idx, pos)| if idx <= highest_req_pos {
@ -379,10 +379,10 @@ pub fn get_required_usage_from<'a, 'b>(p: &Parser<'a, 'b>,
let args_in_groups = p.groups
.iter()
.filter(|gn| desc_reqs.contains(&gn.name))
.flat_map(|g| p.arg_names_in_group(&g.name))
.flat_map(|g| p.arg_names_in_group(g.name))
.collect::<Vec<_>>();
let pmap = if let Some(ref m) = matcher {
let pmap = if let Some(m) = matcher {
desc_reqs.iter()
.filter(|a| p.positionals.values().any(|p| &&p.b.name == a))
.filter(|&pos| !m.contains(pos))
@ -414,10 +414,10 @@ pub fn get_required_usage_from<'a, 'b>(p: &Parser<'a, 'b>,
.filter(|name| !args_in_groups.contains(name))
.filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name))) {
debugln!("usage::get_required_usage_from:iter:{}:", a);
let arg = find_by_name!(p, a, flags, iter)
let arg = find_by_name!(p, *a, flags, iter)
.map(|f| f.to_string())
.unwrap_or_else(|| {
find_by_name!(p, a, opts, iter)
find_by_name!(p, *a, opts, iter)
.map(|o| o.to_string())
.expect(INTERNAL_ERROR_MSG)
});

View File

@ -11,7 +11,7 @@ use errors::Result as ClapResult;
use osstringext::OsStrExt2;
use app::settings::AppSettings as AS;
use app::parser::{Parser, ParseResult};
use fmt::Colorizer;
use fmt::{Colorizer, ColorizerOption};
use app::usage;
pub struct Validator<'a, 'b, 'z>(&'z mut Parser<'a, 'b>)
@ -135,16 +135,16 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
macro_rules! build_err {
($p:expr, $name:expr, $matcher:ident) => ({
debugln!("build_err!: name={}", $name);
let mut c_with = find_from!($p, $name, blacklist, &$matcher);
let mut c_with = find_from!($p, &$name, blacklist, &$matcher);
c_with = c_with.or(
$p.find_any_arg($name).map_or(None, |aa| aa.blacklist())
$p.find_any_arg(&$name).map_or(None, |aa| aa.blacklist())
.map_or(None,
|bl| bl.iter().find(|arg| $matcher.contains(arg)))
.map_or(None, |an| $p.find_any_arg(an))
.map_or(None, |aa| Some(format!("{}", aa)))
);
debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, $name);
$matcher.remove($name);
debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, &$name);
$matcher.remove(&$name);
let usg = usage::create_error_usage($p, $matcher, None);
if let Some(f) = find_by_name!($p, $name, flags, iter) {
debugln!("build_err!: It was a flag...");
@ -174,12 +174,12 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
n);
if matcher.contains(n) {
debugln!("Validator::validate_blacklist:iter:iter: matcher contains it...");
return Err(build_err!(self.0, &n, matcher));
return Err(build_err!(self.0, n, matcher));
}
}
} else if matcher.contains(name) {
debugln!("Validator::validate_blacklist:iter: matcher contains it...");
return Err(build_err!(self.0, name, matcher));
return Err(build_err!(self.0, *name, matcher));
}
}
Ok(())
@ -191,15 +191,15 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
debugln!("Validator::validate_matched_args:iter:{}: vals={:#?}",
name,
ma.vals);
if let Some(opt) = find_by_name!(self.0, name, opts, iter) {
if let Some(opt) = find_by_name!(self.0, *name, opts, iter) {
try!(self.validate_arg_num_vals(opt, ma, matcher));
try!(self.validate_values(opt, ma, matcher));
try!(self.validate_arg_requires(opt, ma, matcher));
try!(self.validate_arg_num_occurs(opt, ma, matcher));
} else if let Some(flag) = find_by_name!(self.0, name, flags, iter) {
} else if let Some(flag) = find_by_name!(self.0, *name, flags, iter) {
try!(self.validate_arg_requires(flag, ma, matcher));
try!(self.validate_arg_num_occurs(flag, ma, matcher));
} else if let Some(pos) = find_by_name!(self.0, name, positionals, values) {
} else if let Some(pos) = find_by_name!(self.0, *name, positionals, values) {
try!(self.validate_arg_num_vals(pos, ma, matcher));
try!(self.validate_arg_num_occurs(pos, ma, matcher));
try!(self.validate_values(pos, ma, matcher));
@ -344,15 +344,15 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
if matcher.contains(name) {
continue 'outer;
}
if let Some(a) = find_by_name!(self.0, name, flags, iter) {
if let Some(a) = find_by_name!(self.0, *name, flags, iter) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
} else if let Some(a) = find_by_name!(self.0, name, opts, iter) {
} else if let Some(a) = find_by_name!(self.0, *name, opts, iter) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
} else if let Some(a) = find_by_name!(self.0, name, positionals, values) {
} else if let Some(a) = find_by_name!(self.0, *name, positionals, values) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
}
@ -417,10 +417,10 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
fn missing_required_error(&self, matcher: &ArgMatcher, extra: Option<&str>) -> ClapResult<()> {
debugln!("Validator::missing_required_error: extra={:?}", extra);
let c = Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: self.0.color(),
};
});
let mut reqs = self.0
.required
.iter()
@ -453,4 +453,4 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
self.validate_required_unless(a, matcher)
.unwrap_or(false)
}
}
}

View File

@ -63,7 +63,7 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
let mult_vals = self.v
.val_names
.as_ref()
.map_or(true, |ref names| names.len() < 2);
.map_or(true, |names| names.len() < 2);
if self.is_set(ArgSettings::Multiple) && mult_vals {
"..."
} else {

View File

@ -472,7 +472,7 @@ impl<'a> From<&'a BTreeMap<Yaml, Yaml>> for ArgGroup<'a> {
b
};
for (k, v) in group_settings.iter() {
for (k, v) in group_settings {
a = match k.as_str().unwrap() {
"required" => a.required(v.as_bool().unwrap()),
"multiple" => a.multiple(v.as_bool().unwrap()),

View File

@ -1,4 +1,4 @@
#[cfg(feature = "yaml")]
macro_rules! yaml_tuple2 {
($a:ident, $v:ident, $c:ident) => {{
if let Some(vec) = $v.as_vec() {
@ -18,6 +18,7 @@ macro_rules! yaml_tuple2 {
};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_tuple3 {
($a:ident, $v:ident, $c:ident) => {{
if let Some(vec) = $v.as_vec() {
@ -37,6 +38,7 @@ macro_rules! yaml_tuple3 {
};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_vec_or_str {
($v:ident, $a:ident, $c:ident) => {{
let maybe_vec = $v.as_vec();
@ -60,6 +62,7 @@ macro_rules! yaml_vec_or_str {
};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_opt_str {
($v:expr) => {{
if $v.is_null() {
@ -70,31 +73,35 @@ macro_rules! yaml_opt_str {
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_str {
($v:expr) => {{
$v.as_str().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v))
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_to_str {
($a:ident, $v:ident, $c:ident) => {{
$a.$c(yaml_str!($v))
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_to_bool {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_bool().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)))
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_to_u64 {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_i64().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) as u64)
}};
}
#[cfg(feature = "yaml")]
macro_rules! yaml_to_usize {
($a:ident, $v:ident, $c:ident) => {{
$a.$c($v.as_i64().unwrap_or_else(|| panic!("failed to convert YAML {:?} value to a string", $v)) as usize)

View File

@ -3,23 +3,23 @@ use std::ascii::AsciiExt;
use std::str::FromStr;
bitflags! {
flags Flags: u16 {
const REQUIRED = 1 << 0,
const MULTIPLE = 1 << 1,
const EMPTY_VALS = 1 << 2,
const GLOBAL = 1 << 3,
const HIDDEN = 1 << 4,
const TAKES_VAL = 1 << 5,
const USE_DELIM = 1 << 6,
const NEXT_LINE_HELP = 1 << 7,
const R_UNLESS_ALL = 1 << 8,
const REQ_DELIM = 1 << 9,
const DELIM_NOT_SET = 1 << 10,
const HIDE_POS_VALS = 1 << 11,
const ALLOW_TAC_VALS = 1 << 12,
const REQUIRE_EQUALS = 1 << 13,
const LAST = 1 << 14,
const HIDE_DEFAULT_VAL = 1 << 15,
struct Flags: u16 {
const REQUIRED = 1;
const MULTIPLE = 1 << 1;
const EMPTY_VALS = 1 << 2;
const GLOBAL = 1 << 3;
const HIDDEN = 1 << 4;
const TAKES_VAL = 1 << 5;
const USE_DELIM = 1 << 6;
const NEXT_LINE_HELP = 1 << 7;
const R_UNLESS_ALL = 1 << 8;
const REQ_DELIM = 1 << 9;
const DELIM_NOT_SET = 1 << 10;
const HIDE_POS_VALS = 1 << 11;
const ALLOW_TAC_VALS = 1 << 12;
const REQUIRE_EQUALS = 1 << 13;
const LAST = 1 << 14;
const HIDE_DEFAULT_VAL = 1 << 15;
}
}

View File

@ -82,10 +82,8 @@ fn generate_inner<'a, 'b, 'p>(p: &'p Parser<'a, 'b>, previous_command_name: &str
format!("{}_{}", previous_command_name, &p.meta.name)
};
let mut subcommands_detection_cases = if previous_command_name.is_empty() {
String::new()
} else if !names.contains(&&*p.meta.name) {
names.push(&&*p.meta.name);
let mut subcommands_detection_cases = if !names.contains(&&*p.meta.name) {
names.push(&*p.meta.name);
format!(r"
'{0}' {{
$command += '_{0}'

View File

@ -9,7 +9,7 @@ use std::result::Result as StdResult;
// Internal
use args::{FlagBuilder, AnyArg};
use fmt;
use fmt::{Colorizer, ColorizerOption, ColorWhen};
use suggestions;
/// Short hand for [`Result`] type
@ -408,17 +408,17 @@ impl Error {
pub fn argument_conflict<'a, 'b, A, O, U>(arg: &A,
other: Option<O>,
usage: U,
color: fmt::ColorWhen)
color: ColorWhen)
-> Self
where A: AnyArg<'a, 'b> + Display,
O: Into<String>,
U: Display
{
let mut v = vec![arg.name().to_owned()];
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The argument '{}' cannot be used with {}\n\n\
{}\n\n\
@ -444,14 +444,14 @@ impl Error {
}
#[doc(hidden)]
pub fn empty_value<'a, 'b, A, U>(arg: &A, usage: U, color: fmt::ColorWhen) -> Self
pub fn empty_value<'a, 'b, A, U>(arg: &A, usage: U, color: ColorWhen) -> Self
where A: AnyArg<'a, 'b> + Display,
U: Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The argument '{}' requires a value but none was supplied\
\n\n\
@ -471,21 +471,21 @@ impl Error {
good_vals: &[G],
arg: &A,
usage: U,
color: fmt::ColorWhen)
color: ColorWhen)
-> Self
where B: AsRef<str>,
G: AsRef<str> + Display,
A: AnyArg<'a, 'b> + Display,
U: Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
let suffix =
suggestions::did_you_mean_suffix(bad_val.as_ref(),
good_vals.iter(),
suggestions::DidYouMeanMessageStyle::EnumValue);
suggestions::did_you_mean_value_suffix(
bad_val.as_ref(),
good_vals.iter());
let mut sorted = vec![];
for v in good_vals {
@ -517,7 +517,7 @@ impl Error {
did_you_mean: D,
name: N,
usage: U,
color: fmt::ColorWhen)
color: ColorWhen)
-> Self
where S: Into<String>,
D: AsRef<str> + Display,
@ -525,10 +525,10 @@ impl Error {
U: Display
{
let s = subcmd.into();
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The subcommand '{}' wasn't recognized\n\t\
Did you mean '{}'?\n\n\
@ -550,15 +550,15 @@ impl Error {
}
#[doc(hidden)]
pub fn unrecognized_subcommand<S, N>(subcmd: S, name: N, color: fmt::ColorWhen) -> Self
pub fn unrecognized_subcommand<S, N>(subcmd: S, name: N, color: ColorWhen) -> Self
where S: Into<String>,
N: Display
{
let s = subcmd.into();
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The subcommand '{}' wasn't recognized\n\n\
{}\n\t\
@ -575,14 +575,14 @@ impl Error {
}
#[doc(hidden)]
pub fn missing_required_argument<R, U>(required: R, usage: U, color: fmt::ColorWhen) -> Self
pub fn missing_required_argument<R, U>(required: R, usage: U, color: ColorWhen) -> Self
where R: Display,
U: Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The following required arguments were not provided:{}\n\n\
{}\n\n\
@ -597,14 +597,14 @@ impl Error {
}
#[doc(hidden)]
pub fn missing_subcommand<N, U>(name: N, usage: U, color: fmt::ColorWhen) -> Self
pub fn missing_subcommand<N, U>(name: N, usage: U, color: ColorWhen) -> Self
where N: AsRef<str> + Display,
U: Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} '{}' requires a subcommand, but one was not provided\n\n\
{}\n\n\
@ -620,13 +620,13 @@ impl Error {
#[doc(hidden)]
pub fn invalid_utf8<U>(usage: U, color: fmt::ColorWhen) -> Self
pub fn invalid_utf8<U>(usage: U, color: ColorWhen) -> Self
where U: Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} Invalid UTF-8 was detected in one or more arguments\n\n\
{}\n\n\
@ -643,17 +643,17 @@ impl Error {
pub fn too_many_values<'a, 'b, V, A, U>(val: V,
arg: &A,
usage: U,
color: fmt::ColorWhen)
color: ColorWhen)
-> Self
where V: AsRef<str> + Display + ToOwned,
A: AnyArg<'a, 'b> + Display,
U: Display
{
let v = val.as_ref();
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The value '{}' was provided to '{}', but it wasn't expecting \
any more values\n\n\
@ -674,15 +674,15 @@ impl Error {
min_vals: u64,
curr_vals: usize,
usage: U,
color: fmt::ColorWhen)
color: ColorWhen)
-> Self
where A: AnyArg<'a, 'b> + Display,
U: Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The argument '{}' requires at least {} values, but only {} w{} \
provided\n\n\
@ -701,13 +701,13 @@ impl Error {
}
#[doc(hidden)]
pub fn value_validation<'a, 'b, A>(arg: Option<&A>, err: String, color: fmt::ColorWhen) -> Self
pub fn value_validation<'a, 'b, A>(arg: Option<&A>, err: String, color: ColorWhen) -> Self
where A: AnyArg<'a, 'b> + Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} Invalid value{}: {}",
c.error("error:"),
@ -725,7 +725,7 @@ impl Error {
#[doc(hidden)]
pub fn value_validation_auto(err: String) -> Self {
let n: Option<&FlagBuilder> = None;
Error::value_validation(n, err, fmt::ColorWhen::Auto)
Error::value_validation(n, err, ColorWhen::Auto)
}
#[doc(hidden)]
@ -734,16 +734,16 @@ impl Error {
curr_vals: usize,
suffix: S,
usage: U,
color: fmt::ColorWhen)
color: ColorWhen)
-> Self
where A: AnyArg<'a, 'b> + Display,
S: Display,
U: Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The argument '{}' requires {} values, but {} w{} \
provided\n\n\
@ -762,14 +762,14 @@ impl Error {
}
#[doc(hidden)]
pub fn unexpected_multiple_usage<'a, 'b, A, U>(arg: &A, usage: U, color: fmt::ColorWhen) -> Self
pub fn unexpected_multiple_usage<'a, 'b, A, U>(arg: &A, usage: U, color: ColorWhen) -> Self
where A: AnyArg<'a, 'b> + Display,
U: Display
{
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} The argument '{}' was provided more than once, but cannot \
be used multiple times\n\n\
@ -788,16 +788,16 @@ impl Error {
pub fn unknown_argument<A, U>(arg: A,
did_you_mean: &str,
usage: U,
color: fmt::ColorWhen)
color: ColorWhen)
-> Self
where A: Into<String>,
U: Display
{
let a = arg.into();
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} Found argument '{}' which wasn't expected, or isn't valid in \
this context{}\n\
@ -818,11 +818,11 @@ impl Error {
}
#[doc(hidden)]
pub fn io_error(e: &Error, color: fmt::ColorWhen) -> Self {
let c = fmt::Colorizer {
pub fn io_error(e: &Error, color: ColorWhen) -> Self {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: color,
};
});
Error {
message: format!("{} {}", c.error("error:"), e.description()),
kind: ErrorKind::Io,
@ -835,10 +835,10 @@ impl Error {
where A: Into<String>
{
let a = arg.into();
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: fmt::ColorWhen::Auto,
};
when: ColorWhen::Auto,
});
Error {
message: format!("{} The argument '{}' wasn't found",
c.error("error:"),
@ -853,10 +853,10 @@ impl Error {
/// This can be used in combination with `Error::exit` to exit your program
/// with a custom error message.
pub fn with_description(description: &str, kind: ErrorKind) -> Self {
let c = fmt::Colorizer {
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: fmt::ColorWhen::Auto,
};
when: ColorWhen::Auto,
});
Error {
message: format!("{} {}", c.error("error:"), description),
kind: kind,

View File

@ -7,6 +7,7 @@ use ansi_term::Colour::{Green, Red, Yellow};
#[cfg(feature = "color")]
use atty;
use std::fmt;
use std::env;
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq)]
@ -33,20 +34,23 @@ pub fn is_a_tty(_: bool) -> bool {
false
}
pub fn is_term_dumb() -> bool { env::var("TERM").ok() == Some(String::from("dumb")) }
#[doc(hidden)]
pub struct Colorizer {
pub struct ColorizerOption {
pub use_stderr: bool,
pub when: ColorWhen,
}
#[doc(hidden)]
pub struct Colorizer {
when: ColorWhen,
}
macro_rules! color {
($_self:ident, $c:ident, $m:expr) => {
match $_self.when {
ColorWhen::Auto => if is_a_tty($_self.use_stderr) {
Format::$c($m)
} else {
Format::None($m)
},
ColorWhen::Auto => Format::$c($m),
ColorWhen::Always => Format::$c($m),
ColorWhen::Never => Format::None($m),
}
@ -54,6 +58,18 @@ macro_rules! color {
}
impl Colorizer {
pub fn new(option: ColorizerOption) -> Colorizer {
let is_a_tty = is_a_tty(option.use_stderr);
let is_term_dumb = is_term_dumb();
Colorizer {
when: if is_a_tty && !is_term_dumb {
option.when
} else {
ColorWhen::Never
},
}
}
pub fn good<T>(&self, msg: T) -> Format<T>
where T: fmt::Display + AsRef<str>
{
@ -85,10 +101,10 @@ impl Colorizer {
impl Default for Colorizer {
fn default() -> Self {
Colorizer {
Colorizer::new(ColorizerOption {
use_stderr: true,
when: ColorWhen::Auto,
}
})
}
}

Some files were not shown because too many files have changed in this diff Show More