summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Aaron Murphy <mmstickman@gmail.com>2017-06-09 12:01:03 -0400
committerMichael Aaron Murphy <mmstickman@gmail.com>2017-06-09 12:04:09 -0400
commit5300d5c00b47adb005f36a30cfd61b7766b84117 (patch)
tree2ad17d5fb7c43924c7937adb255c73cf45444b07
parent02da40efb6b8e36f189e5ce348d888d35ef9e90f (diff)
0.11.2: Handful of Improvements0.11.2
- On Linux, a check will be performed on transparent_hupages and issue a warning if it is set to always instead of madvise - The Ion shell is now a preferred shell, with Dash coming as second to Ion - The ArgumentSplitter was improved -- copied from my Ion shell implementation - Invalid arguments will issue warnings instead of crashing the program - If an empty line is passed to standard input, it will be ignored - All Rust dependencies have been updated to their latest versions
-rw-r--r--Cargo.lock72
-rw-r--r--Cargo.toml38
-rw-r--r--README.md22
-rw-r--r--TODO.md5
-rw-r--r--src/arguments/mod.rs26
-rw-r--r--src/execute/argument_splitter.rs110
-rw-r--r--src/execute/command.rs4
-rw-r--r--src/input_iterator/iterator.rs12
-rw-r--r--src/main.rs24
-rw-r--r--src/shell.rs25
10 files changed, 187 insertions, 151 deletions
diff --git a/Cargo.lock b/Cargo.lock
index a7eb491..c01308d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1,35 +1,35 @@
[root]
name = "parallel"
-version = "0.11.1"
+version = "0.11.2"
dependencies = [
- "arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "num_cpus 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"numtoa 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
- "permutate 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "permutate 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
+ "smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"sys-info 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
- "time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
- "wait-timeout 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
+ "wait-timeout 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "arrayvec"
-version = "0.3.20"
+version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "nodrop 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+ "nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
"odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "gcc"
-version = "0.3.42"
+version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itoa"
-version = "0.1.1"
+version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -43,12 +43,12 @@ dependencies = [
[[package]]
name = "libc"
-version = "0.2.20"
+version = "0.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nodrop"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -56,10 +56,10 @@ dependencies = [
[[package]]
name = "num_cpus"
-version = "1.2.1"
+version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -74,17 +74,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "permutate"
-version = "0.2.0"
+version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "redox_syscall"
-version = "0.1.16"
+version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "smallvec"
-version = "0.3.1"
+version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@@ -92,26 +92,26 @@ name = "sys-info"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)",
+ "gcc 0.3.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "time"
-version = "0.1.36"
+version = "0.1.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
- "redox_syscall 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
+ "redox_syscall 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wait-timeout"
-version = "0.1.3"
+version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
- "libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)",
+ "libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -125,20 +125,20 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
-"checksum arrayvec 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d89f1b0e242270b5b797778af0c8d182a1a2ccac5d8d6fadf414223cc0fab096"
-"checksum gcc 0.3.42 (registry+https://github.com/rust-lang/crates.io-index)" = "291055c78f59ca3d84c99026c9501c469413d386bb46be1e1cf1d285cd1db3b0"
-"checksum itoa 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ae3088ea4baeceb0284ee9eea42f591226e6beaecf65373e41b38d95a1b8e7a1"
+"checksum arrayvec 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)" = "699e63a93b79d717e8c3b5eb1b28b7780d0d6d9e59a72eb769291c83b0c8dc67"
+"checksum gcc 0.3.50 (registry+https://github.com/rust-lang/crates.io-index)" = "5f837c392f2ea61cb1576eac188653df828c861b7137d74ea4a5caa89621f9e6"
+"checksum itoa 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2f404fbc66fd9aac13e998248505e7ecb2ad8e44ab6388684c5fb11c6c251c"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
-"checksum libc 0.2.20 (registry+https://github.com/rust-lang/crates.io-index)" = "684f330624d8c3784fb9558ca46c4ce488073a8d22450415c5eb4f4cfb0d11b5"
-"checksum nodrop 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0dbbadd3f4c98dea0bd3d9b4be4c0cdaf1ab57035cb2e41fce3983db5add7cc5"
-"checksum num_cpus 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a225d1e2717567599c24f88e49f00856c6e825a12125181ee42c4257e3688d39"
+"checksum libc 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "e7eb6b826bfc1fdea7935d46556250d1799b7fe2d9f7951071f4291710665e3e"
+"checksum nodrop 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "52cd74cd09beba596430cc6e3091b74007169a56246e1262f0ba451ea95117b2"
+"checksum num_cpus 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6e416ba127a4bb3ff398cb19546a8d0414f73352efe2857f4060d36f5fe5983a"
"checksum numtoa 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5c8fcb09df1eba2d128cfc50bdb0c3f33ac4f91fdacc2d3ac039b3b192398f8f"
"checksum odds 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "c3df9b730298cea3a1c3faa90b7e2f9df3a9c400d0936d6015e6165734eefcba"
-"checksum permutate 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1b4ba980af238a6d6fcc0df53fe0d7920376bc4ce2c6ce298992891a230b47a8"
-"checksum redox_syscall 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "8dd35cc9a8bdec562c757e3d43c1526b5c6d2653e23e2315065bc25556550753"
-"checksum smallvec 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a3c84984c278afe61a46e19868e8b23e2ee3be5b3cc6dea6edad4893bc6c841"
+"checksum permutate 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53b7d5b19a715ffab38693a9dd44b067fdfa2b18eef65bd93562dfe507022fae"
+"checksum redox_syscall 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "3041aeb6000db123d2c9c751433f526e1f404b23213bd733167ab770c3989b4d"
+"checksum smallvec 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e40af10aafe98b4d8294ae8388d8a5cd0707c65d364872efe72d063ec44bee0"
"checksum sys-info 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "246f8bbb443ae0026c92266c26d997d04634c8b44c3baff9fa3bff445b40878c"
-"checksum time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "211b63c112206356ef1ff9b19355f43740fc3f85960c598a93d3a3d3ba7beade"
-"checksum wait-timeout 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e8ab44ea5707ff5261a802e8be19697a2e342ecc55409b062c285fc4f624d05"
+"checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3"
+"checksum wait-timeout 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b9f3bf741a801531993db6478b95682117471f76916f5e690dd8d45395b09349"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
diff --git a/Cargo.toml b/Cargo.toml
index 4589f00..7e44407 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "parallel"
-version = "0.11.1"
+version = "0.11.2"
authors = ["Michael Aaron Murphy <mmstickman@gmail.com>"]
license = "MIT"
description = "Command-line CPU load balancer for executing jobs in parallel"
@@ -10,40 +10,12 @@ categories = ["command-line-utilities"]
readme = "README.md"
[dependencies]
-itoa = "0.1"
+itoa = "0.3"
numtoa = "0.0"
-num_cpus = "1.2"
-permutate = "0.2"
+num_cpus = "1.5"
+permutate = "0.3"
arrayvec = "0.3"
time = "0.1"
-smallvec = "0.3"
+smallvec = "0.4"
sys-info = "0.4"
wait-timeout = "0.1"
-
-[profile.release]
-opt-level = 3
-lto = true
-
-[package.metadata.deb]
-maintainer = "Michael Aaron Murphy <mmstickman@gmail.com>"
-copyright = "2016-2017, Michael Aaron Murphy <mmstickman@gmail.com>"
-license_file = ["LICENSE", "4"]
-extended_description = """\
-A CPU load balancer for the command-line, written in Rust and inspired by \
-GNU Parallel. The purpose of the application is to parallelize otherwise \
-serial tasks by evenly distributing tasks to all CPU cores in a system.
-
-The program is supplied with a command template and a list of inputs, \
-where those inputs will be evenly passed to each core in the system. \
-Inputs are processed serially in parallel, as in each job will be assigned \
-the next task in the queue as soon as a job completes. In addition, the \
-standard output/error of each task will be buffered so that they are \
-printed in the order that inputs are received, as if the commands were \
-executed serially in a traditional for loop."""
-depends = "$auto"
-section = "utils"
-priority = "optional"
-assets = [
- ["target/release/parallel", "usr/bin/", "755"],
- ["README.md", "usr/share/doc/parallel/README", "644"],
-]
diff --git a/README.md b/README.md
index 47bc0fc..ab70ed7 100644
--- a/README.md
+++ b/README.md
@@ -8,28 +8,6 @@ This is an attempt at recreating the functionality of [GNU Parallel](https://www
## Note
-A nightly version of the Rust compiler is required to build this application.
-This is required to disable `jemalloc` because it incurs a performance overhead on Linux systems.
-In addition to using a nightly compiler, it is recommended to compile with a musl target.
-
-```
-rustup install nightly
-rustup target add x86_64-unknown-linux-musl
-rustup run nightly cargo build --release --target x86_64-unknown-linux-musl
-```
-
-Additionally, `transparent_hugepages` causes serious performance issues with the implementation
-of parallel, so it is recommended to ensure that your Linux distribution has the parameter set
-to `madvise`:
-
-```
-sudo sh -c "echo madvise > /sys/kernel/mm/transparent_hugepage/enabled"
-```
-
-It's a good idea to install the `dash` shell as this implementation of Parallel will try to use it by default.
-Dash is basically a superior implementation of `sh` that's many times faster and more secure.
-If `dash` cannot be found, it will default to `sh`. Although on Windows it will default to `cmd`.
-
See the [to-do list](https://github.com/mmstick/parallel/blob/master/TODO.md) for features and improvements that have yet to be done. If you want to contribute, pull requests are welcome. If you have an idea for improvement which isn't listed in the to-do list, feel free to [email me](mailto:mmstickman@gmail.com) and I will consider implementing that idea.
## Benchmark Comparison to GNU Parallel
diff --git a/TODO.md b/TODO.md
index 7a3f769..55777f3 100644
--- a/TODO.md
+++ b/TODO.md
@@ -7,6 +7,11 @@ The list is actively updated with each successful pull request.
- Eliminate the need to run commands within a shell
- Implement `progress`
- Fix `-n` issue when using `{1..}` tokens
+- Compress arguments written to the disk with Brotli
+- Re-implement in-memory argument passing versus disk-exclusive argument iteration
+- Rewrite the arguments module
+- Rewrite the error handling logic
+- Utilize the crossbeam crate so that strings don't need to be leaked
## May or may not implement
- Kill the youngest job and add it to the back of the queue if available memory is 50% less than `memfree`'s value.
diff --git a/src/arguments/mod.rs b/src/arguments/mod.rs
index 66d6470..06a4dbe 100644
--- a/src/arguments/mod.rs
+++ b/src/arguments/mod.rs
@@ -35,6 +35,7 @@ pub const SHELL_QUOTE: u16 = 128;
pub const ETA: u16 = 256;
pub const JOBLOG: u16 = 512;
pub const JOBLOG_8601: u16 = 1024;
+pub const ION_EXISTS: u16 = 2048;
/// `Args` is a collection of critical options and arguments that were collected at
/// startup of the application.
@@ -118,13 +119,13 @@ impl Args {
println!("{}", man::MAN_PAGE);
exit(0);
},
- b'k' => (),
b'p' => self.flags |= PIPE_IS_ENABLED,
b'q' => quote_enabled = true,
b's' => self.flags |= QUIET_MODE,
b'v' => self.flags |= VERBOSE_MODE,
_ => {
- return Err(ParseErr::InvalidArgument(index-1))
+ let stderr = io::stderr();
+ let _ = writeln!(stderr.lock(), "parallel: unsupported argument: '-{}'", character as char);
}
}
}
@@ -143,8 +144,6 @@ impl Args {
println!("{}", man::MAN_PAGE);
exit(0);
},
- "keep-order" => (),
- "group" => (),
"joblog" => {
let file = arguments.get(index).ok_or(ParseErr::JoblogNoValue)?;
self.joblog = Some(file.to_owned());
@@ -157,7 +156,6 @@ impl Args {
if val != 0 { self.ncores = val; }
index += 1;
},
- "line-buffer" | "lb" => (),
"num-cpu-cores" => {
println!("{}", num_cpus::get());
exit(0);
@@ -172,7 +170,6 @@ impl Args {
self.memory = parse_memory(val).map_err(|_| ParseErr::MemInvalid(index))?;
index += 1;
},
- "no-notice" => (),
"pipe" => self.flags |= PIPE_IS_ENABLED,
"quiet" | "silent" => self.flags |= QUIET_MODE,
"quote" => quote_enabled = true,
@@ -183,13 +180,11 @@ impl Args {
self.timeout = Duration::from_millis((seconds * 1000f64) as u64);
index += 1;
},
- "ungroup" => (),
"verbose" => self.flags |= VERBOSE_MODE,
"version" => {
println!("MIT/Rust Parallel {}", env!("CARGO_PKG_VERSION"));
exit(0);
},
- "will-cite" => (),
"tmpdir" | "tempdir" => {
*base_path = PathBuf::from(arguments.get(index).ok_or(ParseErr::WorkDirNoValue)?);
index += 1;
@@ -207,7 +202,10 @@ impl Args {
comm.push_str(&argument[10..]);
break
},
- _ => return Err(ParseErr::InvalidArgument(index-1)),
+ _ => {
+ let stderr = io::stderr();
+ let _ = writeln!(stderr.lock(), "parallel: unsupported argument: '{}'", argument);
+ }
}
}
} else {
@@ -389,8 +387,9 @@ fn write_stdin_to_disk(max_args: usize, mut unprocessed_path: PathBuf, inputs_ar
let stdin = io::stdin();
if max_args < 2 {
- for line in stdin.lock().lines() {
+ for line in BufReader::new(stdin.lock()).lines() {
if let Ok(line) = parse_line(line) {
+ if line.is_empty() { continue }
disk_buffer.write(line.as_bytes()).and_then(|_| disk_buffer.write(b"\n"))
.map_err(|why| FileErr::Write(unprocessed_path.clone(), why))?;
number_of_arguments += 1;
@@ -398,8 +397,9 @@ fn write_stdin_to_disk(max_args: usize, mut unprocessed_path: PathBuf, inputs_ar
}
} else {
let mut max_args_index = max_args;
- for line in stdin.lock().lines() {
+ for line in BufReader::new(stdin.lock()).lines() {
if let Ok(line) = parse_line(line) {
+ if line.is_empty() { continue }
if max_args_index == max_args {
max_args_index -= 1;
number_of_arguments += 1;
@@ -466,7 +466,7 @@ fn write_inputs_to_disk(lists: Vec<Vec<String>>, current_inputs: Vec<String>, ma
if max_args < 2 {
disk_buffer.write(b"\n").map_err(|why| FileErr::Write(unprocessed_path.clone(), why))?;
- while let Ok(true) = permutator.next_with_buffer(&mut permutation_buffer) {
+ while permutator.next_with_buffer(&mut permutation_buffer) {
let mut iter = permutation_buffer.iter();
disk_buffer.write(iter.next().unwrap().as_bytes())
.map_err(|why| FileErr::Write(unprocessed_path.clone(), why))?;
@@ -480,7 +480,7 @@ fn write_inputs_to_disk(lists: Vec<Vec<String>>, current_inputs: Vec<String>, ma
}
} else {
let mut max_args_index = max_args - 1;
- while let Ok(true) = permutator.next_with_buffer(&mut permutation_buffer) {
+ while permutator.next_with_buffer(&mut permutation_buffer) {
let mut iter = permutation_buffer.iter();
if max_args_index == max_args {
max_args_index -= 1;
diff --git a/src/execute/argument_splitter.rs b/src/execute/argument_splitter.rs
index ef1646c..5dcd7d8 100644
--- a/src/execute/argument_splitter.rs
+++ b/src/execute/argument_splitter.rs
@@ -1,10 +1,14 @@
const DOUBLE: u8 = 1;
const SINGLE: u8 = 2;
const BACK: u8 = 4;
+const COMM_1: u8 = 8;
+const COMM_2: u8 = 16;
+const VARIAB: u8 = 32;
+const ARRAY: u8 = 64;
+const METHOD: u8 = 128;
-/// An efficient `Iterator` structure for splitting arguments
+/// An argument splitter that is optimized for parsing logic for the Ion shell.
pub struct ArgumentSplitter<'a> {
- buffer: Vec<u8>,
data: &'a str,
read: usize,
flags: u8,
@@ -13,7 +17,6 @@ pub struct ArgumentSplitter<'a> {
impl<'a> ArgumentSplitter<'a> {
pub fn new(data: &'a str) -> ArgumentSplitter<'a> {
ArgumentSplitter {
- buffer: Vec::with_capacity(32),
data: data,
read: 0,
flags: 0,
@@ -22,33 +25,58 @@ impl<'a> ArgumentSplitter<'a> {
}
impl<'a> Iterator for ArgumentSplitter<'a> {
- type Item = String;
+ type Item = &'a str;
- fn next(&mut self) -> Option<String> {
- for character in self.data.bytes().skip(self.read) {
+ fn next(&mut self) -> Option<&'a str> {
+ while let Some(&b' ') = self.data.as_bytes().get(self.read) {
self.read += 1;
+ }
+ let start = self.read;
+
+ let (mut level, mut array_level, mut array_process_level) = (0, 0, 0);
+ for character in self.data.bytes().skip(self.read) {
match character {
- _ if self.flags & BACK != 0 => {
- self.buffer.push(character);
- self.flags ^= BACK;
+ _ if self.flags & BACK != 0 => self.flags ^= BACK,
+ b'\\' => self.flags ^= BACK,
+ b'@' if self.flags & SINGLE == 0 => {
+ self.flags &= 255 ^ COMM_1;
+ self.flags |= COMM_2 + ARRAY;
+ self.read += 1;
+ continue
+ },
+ b'$' if self.flags & SINGLE == 0 => {
+ self.flags &= 255 ^ COMM_2;
+ self.flags |= COMM_1 + VARIAB;
+ self.read += 1;
+ continue
},
+ b'[' if self.flags & SINGLE == 0 && self.flags & COMM_2 != 0 => array_process_level += 1,
+ b'[' if self.flags & SINGLE == 0 => array_level += 1,
+ b']' if self.flags & SINGLE == 0 && array_level != 0 => array_level -= 1,
+ b']' if self.flags & SINGLE == 0 => array_process_level -= 1,
+ b'(' if self.flags & SINGLE == 0 && self.flags & COMM_1 != 0 => level += 1,
+ b'(' if self.flags & SINGLE == 0 && self.flags & (VARIAB + ARRAY) != 0 => {
+ self.flags |= METHOD;
+ self.flags &= 255 ^ (VARIAB + ARRAY);
+ },
+ b')' if self.flags & SINGLE == 0 && self.flags & METHOD != 0 => {
+ self.flags &= 255 ^ METHOD;
+ },
+ b')' if self.flags & SINGLE == 0 => level -= 1,
b'"' if self.flags & SINGLE == 0 => self.flags ^= DOUBLE,
b'\'' if self.flags & DOUBLE == 0 => self.flags ^= SINGLE,
- b' ' if !self.buffer.is_empty() & (self.flags & (SINGLE + DOUBLE) == 0) => break,
- b'\\' if (self.flags & (SINGLE + DOUBLE) == 0) => self.flags ^= BACK,
- _ => {
- self.buffer.push(character);
- }
+ b' ' if self.flags & (SINGLE + DOUBLE + METHOD) == 0
+ && level == 0 && array_level == 0 && array_process_level == 0 => break,
+ _ => ()
}
+ self.read += 1;
+ self.flags &= 255 ^ (COMM_1 + COMM_2);
}
- if self.buffer.is_empty() {
+ if start == self.read {
None
} else {
- let mut output = self.buffer.clone();
- output.shrink_to_fit();
- self.buffer.clear();
- Some(unsafe { String::from_utf8_unchecked(output) })
+ Some(&self.data[start..self.read])
}
}
}
@@ -57,20 +85,40 @@ impl<'a> Iterator for ArgumentSplitter<'a> {
mod tests {
use super::*;
+ fn compare(input: &str, expected: Vec<&str>) {
+ let arguments = ArgumentSplitter::new(input).collect::<Vec<&str>>();
+ for (left, right) in expected.iter().zip(arguments.iter()) {
+ assert_eq!(left, right);
+ }
+ assert_eq!(expected.len(), arguments.len());
+ }
+
#[test]
- fn test_split_args() {
- use std::str;
+ fn methods() {
+ let input = "echo $join(array, ', ') @split(var, ', ')";
+ let expected = vec!["echo", "$join(array, ', ')", "@split(var, ', ')"];
+ compare(input, expected);
+ }
+
+ #[test]
+ fn processes() {
+ let input = "echo $(echo one $(echo two)) @[echo one @[echo two]]";
+ let expected = vec!["echo", "$(echo one $(echo two))", "@[echo one @[echo two]]"];
+ compare(input, expected);
+ }
- let argument = ArgumentSplitter::new("ffmpeg -i \"file with spaces\" \"output with spaces\"");
- let expected = vec!["ffmpeg", "-i", "file with spaces", "output with spaces"];
- let argument = argument.collect::<Vec<Vec<u8>>>();
- let argument = argument.iter().map(|x| str::from_utf8(x).unwrap()).collect::<Vec<&str>>();
- assert_eq!(argument, expected);
- let argument = ArgumentSplitter::new("one\\ two\\\\ a\\\'b\\\"c");
- let expected = vec!["one two\\", "a\'b\"c"];
- let argument = argument.collect::<Vec<Vec<u8>>>();
- let argument = argument.iter().map(|x| str::from_utf8(x).unwrap()).collect::<Vec<&str>>();
- assert_eq!(argument, expected);
+ #[test]
+ fn arrays() {
+ let input = "echo [ one two @[echo three four] five ] [ six seven ]";
+ let expected = vec!["echo", "[ one two @[echo three four] five ]", "[ six seven ]"];
+ compare(input, expected);
+ }
+
+ #[test]
+ fn quotes() {
+ let input = "echo 'one two \"three four\"' \"five six 'seven eight'\"";
+ let expected = vec!["echo", "'one two \"three four\"'", "\"five six 'seven eight'\""];
+ compare(input, expected);
}
}
diff --git a/src/execute/command.rs b/src/execute/command.rs
index ebd1209..6f4e681 100644
--- a/src/execute/command.rs
+++ b/src/execute/command.rs
@@ -100,7 +100,7 @@ pub fn get_command_output(command: &str, flags: u16) -> io::Result<Child> {
shell_output(command, flags)
} else {
// Collect each argument into a vector
- let arguments = ArgumentSplitter::new(command).collect::<Vec<String>>();
+ let arguments = ArgumentSplitter::new(command).collect::<Vec<&str>>();
match (arguments.len() == 1, flags & arguments::QUIET_MODE != 0, flags & arguments::PIPE_IS_ENABLED != 0) {
(true, true, false) => Command::new(&arguments[0])
.stdout(Stdio::null()).stderr(Stdio::piped())
@@ -134,6 +134,8 @@ pub fn get_command_output(command: &str, flags: u16) -> io::Result<Child> {
fn shell_output<S: AsRef<OsStr>>(args: S, flags: u16) -> io::Result<Child> {
let (cmd, flag) = if cfg!(windows) {
("cmd".to_owned(), "/C")
+ } else if flags & arguments::ION_EXISTS != 0 {
+ ("ion".to_owned(), "-c")
} else if flags & arguments::DASH_EXISTS != 0 {
("dash".to_owned(), "-c")
} else {
diff --git a/src/input_iterator/iterator.rs b/src/input_iterator/iterator.rs
index 3b5032f..00c0c2a 100644
--- a/src/input_iterator/iterator.rs
+++ b/src/input_iterator/iterator.rs
@@ -16,27 +16,27 @@ pub struct ETA {
impl ETA {
pub fn write_to_stderr(&self, completed: usize) {
let stderr = io::stderr();
- let mut stderr = &mut stderr.lock();
+ let mut stderr = stderr.lock();
// Write the ETA to the standard error
let _ = stderr.write(b"ETA: ");
- let _ = itoa::write(stderr, self.time / 1_000_000_000);
+ let _ = itoa::write(&mut stderr, self.time / 1_000_000_000);
// Write the number of inputs left to process to standard error
let _ = stderr.write(b"s Left: ");
- let _ = itoa::write(stderr, self.left);
+ let _ = itoa::write(&mut stderr, self.left);
// Write the average runtime of processes (with two decimal places) to standard error
let _ = stderr.write(b" AVG: ");
- let _ = itoa::write(stderr, self.average / 1_000_000_000);
+ let _ = itoa::write(&mut stderr, self.average / 1_000_000_000);
let _ = stderr.write(b".");
let remainder = (self.average % 1_000_000_000) / 10_000_000;
- let _ = itoa::write(stderr, remainder);
+ let _ = itoa::write(&mut stderr, remainder);
if remainder < 10 { let _ = stderr.write(b"0"); }
// Write the number of completed units so far to standard error
let _ = stderr.write(b"s Completed: ");
- let _ = itoa::write(stderr, completed);
+ let _ = itoa::write(&mut stderr, completed);
let _ = stderr.write(b"\n");
}
}
diff --git a/src/main.rs b/src/main.rs
index 2898fb6..d9e309d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,8 +1,5 @@
#![deny(dead_code)]
#![allow(unknown_lints)]
-#![feature(loop_break_value)]
-#![feature(alloc_system)]
-extern crate alloc_system;
extern crate arrayvec;
extern crate itoa;
extern crate numtoa;
@@ -25,7 +22,7 @@ mod verbose;
use std::env;
use std::fs::{self, create_dir_all, File};
-use std::io::{self, BufRead, BufReader, Write};
+use std::io::{self, BufRead, BufReader, Read, Write};
use std::mem;
use std::process::exit;
use std::thread::{self, JoinHandle};
@@ -57,6 +54,18 @@ fn main() {
let stdout = io::stdout();
let stderr = io::stderr();
+ // On Linux systems, check if transparent_hugepages is set to always and issue a warning if true.
+ if cfg!(target_os = "linux") {
+ if let Ok(mut file) = File::open("/sys/kernel/mm/transparent_hugepage/enabled") {
+ let mut buffer: [u8; 2] = [0, 0];
+ if let Ok(_) = file.read_exact(&mut buffer) {
+ if &buffer == b"[a" {
+ let _ = writeln!(stderr.lock(), "ion: /sys/kernel/mm/transparent_hugepage/enabled is set to always instead of madvise. This will gravely effect the performance of Parallel.");
+ }
+ }
+ }
+ }
+
// Parse arguments and collect flags and statistics.
let mut args = Args::new();
let mut comm = String::with_capacity(128);
@@ -162,8 +171,11 @@ fn main() {
// The `slot` variable is required by the {%} token.
if args.flags & arguments::INPUTS_ARE_COMMANDS != 0 {
- // Dash kills all other shells in performance, so if it exists, we need to know.
- if shell::dash_exists() { args.flags |= arguments::DASH_EXISTS; }
+ if shell::ion_exists() {
+ args.flags |= arguments::ION_EXISTS;
+ } else if shell::dash_exists() {
+ args.flags |= arguments::DASH_EXISTS;
+ }
for _ in 0..args.ncores {
let flags = args.flags;
diff --git a/src/shell.rs b/src/shell.rs
index b123a75..6af26da 100644
--- a/src/shell.rs
+++ b/src/shell.rs
@@ -15,13 +15,13 @@ pub fn required(kind: Kind) -> bool {
Kind::Tokens(arguments) => {
for token in arguments {
if let Token::Argument(ref arg) = *token {
- if arg.contains(';') || arg.contains('&') || arg.contains('|') {
+ if arg.as_bytes().iter().any(|&x| x == b';' || x == b'&' || x == b'|' || x == b'$' || x == b'<' || x == b'>' || x == b'[' || x == b']' || x == b'@') {
return true
}
}
}
},
- Kind::Input(arg) => if arg.contains(';') || arg.contains('&') || arg.contains('|') {
+ Kind::Input(arg) => if arg.as_bytes().iter().any(|&x| x == b';' || x == b'&' || x == b'|' || x == b'$' || x == b'<' || x == b'>' || x == b'[' || x == b']' || x == b'@') {
return true
}
}
@@ -45,10 +45,29 @@ pub fn dash_exists() -> bool {
false
}
+/// Returns `true` if the Ion shell was found within the `PATH` environment variable.
+pub fn ion_exists() -> bool {
+ if let Ok(path) = env::var("PATH") {
+ for path in path.split(':') {
+ if let Ok(directory) = fs::read_dir(path) {
+ for entry in directory {
+ if let Ok(entry) = entry {
+ let path = entry.path();
+ if path.is_file() && path.file_name() == Some(OsStr::new("ion")) { return true; }
+ }
+ }
+ }
+ }
+ }
+ false
+}
+
/// Sets the corresponding flags if a shell is required and if dash exists.
pub fn set_flags(flags: &mut u16, arguments: &[Token]) {
if required(Kind::Tokens(arguments)) {
- if dash_exists() {
+ if ion_exists() {
+ *flags |= arguments::SHELL_ENABLED + arguments::ION_EXISTS;
+ } else if dash_exists() {
*flags |= arguments::SHELL_ENABLED + arguments::DASH_EXISTS;
} else {
*flags |= arguments::SHELL_ENABLED;