Skip to content

Commit

Permalink
Merge pull request #313 from ms-jpq/dev
Browse files Browse the repository at this point in the history
less alloc
  • Loading branch information
ms-jpq authored Mar 23, 2024
2 parents 34e6629 + cdd77a8 commit 3c17cc5
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 122 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ authors = ["[email protected]"]
description = "Space Age seD | https://github.com/ms-jpq/sad"
edition = "2021"
name = "sad"
version = "0.4.25"
version = "0.4.26"


[dependencies]
Expand Down
6 changes: 5 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## Good news

v0.4.24
v0.4.26

- Reduce allocations

- Preview multi-selects

- Fewer dependencies

Expand Down
41 changes: 23 additions & 18 deletions src/displace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ use {
super::{
argparse::{Action, Engine, Options},
fs_pipe::{slurp, spit},
input::LineIn,
input::RowIn,
types::Die,
udiff::{apply_patches, patches, pure_diffs, udiff},
},
ansi_term::Colour,
std::{ffi::OsString, path::PathBuf, sync::Arc},
std::{borrow::ToOwned, ffi::OsString, path::PathBuf},
};

impl Engine {
Expand All @@ -19,17 +19,17 @@ impl Engine {
}
}

impl LineIn {
impl RowIn {
const fn path(&self) -> &PathBuf {
match self {
Self::Entire(path) | Self::Piecewise(path, _) => path,
}
}
}

pub async fn displace(opts: &Arc<Options>, input: LineIn) -> Result<OsString, Die> {
pub async fn displace(opts: &Options, input: RowIn) -> Result<OsString, Die> {
let path = input.path().clone();
let name = opts
let mut name = opts
.cwd
.as_ref()
.and_then(|cwd| path.strip_prefix(cwd).ok())
Expand All @@ -39,29 +39,34 @@ pub async fn displace(opts: &Arc<Options>, input: LineIn) -> Result<OsString, Di

let slurped = slurp(&path).await?;
let before = slurped.content;
let after = opts.engine.replace(&before);
let replaced = {
let b = before.clone().into_iter().collect::<String>();
opts.engine.replace(&b)
};
let after = replaced
.split_inclusive('\n')
.map(ToOwned::to_owned)
.collect::<Vec<_>>();

if *before == after {
Ok(OsString::new())
} else {
let print = match (&opts.action, input) {
(Action::Preview, LineIn::Entire(_)) => udiff(None, opts.unified, &name, &before, &after),
(Action::Preview, LineIn::Piecewise(_, ranges)) => {
(Action::Preview, RowIn::Entire(_)) => udiff(None, opts.unified, &name, &before, &after),
(Action::Preview, RowIn::Piecewise(_, ranges)) => {
udiff(Some(&ranges), opts.unified, &name, &before, &after)
}
(Action::Commit, LineIn::Entire(_)) => {
spit(&path, &slurped.meta, &after).await?;
let mut out = name;
out.push("\n");
out
(Action::Commit, RowIn::Entire(_)) => {
spit(&path, &slurped.meta, after).await?;
name.push("\n");
name
}
(Action::Commit, LineIn::Piecewise(_, ranges)) => {
(Action::Commit, RowIn::Piecewise(_, ranges)) => {
let patches = patches(opts.unified, &before, &after);
let after = apply_patches(patches, &ranges, &before);
spit(&path, &slurped.meta, &after).await?;
let mut out = name;
out.push("\n");
out
spit(&path, &slurped.meta, after).await?;
name.push("\n");
name
}
(Action::FzfPreview(_, _), _) => {
let ranges = pure_diffs(opts.unified, &before, &after);
Expand Down
54 changes: 36 additions & 18 deletions src/fs_pipe.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
use {
super::types::Die,
std::{borrow::ToOwned, fs::Metadata, io::ErrorKind, path::Path},
std::{borrow::ToOwned, fs::Metadata, path::Path},
tokio::{
fs::{rename, File, OpenOptions},
io::{AsyncReadExt, AsyncWriteExt, BufWriter},
io::{AsyncBufReadExt, AsyncWriteExt, BufReader, BufWriter},
},
uuid::Uuid,
};

pub struct Slurpee {
pub meta: Metadata,
pub content: String,
pub content: Vec<String>,
}

pub async fn slurp(path: &Path) -> Result<Slurpee, Die> {
let mut fd = File::open(path)
let fd = File::open(path)
.await
.map_err(|e| Die::IO(path.to_owned(), e.kind()))?;

Expand All @@ -23,21 +23,37 @@ pub async fn slurp(path: &Path) -> Result<Slurpee, Die> {
.await
.map_err(|e| Die::IO(path.to_owned(), e.kind()))?;

let content = if meta.is_file() {
let mut s = String::new();
match fd.read_to_string(&mut s).await {
Ok(_) => s,
Err(err) if err.kind() == ErrorKind::InvalidData => s,
Err(err) => return Err(Die::IO(path.to_owned(), err.kind())),
let mut slurm = Slurpee {
meta,
content: Vec::new(),
};
if slurm.meta.is_file() {
let mut reader = BufReader::new(fd);
loop {
let mut buf = Vec::new();
match reader.read_until(b'\n', &mut buf).await {
Err(err) => return Err(Die::IO(path.to_owned(), err.kind())),
Ok(0) => break,
Ok(_) => {
if let Ok(s) = String::from_utf8(buf) {
slurm.content.push(s);
} else {
slurm.content.clear();
return Ok(slurm);
}
}
}
}
} else {
String::new()
};

Ok(Slurpee { meta, content })
Ok(slurm)
}

pub async fn spit(canonical: &Path, meta: &Metadata, text: &str) -> Result<(), Die> {
pub async fn spit(
canonical: &Path,
meta: &Metadata,
text: Vec<impl AsRef<[u8]> + Send>,
) -> Result<(), Die> {
let uuid = Uuid::new_v4().as_simple().to_string();
let mut file_name = canonical
.file_name()
Expand All @@ -58,10 +74,12 @@ pub async fn spit(canonical: &Path, meta: &Metadata, text: &str) -> Result<(), D
.map_err(|e| Die::IO(tmp.clone(), e.kind()))?;

let mut writer = BufWriter::new(fd);
writer
.write_all(text.as_bytes())
.await
.map_err(|e| Die::IO(tmp.clone(), e.kind()))?;
for t in text {
writer
.write_all(t.as_ref())
.await
.map_err(|e| Die::IO(tmp.clone(), e.kind()))?;
}

writer
.flush()
Expand Down
4 changes: 2 additions & 2 deletions src/fzf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ pub fn stream_fzf_proc(
args: arguments,
env: fzf_env,
};
stream_subproc(cmd, stream).then(|line| async {
match line {
stream_subproc(cmd, stream).then(|row| async {
match row {
Ok(o) => Ok(o),
Err(Die::BadExit(_, 130)) => Err(Die::Interrupt),
e => {
Expand Down
28 changes: 14 additions & 14 deletions src/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,20 @@ use {
};

#[derive(Debug)]
pub enum LineIn {
pub enum RowIn {
Entire(PathBuf),
Piecewise(PathBuf, HashSet<DiffRange>),
}

#[derive(Debug)]
struct DiffLine(PathBuf, DiffRange);
struct DiffRow(PathBuf, DiffRange);

fn p_line(line: &str) -> Result<DiffLine, Die> {
fn p_row(row: &str) -> Result<DiffRow, Die> {
let f = || Die::ArgumentError(String::new());
let ff = |_| f();
let preg = "\n\n\n\n@@ -(\\d+),(\\d+) \\+(\\d+),(\\d+) @@$";
let re = Regex::new(preg).map_err(Die::RegexError)?;
let captures = re.captures(line).ok_or_else(f)?;
let captures = re.captures(row).ok_or_else(f)?;

let before_start = captures
.get(1)
Expand Down Expand Up @@ -66,11 +66,11 @@ fn p_line(line: &str) -> Result<DiffLine, Die> {
before: (before_start - 1, before_inc),
after: (after_start - 1, after_inc),
};
let path = PathBuf::from(String::from(re.replace(line, "")));
Ok(DiffLine(path, range))
let path = PathBuf::from(String::from(re.replace(row, "")));
Ok(DiffRow(path, range))
}

async fn stream_patch(patches: &Path) -> impl Stream<Item = Result<LineIn, Die>> {
async fn stream_patch(patches: &Path) -> impl Stream<Item = Result<RowIn, Die>> {
let patches = patches.to_owned();

let fd = match File::open(&patches).await {
Expand Down Expand Up @@ -99,12 +99,12 @@ async fn stream_patch(patches: &Path) -> impl Stream<Item = Result<LineIn, Die>>
let ranges = s.3;
s.2 = PathBuf::new();
s.3 = HashSet::new();
Ok(Some((Some(LineIn::Piecewise(path, ranges)), s)))
Ok(Some((Some(RowIn::Piecewise(path, ranges)), s)))
}
Some(buf) => {
let line =
let row =
String::from_utf8(buf).map_err(|_| Die::IO(s.1.clone(), ErrorKind::InvalidData))?;
let parsed = p_line(&line)?;
let parsed = p_row(&row)?;
if parsed.0 == s.2 {
s.3.insert(parsed.1);
Ok(Some((None, s)))
Expand All @@ -117,7 +117,7 @@ async fn stream_patch(patches: &Path) -> impl Stream<Item = Result<LineIn, Die>>
if ranges.is_empty() {
Ok(Some((None, s)))
} else {
Ok(Some((Some(LineIn::Piecewise(path, ranges)), s)))
Ok(Some((Some(RowIn::Piecewise(path, ranges)), s)))
}
}
}
Expand Down Expand Up @@ -147,7 +147,7 @@ fn u8_pathbuf(v8: Vec<u8>) -> PathBuf {
}
}

fn stream_stdin(use_nul: bool) -> impl Stream<Item = Result<LineIn, Die>> {
fn stream_stdin(use_nul: bool) -> impl Stream<Item = Result<RowIn, Die>> {
if io::stdin().is_terminal() {
let err = Die::ArgumentError("/dev/stdin connected to tty".to_owned());
return Either::Left(once(ready(Err(err))));
Expand All @@ -171,7 +171,7 @@ fn stream_stdin(use_nul: bool) -> impl Stream<Item = Result<LineIn, Die>> {
Err(e) => Err(Die::IO(path, e.kind())),
Ok(canonical) => Ok(Some({
if s.1.insert(canonical.clone()) {
(Some(LineIn::Entire(canonical)), s)
(Some(RowIn::Entire(canonical)), s)
} else {
(None, s)
}
Expand All @@ -184,7 +184,7 @@ fn stream_stdin(use_nul: bool) -> impl Stream<Item = Result<LineIn, Die>> {
Either::Right(stream.try_filter_map(|x| ready(Ok(x))))
}

pub async fn stream_in(mode: &Mode, args: &Arguments) -> impl Stream<Item = Result<LineIn, Die>> {
pub async fn stream_in(mode: &Mode, args: &Arguments) -> impl Stream<Item = Result<RowIn, Die>> {
match mode {
Mode::Initial => Either::Left(stream_stdin(args.read0)),
Mode::Preview(path) | Mode::Patch(path) => Either::Right(stream_patch(path).await),
Expand Down
13 changes: 4 additions & 9 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ use {
path::PathBuf,
pin::pin,
process::{ExitCode, Termination},
sync::Arc,
thread::available_parallelism,
},
subprocess::{stream_into, stream_subproc},
Expand Down Expand Up @@ -67,7 +66,7 @@ async fn consume(stream: impl Stream<Item = Result<(), Die>> + Send) -> Result<(
});
let out = select(
stream
.filter_map(|row| async { row.err() })
.filter_map(|row| ready(row.err()))
.chain(once(ready(Die::Eof))),
int,
);
Expand All @@ -82,16 +81,12 @@ async fn run(threads: usize) -> Result<(), Die> {
let (mode, args) = parse_args();
let input_stream = stream_in(&mode, &args).await;
let opts = parse_opts(mode, args)?;
let options = Arc::new(opts);
let opts = options.clone();

let trans_stream = input_stream
.map_ok(move |input| {
let opts = options.clone();
async move { displace(&opts, input).await }
})
.map_ok(|input| displace(&opts, input))
.try_buffer_unordered(threads);

let out_stream = stream_sink(&opts, trans_stream.boxed());

consume(out_stream).await
}

Expand Down
Loading

0 comments on commit 3c17cc5

Please sign in to comment.