mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-30 20:47:46 +00:00
uucore: Move fast_inc functions from seq
A new fast-inc feature, to be used by seq and cat.
This commit is contained in:
parent
54b2c12844
commit
764514bf22
6 changed files with 184 additions and 121 deletions
|
@ -23,6 +23,7 @@ num-traits = { workspace = true }
|
||||||
thiserror = { workspace = true }
|
thiserror = { workspace = true }
|
||||||
uucore = { workspace = true, features = [
|
uucore = { workspace = true, features = [
|
||||||
"extendedbigdecimal",
|
"extendedbigdecimal",
|
||||||
|
"fast-inc",
|
||||||
"format",
|
"format",
|
||||||
"parser",
|
"parser",
|
||||||
"quoting-style",
|
"quoting-style",
|
||||||
|
|
|
@ -15,7 +15,7 @@ use uucore::error::{FromIo, UResult};
|
||||||
use uucore::extendedbigdecimal::ExtendedBigDecimal;
|
use uucore::extendedbigdecimal::ExtendedBigDecimal;
|
||||||
use uucore::format::num_format::FloatVariant;
|
use uucore::format::num_format::FloatVariant;
|
||||||
use uucore::format::{Format, num_format};
|
use uucore::format::{Format, num_format};
|
||||||
use uucore::{format_usage, help_about, help_usage};
|
use uucore::{fast_inc::fast_inc, format_usage, help_about, help_usage};
|
||||||
|
|
||||||
mod error;
|
mod error;
|
||||||
|
|
||||||
|
@ -259,71 +259,6 @@ pub fn uu_app() -> Command {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fast code path increment function.
|
|
||||||
///
|
|
||||||
/// Add inc to the string val[start..end]. This operates on ASCII digits, assuming
|
|
||||||
/// val and inc are well formed.
|
|
||||||
///
|
|
||||||
/// Returns the new value for start (can be less that the original value if we
|
|
||||||
/// have a carry or if inc > start).
|
|
||||||
///
|
|
||||||
/// We also assume that there is enough space in val to expand if start needs
|
|
||||||
/// to be updated.
|
|
||||||
#[inline]
|
|
||||||
fn fast_inc(val: &mut [u8], start: usize, end: usize, inc: &[u8]) -> usize {
|
|
||||||
// To avoid a lot of casts to signed integers, we make sure to decrement pos
|
|
||||||
// as late as possible, so that it does not ever go negative.
|
|
||||||
let mut pos = end;
|
|
||||||
let mut carry = 0u8;
|
|
||||||
|
|
||||||
// First loop, add all digits of inc into val.
|
|
||||||
for inc_pos in (0..inc.len()).rev() {
|
|
||||||
pos -= 1;
|
|
||||||
|
|
||||||
let mut new_val = inc[inc_pos] + carry;
|
|
||||||
// Be careful here, only add existing digit of val.
|
|
||||||
if pos >= start {
|
|
||||||
new_val += val[pos] - b'0';
|
|
||||||
}
|
|
||||||
if new_val > b'9' {
|
|
||||||
carry = 1;
|
|
||||||
new_val -= 10;
|
|
||||||
} else {
|
|
||||||
carry = 0;
|
|
||||||
}
|
|
||||||
val[pos] = new_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Done, now, if we have a carry, add that to the upper digits of val.
|
|
||||||
if carry == 0 {
|
|
||||||
return start.min(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
return fast_inc_one(val, start, pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn fast_inc_one(val: &mut [u8], start: usize, end: usize) -> usize {
|
|
||||||
let mut pos = end;
|
|
||||||
|
|
||||||
while pos > start {
|
|
||||||
pos -= 1;
|
|
||||||
|
|
||||||
if val[pos] == b'9' {
|
|
||||||
// 9+1 = 10. Carry propagating, keep going.
|
|
||||||
val[pos] = b'0';
|
|
||||||
} else {
|
|
||||||
// Carry stopped propagating, return unchanged start.
|
|
||||||
val[pos] += 1;
|
|
||||||
return start;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The carry propagated so far that a new digit was added.
|
|
||||||
val[start - 1] = b'1';
|
|
||||||
start - 1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Integer print, default format, positive increment: fast code path
|
/// Integer print, default format, positive increment: fast code path
|
||||||
/// that avoids reformating digit at all iterations.
|
/// that avoids reformating digit at all iterations.
|
||||||
fn fast_print_seq(
|
fn fast_print_seq(
|
||||||
|
@ -452,58 +387,3 @@ fn print_seq(
|
||||||
stdout.flush()?;
|
stdout.flush()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn test_fast_inc_simple() {
|
|
||||||
use crate::fast_inc;
|
|
||||||
|
|
||||||
let mut val = [b'.', b'.', b'.', b'0', b'_'];
|
|
||||||
let inc = [b'4'].as_ref();
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 3, 4, inc), 3);
|
|
||||||
assert_eq!(val, "...4_".as_bytes());
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 3, 4, inc), 3);
|
|
||||||
assert_eq!(val, "...8_".as_bytes());
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 3, 4, inc), 2); // carried 1 more digit
|
|
||||||
assert_eq!(val, "..12_".as_bytes());
|
|
||||||
|
|
||||||
let mut val = [b'0', b'_'];
|
|
||||||
let inc = [b'2'].as_ref();
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 0, 1, inc), 0);
|
|
||||||
assert_eq!(val, "2_".as_bytes());
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 0, 1, inc), 0);
|
|
||||||
assert_eq!(val, "4_".as_bytes());
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 0, 1, inc), 0);
|
|
||||||
assert_eq!(val, "6_".as_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that we handle increment > val correctly.
|
|
||||||
#[test]
|
|
||||||
fn test_fast_inc_large_inc() {
|
|
||||||
use crate::fast_inc;
|
|
||||||
|
|
||||||
let mut val = [b'.', b'.', b'.', b'7', b'_'];
|
|
||||||
let inc = "543".as_bytes();
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 3, 4, inc), 1); // carried 2 more digits
|
|
||||||
assert_eq!(val, ".550_".as_bytes());
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 1, 4, inc), 0); // carried 1 more digit
|
|
||||||
assert_eq!(val, "1093_".as_bytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that we handle longer carries
|
|
||||||
#[test]
|
|
||||||
fn test_fast_inc_carry() {
|
|
||||||
use crate::fast_inc;
|
|
||||||
|
|
||||||
let mut val = [b'.', b'9', b'9', b'9', b'_'];
|
|
||||||
let inc = "1".as_bytes();
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 1, 4, inc), 0);
|
|
||||||
assert_eq!(val, "1000_".as_bytes());
|
|
||||||
|
|
||||||
let mut val = [b'.', b'9', b'9', b'9', b'_'];
|
|
||||||
let inc = "11".as_bytes();
|
|
||||||
assert_eq!(fast_inc(val.as_mut(), 1, 4, inc), 0);
|
|
||||||
assert_eq!(val, "1010_".as_bytes());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -91,6 +91,7 @@ checksum = ["data-encoding", "thiserror", "sum"]
|
||||||
encoding = ["data-encoding", "data-encoding-macro", "z85"]
|
encoding = ["data-encoding", "data-encoding-macro", "z85"]
|
||||||
entries = ["libc"]
|
entries = ["libc"]
|
||||||
extendedbigdecimal = ["bigdecimal", "num-traits"]
|
extendedbigdecimal = ["bigdecimal", "num-traits"]
|
||||||
|
fast-inc = []
|
||||||
fs = ["dunce", "libc", "winapi-util", "windows-sys"]
|
fs = ["dunce", "libc", "winapi-util", "windows-sys"]
|
||||||
fsext = ["libc", "windows-sys"]
|
fsext = ["libc", "windows-sys"]
|
||||||
fsxattr = ["xattr"]
|
fsxattr = ["xattr"]
|
||||||
|
|
|
@ -20,6 +20,8 @@ pub mod custom_tz_fmt;
|
||||||
pub mod encoding;
|
pub mod encoding;
|
||||||
#[cfg(feature = "extendedbigdecimal")]
|
#[cfg(feature = "extendedbigdecimal")]
|
||||||
pub mod extendedbigdecimal;
|
pub mod extendedbigdecimal;
|
||||||
|
#[cfg(feature = "fast-inc")]
|
||||||
|
pub mod fast_inc;
|
||||||
#[cfg(feature = "format")]
|
#[cfg(feature = "format")]
|
||||||
pub mod format;
|
pub mod format;
|
||||||
#[cfg(feature = "fs")]
|
#[cfg(feature = "fs")]
|
||||||
|
|
177
src/uucore/src/lib/features/fast_inc.rs
Normal file
177
src/uucore/src/lib/features/fast_inc.rs
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
// This file is part of the uutils coreutils package.
|
||||||
|
//
|
||||||
|
// For the full copyright and license information, please view the LICENSE
|
||||||
|
// file that was distributed with this source code.
|
||||||
|
|
||||||
|
/// Fast increment function, operating on ASCII strings.
|
||||||
|
///
|
||||||
|
/// Add inc to the string val[start..end]. This operates on ASCII digits, assuming
|
||||||
|
/// val and inc are well formed.
|
||||||
|
///
|
||||||
|
/// Returns the new value for start (can be less that the original value if we
|
||||||
|
/// have a carry or if inc > start).
|
||||||
|
///
|
||||||
|
/// We also assume that there is enough space in val to expand if start needs
|
||||||
|
/// to be updated.
|
||||||
|
/// ```
|
||||||
|
/// use uucore::fast_inc::fast_inc;
|
||||||
|
///
|
||||||
|
/// // Start with a buffer containing "0", with one byte of head space
|
||||||
|
/// let mut val = Vec::from(".0".as_bytes());
|
||||||
|
/// let mut start = val.len()-1;
|
||||||
|
/// let end = val.len();
|
||||||
|
/// let inc = "6".as_bytes();
|
||||||
|
/// assert_eq!(&val[start..end], "0".as_bytes());
|
||||||
|
/// start = fast_inc(val.as_mut(), start, end, inc);
|
||||||
|
/// assert_eq!(&val[start..end], "6".as_bytes());
|
||||||
|
/// start = fast_inc(val.as_mut(), start, end, inc);
|
||||||
|
/// assert_eq!(&val[start..end], "12".as_bytes());
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn fast_inc(val: &mut [u8], start: usize, end: usize, inc: &[u8]) -> usize {
|
||||||
|
// To avoid a lot of casts to signed integers, we make sure to decrement pos
|
||||||
|
// as late as possible, so that it does not ever go negative.
|
||||||
|
let mut pos = end;
|
||||||
|
let mut carry = 0u8;
|
||||||
|
|
||||||
|
// First loop, add all digits of inc into val.
|
||||||
|
for inc_pos in (0..inc.len()).rev() {
|
||||||
|
pos -= 1;
|
||||||
|
|
||||||
|
let mut new_val = inc[inc_pos] + carry;
|
||||||
|
// Be careful here, only add existing digit of val.
|
||||||
|
if pos >= start {
|
||||||
|
new_val += val[pos] - b'0';
|
||||||
|
}
|
||||||
|
if new_val > b'9' {
|
||||||
|
carry = 1;
|
||||||
|
new_val -= 10;
|
||||||
|
} else {
|
||||||
|
carry = 0;
|
||||||
|
}
|
||||||
|
val[pos] = new_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done, now, if we have a carry, add that to the upper digits of val.
|
||||||
|
if carry == 0 {
|
||||||
|
return start.min(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
fast_inc_one(val, start, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fast increment by one function, operating on ASCII strings.
|
||||||
|
///
|
||||||
|
/// Add 1 to the string val[start..end]. This operates on ASCII digits, assuming
|
||||||
|
/// val is well formed.
|
||||||
|
///
|
||||||
|
/// Returns the new value for start (can be less that the original value if we
|
||||||
|
/// have a carry).
|
||||||
|
///
|
||||||
|
/// We also assume that there is enough space in val to expand if start needs
|
||||||
|
/// to be updated.
|
||||||
|
/// ```
|
||||||
|
/// use uucore::fast_inc::fast_inc_one;
|
||||||
|
///
|
||||||
|
/// // Start with a buffer containing "8", with one byte of head space
|
||||||
|
/// let mut val = Vec::from(".8".as_bytes());
|
||||||
|
/// let mut start = val.len()-1;
|
||||||
|
/// let end = val.len();
|
||||||
|
/// assert_eq!(&val[start..end], "8".as_bytes());
|
||||||
|
/// start = fast_inc_one(val.as_mut(), start, end);
|
||||||
|
/// assert_eq!(&val[start..end], "9".as_bytes());
|
||||||
|
/// start = fast_inc_one(val.as_mut(), start, end);
|
||||||
|
/// assert_eq!(&val[start..end], "10".as_bytes());
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
pub fn fast_inc_one(val: &mut [u8], start: usize, end: usize) -> usize {
|
||||||
|
let mut pos = end;
|
||||||
|
|
||||||
|
while pos > start {
|
||||||
|
pos -= 1;
|
||||||
|
|
||||||
|
if val[pos] == b'9' {
|
||||||
|
// 9+1 = 10. Carry propagating, keep going.
|
||||||
|
val[pos] = b'0';
|
||||||
|
} else {
|
||||||
|
// Carry stopped propagating, return unchanged start.
|
||||||
|
val[pos] += 1;
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The carry propagated so far that a new digit was added.
|
||||||
|
val[start - 1] = b'1';
|
||||||
|
start - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::fast_inc::fast_inc;
|
||||||
|
use crate::fast_inc::fast_inc_one;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fast_inc_simple() {
|
||||||
|
let mut val = Vec::from("...0_".as_bytes());
|
||||||
|
let inc = "4".as_bytes();
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 3, 4, inc), 3);
|
||||||
|
assert_eq!(val, "...4_".as_bytes());
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 3, 4, inc), 3);
|
||||||
|
assert_eq!(val, "...8_".as_bytes());
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 3, 4, inc), 2); // carried 1 more digit
|
||||||
|
assert_eq!(val, "..12_".as_bytes());
|
||||||
|
|
||||||
|
let mut val = Vec::from("0_".as_bytes());
|
||||||
|
let inc = "2".as_bytes();
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 0, 1, inc), 0);
|
||||||
|
assert_eq!(val, "2_".as_bytes());
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 0, 1, inc), 0);
|
||||||
|
assert_eq!(val, "4_".as_bytes());
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 0, 1, inc), 0);
|
||||||
|
assert_eq!(val, "6_".as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we handle increment > val correctly.
|
||||||
|
#[test]
|
||||||
|
fn test_fast_inc_large_inc() {
|
||||||
|
let mut val = Vec::from("...7_".as_bytes());
|
||||||
|
let inc = "543".as_bytes();
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 3, 4, inc), 1); // carried 2 more digits
|
||||||
|
assert_eq!(val, ".550_".as_bytes());
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 1, 4, inc), 0); // carried 1 more digit
|
||||||
|
assert_eq!(val, "1093_".as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that we handle longer carries
|
||||||
|
#[test]
|
||||||
|
fn test_fast_inc_carry() {
|
||||||
|
let mut val = Vec::from(".999_".as_bytes());
|
||||||
|
let inc = "1".as_bytes();
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 1, 4, inc), 0);
|
||||||
|
assert_eq!(val, "1000_".as_bytes());
|
||||||
|
|
||||||
|
let mut val = Vec::from(".999_".as_bytes());
|
||||||
|
let inc = "11".as_bytes();
|
||||||
|
assert_eq!(fast_inc(val.as_mut(), 1, 4, inc), 0);
|
||||||
|
assert_eq!(val, "1010_".as_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fast_inc_one_simple() {
|
||||||
|
let mut val = Vec::from("...8_".as_bytes());
|
||||||
|
assert_eq!(fast_inc_one(val.as_mut(), 3, 4), 3);
|
||||||
|
assert_eq!(val, "...9_".as_bytes());
|
||||||
|
assert_eq!(fast_inc_one(val.as_mut(), 3, 4), 2); // carried 1 more digit
|
||||||
|
assert_eq!(val, "..10_".as_bytes());
|
||||||
|
assert_eq!(fast_inc_one(val.as_mut(), 2, 4), 2);
|
||||||
|
assert_eq!(val, "..11_".as_bytes());
|
||||||
|
|
||||||
|
let mut val = Vec::from("0_".as_bytes());
|
||||||
|
assert_eq!(fast_inc_one(val.as_mut(), 0, 1), 0);
|
||||||
|
assert_eq!(val, "1_".as_bytes());
|
||||||
|
assert_eq!(fast_inc_one(val.as_mut(), 0, 1), 0);
|
||||||
|
assert_eq!(val, "2_".as_bytes());
|
||||||
|
assert_eq!(fast_inc_one(val.as_mut(), 0, 1), 0);
|
||||||
|
assert_eq!(val, "3_".as_bytes());
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,6 +45,8 @@ pub use crate::features::custom_tz_fmt;
|
||||||
pub use crate::features::encoding;
|
pub use crate::features::encoding;
|
||||||
#[cfg(feature = "extendedbigdecimal")]
|
#[cfg(feature = "extendedbigdecimal")]
|
||||||
pub use crate::features::extendedbigdecimal;
|
pub use crate::features::extendedbigdecimal;
|
||||||
|
#[cfg(feature = "fast-inc")]
|
||||||
|
pub use crate::features::fast_inc;
|
||||||
#[cfg(feature = "format")]
|
#[cfg(feature = "format")]
|
||||||
pub use crate::features::format;
|
pub use crate::features::format;
|
||||||
#[cfg(feature = "fs")]
|
#[cfg(feature = "fs")]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue