mirror of
https://github.com/uutils/coreutils.git
synced 2026-06-10 15:48:22 -07:00
Merge branch 'master' of https://github.com/uutils/coreutils
This commit is contained in:
@@ -80,6 +80,9 @@ jobs:
|
||||
-e '/tests\/misc\/help-version-getopt.sh/ D' \
|
||||
Makefile
|
||||
|
||||
# printf doesn't limit the values used in its arg, so this produced ~2GB of output
|
||||
sed -i '/INT_OFLOW/ D' tests/misc/printf.sh
|
||||
|
||||
# Use the system coreutils where the test fails due to error in a util that is not the one being tested
|
||||
sed -i 's|stat|/usr/bin/stat|' tests/chgrp/basic.sh tests/cp/existing-perm-dir.sh tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh
|
||||
sed -i 's|ls -|/usr/bin/ls -|' tests/chgrp/posix-H.sh tests/chown/deref.sh tests/cp/same-file.sh tests/misc/mknod.sh tests/mv/part-symlink.sh tests/du/8gb.sh
|
||||
|
||||
Generated
+79
-15
@@ -28,6 +28,15 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.4.12"
|
||||
@@ -127,9 +136,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.2.3"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
||||
checksum = "cc38c385bfd7e444464011bb24820f40dd1c76bcdfa1b78611cb7c2e5cafab75"
|
||||
dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
@@ -169,7 +178,7 @@ version = "2.33.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"ansi_term 0.11.0",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
@@ -212,6 +221,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"nix 0.20.0",
|
||||
"pretty_assertions",
|
||||
"rand 0.7.3",
|
||||
"regex",
|
||||
"sha1",
|
||||
@@ -452,9 +462,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.0"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775"
|
||||
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"crossbeam-utils",
|
||||
@@ -517,6 +527,16 @@ dependencies = [
|
||||
"memchr 2.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.1.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e98e2ad1a782e33928b96fc3948e7c355e5af34ba4de7670fe8bac2a3b2006d"
|
||||
dependencies = [
|
||||
"quote 1.0.9",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "custom_derive"
|
||||
version = "0.1.7"
|
||||
@@ -529,6 +549,12 @@ version = "2.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4f47ca1860a761136924ddd2422ba77b2ea54fe8cc75b9040804a0d9d32ad97"
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.6.2"
|
||||
@@ -580,7 +606,7 @@ checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.2.5",
|
||||
"redox_syscall 0.2.6",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@@ -783,6 +809,15 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lscolors"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d24b894c45c9da468621cdd615a5a79ee5e5523dd4f75c76ebc03d458940c16e"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "match_cfg"
|
||||
version = "0.1.0"
|
||||
@@ -897,6 +932,12 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
|
||||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
version = "4.3.3"
|
||||
@@ -925,6 +966,15 @@ version = "11.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
|
||||
|
||||
[[package]]
|
||||
name = "output_vt100"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "0.1.18"
|
||||
@@ -994,6 +1044,18 @@ version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cab0e7c02cf376875e9335e0ba1da535775beb5450d21e1dffca068818ed98b"
|
||||
dependencies = [
|
||||
"ansi_term 0.12.1",
|
||||
"ctor",
|
||||
"diff",
|
||||
"output_vt100",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
@@ -1176,9 +1238,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
|
||||
checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
@@ -1189,14 +1251,14 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
|
||||
dependencies = [
|
||||
"redox_syscall 0.2.5",
|
||||
"redox_syscall 0.2.6",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.4.5"
|
||||
version = "1.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "957056ecddbeba1b26965114e191d2e8589ce74db242b6ea25fc4062427a5c19"
|
||||
checksum = "2a26af418b574bd56588335b3a3659a65725d4e636eb1016c2f9e3b38c7cc759"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr 2.3.4",
|
||||
@@ -1400,9 +1462,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.69"
|
||||
version = "1.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb"
|
||||
checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.9",
|
||||
@@ -1460,7 +1522,7 @@ checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"numtoa",
|
||||
"redox_syscall 0.2.5",
|
||||
"redox_syscall 0.2.6",
|
||||
"redox_termios",
|
||||
]
|
||||
|
||||
@@ -1996,11 +2058,12 @@ dependencies = [
|
||||
"clap",
|
||||
"globset",
|
||||
"lazy_static",
|
||||
"lscolors",
|
||||
"number_prefix",
|
||||
"once_cell",
|
||||
"term_grid",
|
||||
"termsize",
|
||||
"time",
|
||||
"unicode-width",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
@@ -2306,6 +2369,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"smallvec 1.6.1",
|
||||
"tempdir",
|
||||
"unicode-width",
|
||||
"uucore",
|
||||
"uucore_procs",
|
||||
]
|
||||
|
||||
@@ -335,6 +335,7 @@ filetime = "0.2"
|
||||
glob = "0.3.0"
|
||||
libc = "0.2"
|
||||
nix = "0.20.0"
|
||||
pretty_assertions = "0.7.2"
|
||||
rand = "0.7"
|
||||
regex = "1.0"
|
||||
sha1 = { version="0.6", features=["std"] }
|
||||
|
||||
+7
-78
@@ -22,6 +22,12 @@ use std::io::{self, Read, Write};
|
||||
use thiserror::Error;
|
||||
use uucore::fs::is_stdin_interactive;
|
||||
|
||||
/// Linux splice support
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
mod splice;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
/// Unix domain socket support
|
||||
#[cfg(unix)]
|
||||
use std::net::Shutdown;
|
||||
@@ -30,14 +36,6 @@ use std::os::unix::fs::FileTypeExt;
|
||||
#[cfg(unix)]
|
||||
use unix_socket::UnixStream;
|
||||
|
||||
/// Linux splice support
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use nix::fcntl::{splice, SpliceFFlags};
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use nix::unistd::pipe;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
|
||||
static NAME: &str = "cat";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static SYNTAX: &str = "[OPTION]... [FILE]...";
|
||||
@@ -395,7 +393,7 @@ fn write_fast<R: Read>(handle: &mut InputHandle<R>) -> CatResult<()> {
|
||||
{
|
||||
// If we're on Linux or Android, try to use the splice() system call
|
||||
// for faster writing. If it works, we're done.
|
||||
if !write_fast_using_splice(handle, stdout_lock.as_raw_fd())? {
|
||||
if !splice::write_fast_using_splice(handle, stdout_lock.as_raw_fd())? {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
@@ -411,75 +409,6 @@ fn write_fast<R: Read>(handle: &mut InputHandle<R>) -> CatResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This function is called from `write_fast()` on Linux and Android. The
|
||||
/// function `splice()` is used to move data between two file descriptors
|
||||
/// without copying between kernel- and userspace. This results in a large
|
||||
/// speedup.
|
||||
///
|
||||
/// The `bool` in the result value indicates if we need to fall back to normal
|
||||
/// copying or not. False means we don't have to.
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[inline]
|
||||
fn write_fast_using_splice<R: Read>(handle: &mut InputHandle<R>, writer: RawFd) -> CatResult<bool> {
|
||||
const BUF_SIZE: usize = 1024 * 16;
|
||||
|
||||
let (pipe_rd, pipe_wr) = pipe()?;
|
||||
|
||||
// We only fall back if splice fails on the first call.
|
||||
match splice(
|
||||
handle.file_descriptor,
|
||||
None,
|
||||
pipe_wr,
|
||||
None,
|
||||
BUF_SIZE,
|
||||
SpliceFFlags::empty(),
|
||||
) {
|
||||
Ok(n) => {
|
||||
if n == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
splice_exact(pipe_rd, writer, n)?;
|
||||
}
|
||||
Err(_) => {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
let n = splice(
|
||||
handle.file_descriptor,
|
||||
None,
|
||||
pipe_wr,
|
||||
None,
|
||||
BUF_SIZE,
|
||||
SpliceFFlags::empty(),
|
||||
)?;
|
||||
if n == 0 {
|
||||
// We read 0 bytes from the input,
|
||||
// which means we're done copying.
|
||||
break;
|
||||
}
|
||||
splice_exact(pipe_rd, writer, n)?;
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Splice wrapper which handles short writes
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
#[inline]
|
||||
fn splice_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> {
|
||||
let mut left = num_bytes;
|
||||
loop {
|
||||
let written = splice(read_fd, None, write_fd, None, left, SpliceFFlags::empty())?;
|
||||
left -= written;
|
||||
if left == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Outputs file contents to stdout in a line-by-line fashion,
|
||||
/// propagating any errors that might occur.
|
||||
fn write_lines<R: Read>(
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
use super::{CatResult, InputHandle};
|
||||
|
||||
use nix::fcntl::{splice, SpliceFFlags};
|
||||
use nix::unistd::{self, pipe};
|
||||
use std::io::Read;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
const BUF_SIZE: usize = 1024 * 16;
|
||||
|
||||
/// This function is called from `write_fast()` on Linux and Android. The
|
||||
/// function `splice()` is used to move data between two file descriptors
|
||||
/// without copying between kernel- and userspace. This results in a large
|
||||
/// speedup.
|
||||
///
|
||||
/// The `bool` in the result value indicates if we need to fall back to normal
|
||||
/// copying or not. False means we don't have to.
|
||||
#[inline]
|
||||
pub(super) fn write_fast_using_splice<R: Read>(
|
||||
handle: &mut InputHandle<R>,
|
||||
write_fd: RawFd,
|
||||
) -> CatResult<bool> {
|
||||
let (pipe_rd, pipe_wr) = match pipe() {
|
||||
Ok(r) => r,
|
||||
Err(_) => {
|
||||
// It is very rare that creating a pipe fails, but it can happen.
|
||||
return Ok(true);
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
match splice(
|
||||
handle.file_descriptor,
|
||||
None,
|
||||
pipe_wr,
|
||||
None,
|
||||
BUF_SIZE,
|
||||
SpliceFFlags::empty(),
|
||||
) {
|
||||
Ok(n) => {
|
||||
if n == 0 {
|
||||
return Ok(false);
|
||||
}
|
||||
if splice_exact(pipe_rd, write_fd, n).is_err() {
|
||||
// If the first splice manages to copy to the intermediate
|
||||
// pipe, but the second splice to stdout fails for some reason
|
||||
// we can recover by copying the data that we have from the
|
||||
// intermediate pipe to stdout using normal read/write. Then
|
||||
// we tell the caller to fall back.
|
||||
copy_exact(pipe_rd, write_fd, n)?;
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Splice wrapper which handles short writes.
|
||||
#[inline]
|
||||
fn splice_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> {
|
||||
let mut left = num_bytes;
|
||||
loop {
|
||||
let written = splice(read_fd, None, write_fd, None, left, SpliceFFlags::empty())?;
|
||||
left -= written;
|
||||
if left == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Caller must ensure that `num_bytes <= BUF_SIZE`, otherwise this function
|
||||
/// will panic. The way we use this function in `write_fast_using_splice`
|
||||
/// above is safe because `splice` is set to write at most `BUF_SIZE` to the
|
||||
/// pipe.
|
||||
#[inline]
|
||||
fn copy_exact(read_fd: RawFd, write_fd: RawFd, num_bytes: usize) -> nix::Result<()> {
|
||||
let mut left = num_bytes;
|
||||
let mut buf = [0; BUF_SIZE];
|
||||
loop {
|
||||
let read = unistd::read(read_fd, &mut buf[..left])?;
|
||||
let written = unistd::write(write_fd, &mut buf[..read])?;
|
||||
left -= written;
|
||||
if left == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
+109
-41
@@ -155,7 +155,8 @@ pub enum OverwriteMode {
|
||||
NoClobber,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
/// Possible arguments for `--reflink`.
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub enum ReflinkMode {
|
||||
Always,
|
||||
Auto,
|
||||
@@ -210,7 +211,6 @@ pub struct Options {
|
||||
overwrite: OverwriteMode,
|
||||
parents: bool,
|
||||
strip_trailing_slashes: bool,
|
||||
reflink: bool,
|
||||
reflink_mode: ReflinkMode,
|
||||
preserve_attributes: Vec<Attribute>,
|
||||
recursive: bool,
|
||||
@@ -633,12 +633,12 @@ impl Options {
|
||||
update: matches.is_present(OPT_UPDATE),
|
||||
verbose: matches.is_present(OPT_VERBOSE),
|
||||
strip_trailing_slashes: matches.is_present(OPT_STRIP_TRAILING_SLASHES),
|
||||
reflink: matches.is_present(OPT_REFLINK),
|
||||
reflink_mode: {
|
||||
if let Some(reflink) = matches.value_of(OPT_REFLINK) {
|
||||
match reflink {
|
||||
"always" => ReflinkMode::Always,
|
||||
"auto" => ReflinkMode::Auto,
|
||||
"never" => ReflinkMode::Never,
|
||||
value => {
|
||||
return Err(Error::InvalidArgument(format!(
|
||||
"invalid argument '{}' for \'reflink\'",
|
||||
@@ -1193,47 +1193,20 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
///Copy the file from `source` to `dest` either using the normal `fs::copy` or the
|
||||
///`FICLONE` ioctl if --reflink is specified and the filesystem supports it.
|
||||
/// Copy the file from `source` to `dest` either using the normal `fs::copy` or a
|
||||
/// copy-on-write scheme if --reflink is specified and the filesystem supports it.
|
||||
fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
|
||||
if options.reflink {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
return Err("--reflink is only supported on linux".to_string().into());
|
||||
if options.reflink_mode != ReflinkMode::Never {
|
||||
#[cfg(not(any(target_os = "linux", target_os = "macos")))]
|
||||
return Err("--reflink is only supported on linux and macOS"
|
||||
.to_string()
|
||||
.into());
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
copy_on_write_macos(source, dest, options.reflink_mode)?;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let src_file = File::open(source).unwrap().into_raw_fd();
|
||||
let dst_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(false)
|
||||
.create(true)
|
||||
.open(dest)
|
||||
.unwrap()
|
||||
.into_raw_fd();
|
||||
match options.reflink_mode {
|
||||
ReflinkMode::Always => unsafe {
|
||||
let result = ficlone(dst_file, src_file as *const i32);
|
||||
if result != 0 {
|
||||
return Err(format!(
|
||||
"failed to clone {:?} from {:?}: {}",
|
||||
source,
|
||||
dest,
|
||||
std::io::Error::last_os_error()
|
||||
)
|
||||
.into());
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
ReflinkMode::Auto => unsafe {
|
||||
let result = ficlone(dst_file, src_file as *const i32);
|
||||
if result != 0 {
|
||||
fs::copy(source, dest).context(&*context_for(source, dest))?;
|
||||
}
|
||||
},
|
||||
ReflinkMode::Never => {}
|
||||
}
|
||||
}
|
||||
copy_on_write_linux(source, dest, options.reflink_mode)?;
|
||||
} else if options.no_dereference && fs::symlink_metadata(&source)?.file_type().is_symlink() {
|
||||
// Here, we will copy the symlink itself (actually, just recreate it)
|
||||
let link = fs::read_link(&source)?;
|
||||
@@ -1266,6 +1239,101 @@ fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Copies `source` to `dest` using copy-on-write if possible.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn copy_on_write_linux(source: &Path, dest: &Path, mode: ReflinkMode) -> CopyResult<()> {
|
||||
debug_assert!(mode != ReflinkMode::Never);
|
||||
|
||||
let src_file = File::open(source).unwrap().into_raw_fd();
|
||||
let dst_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.truncate(false)
|
||||
.create(true)
|
||||
.open(dest)
|
||||
.unwrap()
|
||||
.into_raw_fd();
|
||||
match mode {
|
||||
ReflinkMode::Always => unsafe {
|
||||
let result = ficlone(dst_file, src_file as *const i32);
|
||||
if result != 0 {
|
||||
return Err(format!(
|
||||
"failed to clone {:?} from {:?}: {}",
|
||||
source,
|
||||
dest,
|
||||
std::io::Error::last_os_error()
|
||||
)
|
||||
.into());
|
||||
} else {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
ReflinkMode::Auto => unsafe {
|
||||
let result = ficlone(dst_file, src_file as *const i32);
|
||||
if result != 0 {
|
||||
fs::copy(source, dest).context(&*context_for(source, dest))?;
|
||||
}
|
||||
},
|
||||
ReflinkMode::Never => unreachable!(),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Copies `source` to `dest` using copy-on-write if possible.
|
||||
#[cfg(target_os = "macos")]
|
||||
fn copy_on_write_macos(source: &Path, dest: &Path, mode: ReflinkMode) -> CopyResult<()> {
|
||||
debug_assert!(mode != ReflinkMode::Never);
|
||||
|
||||
// Extract paths in a form suitable to be passed to a syscall.
|
||||
// The unwrap() is safe because they come from the command-line and so contain non nul
|
||||
// character.
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
let src = CString::new(source.as_os_str().as_bytes()).unwrap();
|
||||
let dst = CString::new(dest.as_os_str().as_bytes()).unwrap();
|
||||
|
||||
// clonefile(2) was introduced in macOS 10.12 so we cannot statically link against it
|
||||
// for backward compatibility.
|
||||
let clonefile = CString::new("clonefile").unwrap();
|
||||
let raw_pfn = unsafe { libc::dlsym(libc::RTLD_NEXT, clonefile.as_ptr()) };
|
||||
|
||||
let mut error = 0;
|
||||
if !raw_pfn.is_null() {
|
||||
// Call clonefile(2).
|
||||
// Safety: Casting a C function pointer to a rust function value is one of the few
|
||||
// blessed uses of `transmute()`.
|
||||
unsafe {
|
||||
let pfn: extern "C" fn(
|
||||
src: *const libc::c_char,
|
||||
dst: *const libc::c_char,
|
||||
flags: u32,
|
||||
) -> libc::c_int = std::mem::transmute(raw_pfn);
|
||||
error = pfn(src.as_ptr(), dst.as_ptr(), 0);
|
||||
if std::io::Error::last_os_error().kind() == std::io::ErrorKind::AlreadyExists {
|
||||
// clonefile(2) fails if the destination exists. Remove it and try again. Do not
|
||||
// bother to check if removal worked because we're going to try to clone again.
|
||||
let _ = fs::remove_file(dest);
|
||||
error = pfn(src.as_ptr(), dst.as_ptr(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if raw_pfn.is_null() || error != 0 {
|
||||
// clonefile(2) is not supported or it error'ed out (possibly because the FS does not
|
||||
// support COW).
|
||||
match mode {
|
||||
ReflinkMode::Always => {
|
||||
return Err(
|
||||
format!("failed to clone {:?} from {:?}: {}", source, dest, error).into(),
|
||||
)
|
||||
}
|
||||
ReflinkMode::Auto => fs::copy(source, dest).context(&*context_for(source, dest))?,
|
||||
ReflinkMode::Never => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generate an error message if `target` is not the correct `target_type`
|
||||
pub fn verify_target_type(target: &Path, target_type: &TargetType) -> CopyResult<()> {
|
||||
match (target_type, target.is_dir()) {
|
||||
|
||||
+1
-1
@@ -500,7 +500,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
};
|
||||
|
||||
let strs = if matches.free.is_empty() {
|
||||
vec!["./".to_owned()]
|
||||
vec!["./".to_owned()] // TODO: gnu `du` doesn't use trailing "/" here
|
||||
} else {
|
||||
matches.free.clone()
|
||||
};
|
||||
|
||||
@@ -41,6 +41,7 @@ pub struct Behavior {
|
||||
compare: bool,
|
||||
strip: bool,
|
||||
strip_program: String,
|
||||
create_leading: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
@@ -70,7 +71,7 @@ static OPT_BACKUP: &str = "backup";
|
||||
static OPT_BACKUP_2: &str = "backup2";
|
||||
static OPT_DIRECTORY: &str = "directory";
|
||||
static OPT_IGNORED: &str = "ignored";
|
||||
static OPT_CREATED: &str = "created";
|
||||
static OPT_CREATE_LEADING: &str = "create-leading";
|
||||
static OPT_GROUP: &str = "group";
|
||||
static OPT_MODE: &str = "mode";
|
||||
static OPT_OWNER: &str = "owner";
|
||||
@@ -133,9 +134,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
|
||||
.arg(
|
||||
// TODO implement flag
|
||||
Arg::with_name(OPT_CREATED)
|
||||
Arg::with_name(OPT_CREATE_LEADING)
|
||||
.short("D")
|
||||
.help("(unimplemented) create all leading components of DEST except the last, then copy SOURCE to DEST")
|
||||
.help("create all leading components of DEST except the last, then copy SOURCE to DEST")
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_GROUP)
|
||||
@@ -266,8 +267,6 @@ fn check_unimplemented<'a>(matches: &ArgMatches) -> Result<(), &'a str> {
|
||||
Err("--backup")
|
||||
} else if matches.is_present(OPT_BACKUP_2) {
|
||||
Err("-b")
|
||||
} else if matches.is_present(OPT_CREATED) {
|
||||
Err("-D")
|
||||
} else if matches.is_present(OPT_SUFFIX) {
|
||||
Err("--suffix, -S")
|
||||
} else if matches.is_present(OPT_TARGET_DIRECTORY) {
|
||||
@@ -343,6 +342,7 @@ fn behavior(matches: &ArgMatches) -> Result<Behavior, i32> {
|
||||
.value_of(OPT_STRIP_PROGRAM)
|
||||
.unwrap_or(DEFAULT_STRIP_PROGRAM),
|
||||
),
|
||||
create_leading: matches.is_present(OPT_CREATE_LEADING),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -410,12 +410,35 @@ fn standard(paths: Vec<String>, b: Behavior) -> i32 {
|
||||
.iter()
|
||||
.map(PathBuf::from)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let target = Path::new(paths.last().unwrap());
|
||||
|
||||
if (target.is_file() || is_new_file_path(target)) && sources.len() == 1 {
|
||||
copy_file_to_file(&sources[0], &target.to_path_buf(), &b)
|
||||
} else {
|
||||
if sources.len() > 1 || (target.exists() && target.is_dir()) {
|
||||
copy_files_into_dir(sources, &target.to_path_buf(), &b)
|
||||
} else {
|
||||
if let Some(parent) = target.parent() {
|
||||
if !parent.exists() && b.create_leading {
|
||||
if let Err(e) = fs::create_dir_all(parent) {
|
||||
show_error!("failed to create {}: {}", parent.display(), e);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if mode::chmod(&parent, b.mode()).is_err() {
|
||||
show_error!("failed to chmod {}", parent.display());
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if target.is_file() || is_new_file_path(target) {
|
||||
copy_file_to_file(&sources[0], &target.to_path_buf(), &b)
|
||||
} else {
|
||||
show_error!(
|
||||
"invalid target {}: No such file or directory",
|
||||
target.display()
|
||||
);
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Benchmarking ls
|
||||
|
||||
ls majorly involves fetching a lot of details (depending upon what details are requested, eg. time/date, inode details, etc) for each path using system calls. Ideally, any system call should be done only once for each of the paths - not adhering to this principle leads to a lot of system call overhead multiplying and bubbling up, especially for recursive ls, therefore it is important to always benchmark multiple scenarios.
|
||||
This is an overwiew over what was benchmarked, and if you make changes to `ls`, you are encouraged to check
|
||||
how performance was affected for the workloads listed below. Feel free to add other workloads to the
|
||||
list that we should improve / make sure not to regress.
|
||||
|
||||
Run `cargo build --release` before benchmarking after you make a change!
|
||||
|
||||
## Simple recursive ls
|
||||
|
||||
- Get a large tree, for example linux kernel source tree.
|
||||
- Benchmark simple recursive ls with hyperfine: `hyperfine --warmup 2 "target/release/coreutils ls -R tree > /dev/null"`.
|
||||
|
||||
## Recursive ls with all and long options
|
||||
|
||||
- Same tree as above
|
||||
- Benchmark recursive ls with -al -R options with hyperfine: `hyperfine --warmup 2 "target/release/coreutils ls -al -R tree > /dev/null"`.
|
||||
|
||||
## Comparing with GNU ls
|
||||
|
||||
Hyperfine accepts multiple commands to run and will compare them. To compare performance with GNU ls
|
||||
duplicate the string you passed to hyperfine but remove the `target/release/coreutils` bit from it.
|
||||
|
||||
Example: `hyperfine --warmup 2 "target/release/coreutils ls -al -R tree > /dev/null"` becomes
|
||||
`hyperfine --warmup 2 "target/release/coreutils ls -al -R tree > /dev/null" "ls -al -R tree > /dev/null"`
|
||||
(This assumes GNU ls is installed as `ls`)
|
||||
|
||||
This can also be used to compare with version of ls built before your changes to ensure your change does not regress this
|
||||
|
||||
## Checking system call count
|
||||
|
||||
- Another thing to look at would be system calls count using strace (on linux) or equivalent on other operating systems.
|
||||
- Example: `strace -c target/release/coreutils ls -al -R tree`
|
||||
@@ -16,18 +16,19 @@ path = "src/ls.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
lazy_static = "1.0.1"
|
||||
number_prefix = "0.4"
|
||||
term_grid = "0.1.5"
|
||||
termsize = "0.1.6"
|
||||
time = "0.1.40"
|
||||
unicode-width = "0.1.5"
|
||||
globset = "0.4.6"
|
||||
lscolors = { version="0.7.1", features=["ansi_term"] }
|
||||
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "fs"] }
|
||||
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
|
||||
once_cell = "1.7.2"
|
||||
atty = "0.2"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
atty = "0.2"
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
[[bin]]
|
||||
name = "ls"
|
||||
|
||||
+316
-337
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
use std::char::from_digit;
|
||||
|
||||
const SPECIAL_SHELL_CHARS: &str = "~`#$&*()\\|[]{};'\"<>?! ";
|
||||
const SPECIAL_SHELL_CHARS: &str = "~`#$&*()|[]{};\\'\"<>?! ";
|
||||
|
||||
pub(super) enum QuotingStyle {
|
||||
Shell {
|
||||
@@ -27,12 +27,10 @@ pub(super) enum Quotes {
|
||||
// This implementation is heavily inspired by the std::char::EscapeDefault implementation
|
||||
// in the Rust standard library. This custom implementation is needed because the
|
||||
// characters \a, \b, \e, \f & \v are not recognized by Rust.
|
||||
#[derive(Clone, Debug)]
|
||||
struct EscapedChar {
|
||||
state: EscapeState,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum EscapeState {
|
||||
Done,
|
||||
Char(char),
|
||||
@@ -41,14 +39,12 @@ enum EscapeState {
|
||||
Octal(EscapeOctal),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct EscapeOctal {
|
||||
c: char,
|
||||
state: EscapeOctalState,
|
||||
idx: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum EscapeOctalState {
|
||||
Done,
|
||||
Backslash,
|
||||
@@ -135,7 +131,6 @@ impl EscapedChar {
|
||||
'\x0B' => Backslash('v'),
|
||||
'\x0C' => Backslash('f'),
|
||||
'\r' => Backslash('r'),
|
||||
'\\' => Backslash('\\'),
|
||||
'\x00'..='\x1F' | '\x7F' => Octal(EscapeOctal::from(c)),
|
||||
'\'' => match quotes {
|
||||
Quotes::Single => Backslash('\''),
|
||||
@@ -511,6 +506,23 @@ mod tests {
|
||||
],
|
||||
);
|
||||
|
||||
// A control character followed by a special shell character
|
||||
check_names(
|
||||
"one\n&two",
|
||||
vec![
|
||||
("one?&two", "literal"),
|
||||
("one\n&two", "literal-show"),
|
||||
("one\\n&two", "escape"),
|
||||
("\"one\\n&two\"", "c"),
|
||||
("'one?&two'", "shell"),
|
||||
("'one\n&two'", "shell-show"),
|
||||
("'one?&two'", "shell-always"),
|
||||
("'one\n&two'", "shell-always-show"),
|
||||
("'one'$'\\n''&two'", "shell-escape"),
|
||||
("'one'$'\\n''&two'", "shell-escape-always"),
|
||||
],
|
||||
);
|
||||
|
||||
// The first 16 control characters. NUL is also included, even though it is of
|
||||
// no importance for file names.
|
||||
check_names(
|
||||
@@ -627,4 +639,22 @@ mod tests {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backslash() {
|
||||
// Escaped in C-style, but not in Shell-style escaping
|
||||
check_names(
|
||||
"one\\two",
|
||||
vec![
|
||||
("one\\two", "literal"),
|
||||
("one\\two", "literal-show"),
|
||||
("one\\\\two", "escape"),
|
||||
("\"one\\\\two\"", "c"),
|
||||
("'one\\two'", "shell"),
|
||||
("\'one\\two\'", "shell-always"),
|
||||
("'one\\two'", "shell-escape"),
|
||||
("'one\\two'", "shell-escape-always"),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
// spell-checker:ignore (ToDO) parsemode makedev sysmacros makenod newmode perror IFBLK IFCHR IFIFO
|
||||
|
||||
mod parsemode;
|
||||
|
||||
#[macro_use]
|
||||
extern crate uucore;
|
||||
|
||||
@@ -98,7 +96,7 @@ for details about the options it supports.",
|
||||
let mut last_umask: mode_t = 0;
|
||||
let mut newmode: mode_t = MODE_RW_UGO;
|
||||
if matches.opt_present("mode") {
|
||||
match parsemode::parse_mode(matches.opt_str("mode")) {
|
||||
match uucore::mode::parse_mode(matches.opt_str("mode")) {
|
||||
Ok(parsed) => {
|
||||
if parsed > 0o777 {
|
||||
show_info!("mode must specify only file permission bits");
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
// spell-checker:ignore (ToDO) fperm
|
||||
|
||||
use libc::{mode_t, S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR};
|
||||
|
||||
use uucore::mode;
|
||||
|
||||
pub fn parse_mode(mode: Option<String>) -> Result<mode_t, String> {
|
||||
let fperm = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
||||
if let Some(mode) = mode {
|
||||
let arr: &[char] = &['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||||
let result = if mode.contains(arr) {
|
||||
mode::parse_numeric(fperm as u32, mode.as_str())
|
||||
} else {
|
||||
mode::parse_symbolic(fperm as u32, mode.as_str(), true)
|
||||
};
|
||||
result.map(|mode| mode as mode_t)
|
||||
} else {
|
||||
Ok(fperm)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
/// Test if the program is running under WSL
|
||||
// ref: <https://github.com/microsoft/WSL/issues/4555> @@ <https://archive.is/dP0bz>
|
||||
// ToDO: test on WSL2 which likely doesn't need special handling; plan change to `is_wsl_1()` if WSL2 is less needy
|
||||
pub fn is_wsl() -> bool {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
if let Ok(b) = std::fs::read("/proc/sys/kernel/osrelease") {
|
||||
if let Ok(s) = std::str::from_utf8(&b) {
|
||||
let a = s.to_ascii_lowercase();
|
||||
return a.contains("microsoft") || a.contains("wsl");
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbolic_modes() {
|
||||
assert_eq!(super::parse_mode(Some("u+x".to_owned())).unwrap(), 0o766);
|
||||
assert_eq!(
|
||||
super::parse_mode(Some("+x".to_owned())).unwrap(),
|
||||
if !is_wsl() { 0o777 } else { 0o776 }
|
||||
);
|
||||
assert_eq!(super::parse_mode(Some("a-w".to_owned())).unwrap(), 0o444);
|
||||
assert_eq!(super::parse_mode(Some("g-r".to_owned())).unwrap(), 0o626);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numeric_modes() {
|
||||
assert_eq!(super::parse_mode(Some("644".to_owned())).unwrap(), 0o644);
|
||||
assert_eq!(super::parse_mode(Some("+100".to_owned())).unwrap(), 0o766);
|
||||
assert_eq!(super::parse_mode(Some("-4".to_owned())).unwrap(), 0o662);
|
||||
assert_eq!(super::parse_mode(None).unwrap(), 0o666);
|
||||
}
|
||||
}
|
||||
@@ -18,18 +18,15 @@ pub fn err_msg(msg: &str) {
|
||||
|
||||
// by default stdout only flushes
|
||||
// to console when a newline is passed.
|
||||
#[allow(unused_must_use)]
|
||||
pub fn flush_char(c: char) {
|
||||
print!("{}", c);
|
||||
stdout().flush();
|
||||
let _ = stdout().flush();
|
||||
}
|
||||
#[allow(unused_must_use)]
|
||||
pub fn flush_str(s: &str) {
|
||||
print!("{}", s);
|
||||
stdout().flush();
|
||||
let _ = stdout().flush();
|
||||
}
|
||||
#[allow(unused_must_use)]
|
||||
pub fn flush_bytes(bslice: &[u8]) {
|
||||
stdout().write(bslice);
|
||||
stdout().flush();
|
||||
let _ = stdout().write(bslice);
|
||||
let _ = stdout().flush();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
// spell-checker:ignore (change!) each's
|
||||
// spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr
|
||||
|
||||
@@ -9,7 +8,6 @@ mod tokenize;
|
||||
|
||||
static NAME: &str = "printf";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
static SHORT_USAGE: &str = "printf: usage: printf [-v var] format [arguments]";
|
||||
static LONGHELP_LEAD: &str = "printf
|
||||
|
||||
USAGE: printf FORMATSTRING [ARGUMENT]...
|
||||
|
||||
@@ -28,8 +28,7 @@ pub fn arrnum_int_mult(arr_num: &[u8], basenum: u8, base_ten_int_fact: u8) -> Ve
|
||||
}
|
||||
}
|
||||
}
|
||||
#[allow(clippy::map_clone)]
|
||||
let ret: Vec<u8> = ret_rev.iter().rev().map(|x| *x).collect();
|
||||
let ret: Vec<u8> = ret_rev.into_iter().rev().collect();
|
||||
ret
|
||||
}
|
||||
|
||||
@@ -102,70 +101,6 @@ pub fn arrnum_int_div_step(
|
||||
remainder: rem_out,
|
||||
}
|
||||
}
|
||||
// pub struct ArrFloat {
|
||||
// pub leading_zeros: u8,
|
||||
// pub values: Vec<u8>,
|
||||
// pub basenum: u8
|
||||
// }
|
||||
//
|
||||
// pub struct ArrFloatDivOut {
|
||||
// pub quotient: u8,
|
||||
// pub remainder: ArrFloat
|
||||
// }
|
||||
//
|
||||
// pub fn arrfloat_int_div(
|
||||
// arrfloat_in : &ArrFloat,
|
||||
// base_ten_int_divisor : u8,
|
||||
// precision : u16
|
||||
// ) -> DivOut {
|
||||
//
|
||||
// let mut remainder = ArrFloat {
|
||||
// basenum: arrfloat_in.basenum,
|
||||
// leading_zeros: arrfloat_in.leading_zeroes,
|
||||
// values: Vec<u8>::new()
|
||||
// }
|
||||
// let mut quotient = 0;
|
||||
//
|
||||
// let mut bufferval : u16 = 0;
|
||||
// let base : u16 = arrfloat_in.basenum as u16;
|
||||
// let divisor : u16 = base_ten_int_divisor as u16;
|
||||
//
|
||||
// let mut it_f = arrfloat_in.values.iter();
|
||||
// let mut position = 0 + arrfloat_in.leading_zeroes as u16;
|
||||
// let mut at_end = false;
|
||||
// while position< precision {
|
||||
// let next_digit = match it_f.next() {
|
||||
// Some(c) => {}
|
||||
// None => { 0 }
|
||||
// }
|
||||
// match u_cur {
|
||||
// Some(u) => {
|
||||
// bufferval += u.clone() as u16;
|
||||
// if bufferval > divisor {
|
||||
// while bufferval >= divisor {
|
||||
// quotient+=1;
|
||||
// bufferval -= divisor;
|
||||
// }
|
||||
// if bufferval == 0 {
|
||||
// rem_out.position +=1;
|
||||
// } else {
|
||||
// rem_out.replace = Some(bufferval as u8);
|
||||
// }
|
||||
// break;
|
||||
// } else {
|
||||
// bufferval *= base;
|
||||
// }
|
||||
// },
|
||||
// None => {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// u_cur = it_f.next().clone();
|
||||
// rem_out.position+=1;
|
||||
// }
|
||||
// ArrFloatDivOut { quotient: quotient, remainder: remainder }
|
||||
// }
|
||||
//
|
||||
pub fn arrnum_int_add(arrnum: &[u8], basenum: u8, base_ten_int_term: u8) -> Vec<u8> {
|
||||
let mut carry: u16 = u16::from(base_ten_int_term);
|
||||
let mut rem: u16;
|
||||
@@ -193,8 +128,7 @@ pub fn arrnum_int_add(arrnum: &[u8], basenum: u8, base_ten_int_term: u8) -> Vec<
|
||||
}
|
||||
}
|
||||
}
|
||||
#[allow(clippy::map_clone)]
|
||||
let ret: Vec<u8> = ret_rev.iter().rev().map(|x| *x).collect();
|
||||
let ret: Vec<u8> = ret_rev.into_iter().rev().collect();
|
||||
ret
|
||||
}
|
||||
|
||||
@@ -219,8 +153,7 @@ pub fn unsigned_to_arrnum(src: u16) -> Vec<u8> {
|
||||
}
|
||||
|
||||
// temporary needs-improvement-function
|
||||
#[allow(unused_variables)]
|
||||
pub fn base_conv_float(src: &[u8], radix_src: u8, radix_dest: u8) -> f64 {
|
||||
pub fn base_conv_float(src: &[u8], radix_src: u8, _radix_dest: u8) -> f64 {
|
||||
// it would require a lot of addl code
|
||||
// to implement this for arbitrary string input.
|
||||
// until then, the below operates as an outline
|
||||
@@ -267,7 +200,6 @@ pub fn arrnum_to_str(src: &[u8], radix_def_dest: &dyn RadixDef) -> String {
|
||||
str_out
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub fn base_conv_str(
|
||||
src: &str,
|
||||
radix_def_src: &dyn RadixDef,
|
||||
|
||||
@@ -43,45 +43,15 @@ impl Formatter for CninetyNineHexFloatf {
|
||||
// c99 hex has unique requirements of all floating point subs in pretty much every part of building a primitive, from prefix and suffix to need for base conversion (in all other cases if you don't have decimal you must have decimal, here it's the other way around)
|
||||
|
||||
// on the todo list is to have a trait for get_primitive that is implemented by each float formatter and can override a default. when that happens we can take the parts of get_primitive_dec specific to dec and spin them out to their own functions that can be overridden.
|
||||
#[allow(unused_variables)]
|
||||
#[allow(unused_assignments)]
|
||||
fn get_primitive_hex(
|
||||
inprefix: &InPrefix,
|
||||
str_in: &str,
|
||||
analysis: &FloatAnalysis,
|
||||
last_dec_place: usize,
|
||||
_str_in: &str,
|
||||
_analysis: &FloatAnalysis,
|
||||
_last_dec_place: usize,
|
||||
capitalized: bool,
|
||||
) -> FormatPrimitive {
|
||||
let prefix = Some(String::from(if inprefix.sign == -1 { "-0x" } else { "0x" }));
|
||||
|
||||
// assign the digits before and after the decimal points
|
||||
// to separate slices. If no digits after decimal point,
|
||||
// assign 0
|
||||
let (mut first_segment_raw, second_segment_raw) = match analysis.decimal_pos {
|
||||
Some(pos) => (&str_in[..pos], &str_in[pos + 1..]),
|
||||
None => (str_in, "0"),
|
||||
};
|
||||
if first_segment_raw.is_empty() {
|
||||
first_segment_raw = "0";
|
||||
}
|
||||
// convert to string, hexifying if input is in dec.
|
||||
// let (first_segment, second_segment) =
|
||||
// match inprefix.radix_in {
|
||||
// Base::Ten => {
|
||||
// (to_hex(first_segment_raw, true),
|
||||
// to_hex(second_segment_raw, false))
|
||||
// }
|
||||
// _ => {
|
||||
// (String::from(first_segment_raw),
|
||||
// String::from(second_segment_raw))
|
||||
// }
|
||||
// };
|
||||
//
|
||||
//
|
||||
// f.pre_decimal = Some(first_segment);
|
||||
// f.post_decimal = Some(second_segment);
|
||||
//
|
||||
|
||||
// TODO actual conversion, make sure to get back mantissa.
|
||||
// for hex to hex, it's really just a matter of moving the
|
||||
// decimal point and calculating the mantissa by its initial
|
||||
|
||||
@@ -22,12 +22,11 @@ fn get_len_fprim(fprim: &FormatPrimitive) -> usize {
|
||||
len
|
||||
}
|
||||
|
||||
pub struct Decf {
|
||||
as_num: f64,
|
||||
}
|
||||
pub struct Decf;
|
||||
|
||||
impl Decf {
|
||||
pub fn new() -> Decf {
|
||||
Decf { as_num: 0.0 }
|
||||
Decf
|
||||
}
|
||||
}
|
||||
impl Formatter for Decf {
|
||||
|
||||
@@ -5,12 +5,10 @@ use super::super::format_field::FormatField;
|
||||
use super::super::formatter::{FormatPrimitive, Formatter, InPrefix};
|
||||
use super::float_common::{get_primitive_dec, primitive_to_str_common, FloatAnalysis};
|
||||
|
||||
pub struct Floatf {
|
||||
as_num: f64,
|
||||
}
|
||||
pub struct Floatf;
|
||||
impl Floatf {
|
||||
pub fn new() -> Floatf {
|
||||
Floatf { as_num: 0.0 }
|
||||
Floatf
|
||||
}
|
||||
}
|
||||
impl Formatter for Floatf {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user