Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/uu/csplit/src/split_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::csplit_error::CsplitError;
/// format.
pub struct SplitName {
prefix: Vec<u8>,
format: Format<UnsignedInt>,
format: Format<UnsignedInt, u64>,
}

impl SplitName {
Expand Down Expand Up @@ -52,7 +52,7 @@ impl SplitName {
None => format!("%0{n_digits}u"),
};

let format = match Format::<UnsignedInt>::parse(format_string) {
let format = match Format::<UnsignedInt, u64>::parse(format_string) {
Ok(format) => Ok(format),
Err(FormatError::TooManySpecs(_)) => Err(CsplitError::SuffixFormatTooManyPercents),
Err(_) => Err(CsplitError::SuffixFormatIncorrect),
Expand Down
2 changes: 1 addition & 1 deletion src/uu/dd/src/progress.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl ProgUpdate {
variant: FloatVariant::Shortest,
..Default::default()
}
.fmt(&mut duration_str, duration)?;
.fmt(&mut duration_str, &duration.into())?;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems unrelated? no ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is. The float formatter now takes an &ExtendedBigDecimal (and not a f64).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(it's a bit unfortunate and the precision isn't necessary here, but I don't think that single call warrants duplicating formatter for ExtendedBigDecimal and f64)

// We assume that printf will output valid UTF-8
let duration_str = std::str::from_utf8(&duration_str).unwrap();

Expand Down
10 changes: 10 additions & 0 deletions src/uu/seq/BENCHMARKING.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ hyperfine -L seq seq,./target/release/seq "{seq} 0 0.000001 1"
hyperfine -L seq seq,./target/release/seq "{seq} -100 1 1000000"
```

It is also interesting to compare performance with large precision
format. But in this case, the output itself should also be compared,
as GNU `seq` may not provide the same precision (`uutils` version of
`seq` provides arbitrary precision, while GNU `seq` appears to be
limited to `long double` on the given platform, i.e. 64/80/128-bit
float):
```shell
hyperfine -L seq seq,target/release/seq "{seq} -f%.30f 0 0.000001 1"
```

## Optimizations

### Buffering stdout
Expand Down
2 changes: 1 addition & 1 deletion src/uu/seq/src/hexadecimalfloat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore extendedbigdecimal bigdecimal hexdigit numberparse
use crate::extendedbigdecimal::ExtendedBigDecimal;
use crate::number::PreciseNumber;
use crate::numberparse::ParseNumberError;
use bigdecimal::BigDecimal;
use num_traits::FromPrimitive;
use uucore::format::ExtendedBigDecimal;

/// The base of the hex number system
const HEX_RADIX: u32 = 16;
Expand Down
2 changes: 1 addition & 1 deletion src/uu/seq/src/number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// spell-checker:ignore extendedbigdecimal
use num_traits::Zero;

use crate::extendedbigdecimal::ExtendedBigDecimal;
use uucore::format::ExtendedBigDecimal;

/// A number with a specified number of integer and fractional digits.
///
Expand Down
4 changes: 2 additions & 2 deletions src/uu/seq/src/numberparse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use num_bigint::Sign;
use num_traits::Num;
use num_traits::Zero;

use crate::extendedbigdecimal::ExtendedBigDecimal;
use crate::hexadecimalfloat;
use crate::number::PreciseNumber;
use uucore::format::ExtendedBigDecimal;

/// An error returned when parsing a number fails.
#[derive(Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -381,8 +381,8 @@ impl FromStr for PreciseNumber {
#[cfg(test)]
mod tests {
use bigdecimal::BigDecimal;
use uucore::format::ExtendedBigDecimal;

use crate::extendedbigdecimal::ExtendedBigDecimal;
use crate::number::PreciseNumber;
use crate::numberparse::ParseNumberError;

Expand Down
134 changes: 45 additions & 89 deletions src/uu/seq/src/seq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ use std::ffi::OsString;
use std::io::{stdout, BufWriter, ErrorKind, Write};

use clap::{Arg, ArgAction, Command};
use num_traits::{ToPrimitive, Zero};
use num_traits::Zero;

use uucore::error::{FromIo, UResult};
use uucore::format::{num_format, sprintf, Format, FormatArgument};
use uucore::format::num_format::FloatVariant;
use uucore::format::{num_format, ExtendedBigDecimal, Format};
use uucore::{format_usage, help_about, help_usage};

mod error;
mod extendedbigdecimal;
mod hexadecimalfloat;

// public to allow fuzzing
Expand All @@ -24,7 +24,6 @@ pub mod number;
mod number;
mod numberparse;
use crate::error::SeqError;
use crate::extendedbigdecimal::ExtendedBigDecimal;
use crate::number::PreciseNumber;

const ABOUT: &str = help_about!("seq.md");
Expand Down Expand Up @@ -142,26 +141,52 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}
};

let padding = first
.num_integral_digits
.max(increment.num_integral_digits)
.max(last.num_integral_digits);

let precision = select_precision(first_precision, increment_precision, last_precision);

let format = options
.format
.map(Format::<num_format::Float>::parse)
.transpose()?;
// If a format was passed on the command line, use that.
// If not, use some default format based on parameters precision.
let format = match options.format {
Some(str) => Format::<num_format::Float, &ExtendedBigDecimal>::parse(str)?,
None => {
let padding = if options.equal_width {
let precision_value = precision.unwrap_or(0);
first
.num_integral_digits
.max(increment.num_integral_digits)
.max(last.num_integral_digits)
+ if precision_value > 0 {
precision_value + 1
} else {
0
}
} else {
0
};

let formatter = match precision {
// format with precision: decimal floats and integers
Some(precision) => num_format::Float {
variant: FloatVariant::Decimal,
width: padding,
alignment: num_format::NumberAlignment::RightZero,
precision,
..Default::default()
},
// format without precision: hexadecimal floats
None => num_format::Float {
variant: FloatVariant::Shortest,
..Default::default()
},
};
Format::from_formatter(formatter)
}
};

let result = print_seq(
(first.number, increment.number, last.number),
precision,
&options.separator,
&options.terminator,
options.equal_width,
padding,
format.as_ref(),
&format,
);
match result {
Ok(()) => Ok(()),
Expand Down Expand Up @@ -220,93 +245,24 @@ fn done_printing<T: Zero + PartialOrd>(next: &T, increment: &T, last: &T) -> boo
}
}

fn format_bigdecimal(value: &bigdecimal::BigDecimal) -> Option<String> {
let format_arguments = &[FormatArgument::Float(value.to_f64()?)];
let value_as_bytes = sprintf("%g", format_arguments).ok()?;
String::from_utf8(value_as_bytes).ok()
}

/// Write a big decimal formatted according to the given parameters.
fn write_value_float(
writer: &mut impl Write,
value: &ExtendedBigDecimal,
width: usize,
precision: Option<usize>,
) -> std::io::Result<()> {
let value_as_str = match precision {
// format with precision: decimal floats and integers
Some(precision) => match value {
ExtendedBigDecimal::Infinity | ExtendedBigDecimal::MinusInfinity => {
format!("{value:>width$.precision$}")
}
_ => format!("{value:>0width$.precision$}"),
},
// format without precision: hexadecimal floats
None => match value {
ExtendedBigDecimal::BigDecimal(bd) => {
format_bigdecimal(bd).unwrap_or_else(|| "{value}".to_owned())
}
_ => format!("{value:>0width$}"),
},
};
write!(writer, "{value_as_str}")
}

/// Floating point based code path
fn print_seq(
range: RangeFloat,
precision: Option<usize>,
separator: &str,
terminator: &str,
pad: bool,
padding: usize,
format: Option<&Format<num_format::Float>>,
format: &Format<num_format::Float, &ExtendedBigDecimal>,
) -> std::io::Result<()> {
let stdout = stdout().lock();
let mut stdout = BufWriter::new(stdout);
let (first, increment, last) = range;
let mut value = first;
let padding = if pad {
let precision_value = precision.unwrap_or(0);
padding
+ if precision_value > 0 {
precision_value + 1
} else {
0
}
} else {
0
};

let mut is_first_iteration = true;
while !done_printing(&value, &increment, &last) {
if !is_first_iteration {
write!(stdout, "{separator}")?;
}
// If there was an argument `-f FORMAT`, then use that format
// template instead of the default formatting strategy.
//
// TODO The `printf()` method takes a string as its second
// parameter but we have an `ExtendedBigDecimal`. In order to
// satisfy the signature of the function, we convert the
// `ExtendedBigDecimal` into a string. The `printf()`
// logic will subsequently parse that string into something
// similar to an `ExtendedBigDecimal` again before rendering
// it as a string and ultimately writing to `stdout`. We
// shouldn't have to do so much converting back and forth via
// strings.
match &format {
Some(f) => {
let float = match &value {
ExtendedBigDecimal::BigDecimal(bd) => bd.to_f64().unwrap(),
ExtendedBigDecimal::Infinity => f64::INFINITY,
ExtendedBigDecimal::MinusInfinity => f64::NEG_INFINITY,
ExtendedBigDecimal::MinusZero => -0.0,
ExtendedBigDecimal::Nan => f64::NAN,
};
f.fmt(&mut stdout, float)?;
}
None => write_value_float(&mut stdout, &value, padding, precision)?,
}
format.fmt(&mut stdout, &value)?;
// TODO Implement augmenting addition.
value = value + increment.clone();
is_first_iteration = false;
Expand Down
6 changes: 4 additions & 2 deletions src/uucore/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# spell-checker:ignore (features) zerocopy
# spell-checker:ignore (features) bigdecimal zerocopy

[package]
name = "uucore"
Expand Down Expand Up @@ -58,6 +58,8 @@ blake3 = { workspace = true, optional = true }
sm3 = { workspace = true, optional = true }
crc32fast = { workspace = true, optional = true }
regex = { workspace = true, optional = true }
bigdecimal = { workspace = true, optional = true }
num-traits = { workspace = true, optional = true }

[target.'cfg(unix)'.dependencies]
walkdir = { workspace = true, optional = true }
Expand Down Expand Up @@ -94,7 +96,7 @@ fs = ["dunce", "libc", "winapi-util", "windows-sys"]
fsext = ["libc", "windows-sys"]
fsxattr = ["xattr"]
lines = []
format = ["itertools", "quoting-style"]
format = ["bigdecimal", "itertools", "num-traits", "quoting-style"]
mode = ["libc"]
perms = ["entries", "libc", "walkdir"]
buf-copy = []
Expand Down
Loading
Loading