Skip to content

Commit 0162dae

Browse files
committed
yes: align buf, improve |pv>/dev/null
1 parent 62594c0 commit 0162dae

4 files changed

Lines changed: 57 additions & 15 deletions

File tree

Cargo.lock

Lines changed: 30 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@ version = "0.7.0"
372372

373373
[workspace.dependencies]
374374
ansi-width = "0.1.0"
375+
aligned-vec = "0.6.4"
375376
bigdecimal = "0.4"
376377
binary-heap-plus = "0.5.0"
377378
bstr = "1.9.1"

src/uu/yes/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ workspace = true
1919
path = "src/yes.rs"
2020

2121
[dependencies]
22+
aligned-vec = { workspace = true }
2223
clap = { workspace = true }
2324
itertools = { workspace = true }
2425
fluent = { workspace = true }

src/uu/yes/src/yes.rs

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// file that was distributed with this source code.
55

66
// cSpell:ignore strs
7-
7+
use aligned_vec::{AVec, Alignment, ConstAlign};
88
use clap::{Arg, ArgAction, Command, builder::ValueParser};
99
use std::error::Error;
1010
use std::ffi::OsString;
@@ -14,14 +14,15 @@ use uucore::format_usage;
1414
use uucore::translate;
1515

1616
// it's possible that using a smaller or larger buffer might provide better performance on some
17-
// systems, but honestly this is good enough
17+
// systems, but honestly this is good enough (without zero-copy)
18+
// but let it multiple of page size at least for
1819
const BUF_SIZE: usize = 16 * 1024;
1920

2021
#[uucore::main]
2122
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
2223
let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?;
2324

24-
let mut buffer = Vec::with_capacity(BUF_SIZE);
25+
let mut buffer: AVec<u8, ConstAlign<BUF_SIZE>> = AVec::with_capacity(BUF_SIZE, BUF_SIZE);
2526
#[allow(clippy::unwrap_used, reason = "clap provides 'y' by default")]
2627
let _ = args_into_buffer(&mut buffer, matches.get_many::<OsString>("STRING").unwrap());
2728
prepare_buffer(&mut buffer);
@@ -55,8 +56,8 @@ pub fn uu_app() -> Command {
5556

5657
/// Copies words from `i` into `buf`, separated by spaces.
5758
#[allow(clippy::unnecessary_wraps, reason = "needed on some platforms")]
58-
fn args_into_buffer<'a>(
59-
buf: &mut Vec<u8>,
59+
fn args_into_buffer<'a, A: Alignment>(
60+
buf: &mut AVec<u8, A>,
6061
i: impl Iterator<Item = &'a OsString>,
6162
) -> Result<(), Box<dyn Error>> {
6263
// On Unix (and wasi), OsStrs are just &[u8]'s underneath...
@@ -91,7 +92,7 @@ fn args_into_buffer<'a>(
9192

9293
/// Assumes buf holds a single output line forged from the command line arguments, copies it
9394
/// repeatedly until the buffer holds as many copies as it can under [`BUF_SIZE`].
94-
fn prepare_buffer(buf: &mut Vec<u8>) {
95+
fn prepare_buffer<A: Alignment>(buf: &mut AVec<u8, A>) {
9596
if buf.len() * 2 > BUF_SIZE {
9697
return;
9798
}
@@ -102,9 +103,14 @@ fn prepare_buffer(buf: &mut Vec<u8>) {
102103
let target_size = line_len * (BUF_SIZE / line_len);
103104

104105
while buf.len() < target_size {
105-
let to_copy = std::cmp::min(target_size - buf.len(), buf.len());
106+
let current_len = buf.len();
107+
let to_copy = std::cmp::min(target_size - current_len, current_len);
106108
debug_assert_eq!(to_copy % line_len, 0);
107-
buf.extend_from_within(..to_copy);
109+
#[allow(
110+
clippy::unnecessary_to_owned,
111+
reason = "needs useless copy without unsafe"
112+
)]
113+
buf.extend_from_slice(&buf[..to_copy].to_vec());
108114
}
109115
}
110116

@@ -142,7 +148,8 @@ mod tests {
142148
];
143149

144150
for (line, final_len) in tests {
145-
let mut v = std::iter::repeat_n(b'a', line).collect::<Vec<_>>();
151+
let mut v: AVec<u8, ConstAlign<BUF_SIZE>> =
152+
AVec::from_iter(BUF_SIZE, std::iter::repeat_n(b'a', line));
146153
prepare_buffer(&mut v);
147154
assert_eq!(v.len(), final_len);
148155
}
@@ -151,24 +158,27 @@ mod tests {
151158
#[test]
152159
fn test_args_into_buf() {
153160
{
154-
let mut v = Vec::with_capacity(BUF_SIZE);
161+
let mut v: AVec<u8, ConstAlign<BUF_SIZE>> = AVec::with_capacity(BUF_SIZE, BUF_SIZE);
155162
let default_args = ["y".into()];
156163
args_into_buffer(&mut v, default_args.iter()).unwrap();
157-
assert_eq!(String::from_utf8(v).unwrap(), "y\n");
164+
assert_eq!(String::from_utf8(v.to_vec()).unwrap(), "y\n");
158165
}
159166

160167
{
161-
let mut v = Vec::with_capacity(BUF_SIZE);
168+
let mut v: AVec<u8, ConstAlign<BUF_SIZE>> = AVec::with_capacity(BUF_SIZE, BUF_SIZE);
162169
let args = ["foo".into()];
163170
args_into_buffer(&mut v, args.iter()).unwrap();
164-
assert_eq!(String::from_utf8(v).unwrap(), "foo\n");
171+
assert_eq!(String::from_utf8(v.to_vec()).unwrap(), "foo\n");
165172
}
166173

167174
{
168-
let mut v = Vec::with_capacity(BUF_SIZE);
175+
let mut v: AVec<u8, ConstAlign<BUF_SIZE>> = AVec::with_capacity(BUF_SIZE, BUF_SIZE);
169176
let args = ["foo".into(), "bar baz".into(), "qux".into()];
170177
args_into_buffer(&mut v, args.iter()).unwrap();
171-
assert_eq!(String::from_utf8(v).unwrap(), "foo bar baz qux\n");
178+
assert_eq!(
179+
String::from_utf8(v.to_vec()).unwrap(),
180+
"foo bar baz qux\n"
181+
);
172182
}
173183
}
174184
}

0 commit comments

Comments
 (0)