Skip to content

Commit

Permalink
Improve with zero-copied writing.
Browse files Browse the repository at this point in the history
  • Loading branch information
KmolYuan committed Oct 5, 2023
1 parent fd1a531 commit 1f74201
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 76 deletions.
4 changes: 2 additions & 2 deletions four-bar-ui/assets/main.js → four-bar-ui/assets/utility.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Utility functions
/* Utility functions */

// Preload
window.preload = () => new URLSearchParams(window.location.search).get('code') || "";
window.load_url = () => new URLSearchParams(window.location.search).get('code') || "";
window.loading_finished = () => document.getElementById("loading-text").remove();

// IO
Expand Down
4 changes: 2 additions & 2 deletions four-bar-ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@
<p style="font-size: 16px">Loading…</p>
<div class="lds-dual-ring"></div>
</div>
<link data-trunk rel="copy-file" href="assets/main.js" />
<script type="module" src="main.js"></script>
<link data-trunk rel="copy-file" href="assets/utility.js" />
<script type="module" src="utility.js"></script>
<link data-trunk rel="rust" data-wasm-opt="z" data-cargo-no-default-features />
</body>

Expand Down
4 changes: 2 additions & 2 deletions four-bar-ui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,10 @@ impl App {
{
#[wasm_bindgen::prelude::wasm_bindgen]
extern "C" {
fn preload() -> String;
fn load_url() -> String;
fn loading_finished();
}
if let Ok(fb) = ron::from_str(&preload()) {
if let Ok(fb) = ron::from_str(&load_url()) {
app.link.projs.queue().push(None, fb);
}
loading_finished();
Expand Down
2 changes: 1 addition & 1 deletion four-bar-ui/src/app/proj/impl_proj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ where
io::save_csv_ask(&get_curve(*pivot, &self.fb, cfg.res));
}
if small_btn(ui, "🗐", "Copy") {
let t = csv::dump_csv(get_curve(*pivot, &self.fb, cfg.res)).unwrap();
let t = csv::csv_string(get_curve(*pivot, &self.fb, cfg.res)).unwrap();
ui.output_mut(|s| s.copied_text = t);
}
});
Expand Down
4 changes: 2 additions & 2 deletions four-bar-ui/src/app/syn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,8 @@ impl Synthesis {
ui.horizontal_wrapped(|ui| {
if ui.button("🗐 Copy CSV").clicked() {
let text = match &self.target {
io::Curve::P(t) => csv::dump_csv(t).unwrap(),
io::Curve::S(t) => csv::dump_csv(t).unwrap(),
io::Curve::P(t) => csv::csv_string(t).unwrap(),
io::Curve::S(t) => csv::csv_string(t).unwrap(),
};
ui.output_mut(|s| s.copied_text = text);
}
Expand Down
24 changes: 16 additions & 8 deletions four-bar-ui/src/cli/syn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,8 @@ fn from_runtime(
}
let lnk_path = root.join(LNK_RON);
match &lnk_fb {
syn_cmd::SolvedFb::Fb(fb, _) => std::fs::write(lnk_path, io::ron_string(fb))?,
syn_cmd::SolvedFb::SFb(fb, _) => std::fs::write(lnk_path, io::ron_string(fb))?,
syn_cmd::SolvedFb::Fb(fb, _) => write_ron(lnk_path, fb)?,
syn_cmd::SolvedFb::SFb(fb, _) => write_ron(lnk_path, fb)?,
}
// Log results
let refer = refer
Expand All @@ -290,7 +290,7 @@ fn from_runtime(
.add_line("Target", target, plot::Style::Circle, RED)
.add_line("Optimized", &curve, plot::Style::Line, BLUE_900);
{
std::fs::write(root.join(LNK_FIG), io::ron_string(&fig))?;
write_ron(root.join(LNK_FIG), &fig)?;
let path = root.join(LNK_SVG);
let svg = plot::SVGBackend::new(&path, (1600, 1600));
fig.plot(svg)?;
Expand All @@ -312,7 +312,7 @@ fn from_runtime(
writeln!(log, "error={err:.04}")?;
writeln!(log, "\n[atlas.fb]")?;
log_fb(&mut log, &fb)?;
std::fs::write(root.join("atlas.ron"), io::ron_string(&fb))?;
write_ron(root.join("atlas.ron"), &fb)?;
fig.push_line("Atlas", c, plot::Style::Dot, GREEN_900);
}
writeln!(log, "\n[optimized]")?;
Expand All @@ -339,7 +339,7 @@ fn from_runtime(
fig.push_line("Ref. [?]", c, plot::Style::DashedLine, ORANGE_900);
}
fig.fb = None;
std::fs::write(root.join(CURVE_FIG), io::ron_string(&fig))?;
write_ron(root.join(CURVE_FIG), &fig)?;
let path = root.join(CURVE_SVG);
let svg = plot::SVGBackend::new(&path, (1600, 1600));
fig.plot(svg)?;
Expand All @@ -357,7 +357,7 @@ fn from_runtime(
.add_line("Target", target, plot::Style::Circle, RED)
.add_line("Optimized", &curve, plot::Style::Line, BLUE_900);
{
std::fs::write(root.join(LNK_FIG), io::ron_string(&fig))?;
write_ron(root.join(LNK_FIG), &fig)?;
let path = root.join(LNK_SVG);
let svg = plot::SVGBackend::new(&path, (1600, 1600));
fig.plot(svg)?;
Expand All @@ -379,7 +379,7 @@ fn from_runtime(
writeln!(log, "error={err:.04}")?;
writeln!(log, "\n[atlas.fb]")?;
log_sfb(&mut log, &fb)?;
std::fs::write(root.join("atlas.ron"), io::ron_string(&fb))?;
write_ron(root.join("atlas.ron"), &fb)?;
fig.push_line("Atlas", c, plot::Style::Dot, CYAN);
}
writeln!(log, "\n[optimized]")?;
Expand All @@ -406,7 +406,7 @@ fn from_runtime(
fig.push_line("Ref. [?]", c, plot::Style::DashedLine, ORANGE_900);
}
fig.fb = Some(Cow::Owned(fb.take_sphere()));
std::fs::write(root.join(CURVE_FIG), io::ron_string(&fig))?;
write_ron(root.join(CURVE_FIG), &fig)?;
let path = root.join(CURVE_SVG);
let svg = plot::SVGBackend::new(&path, (1600, 1600));
fig.plot(svg)?;
Expand Down Expand Up @@ -486,3 +486,11 @@ fn log_sfb(mut w: impl std::io::Write, fb: &SFourBar) -> std::io::Result<()> {
write!(w, "stat={}", fb.stat)?;
Ok(())
}

fn write_ron<S>(path: impl AsRef<Path>, s: &S) -> Result<(), SynErr>
where
S: serde::Serialize,
{
ron::ser::to_writer_pretty(std::fs::File::create(path)?, s, Default::default())?;
Ok(())
}
106 changes: 54 additions & 52 deletions four-bar-ui/src/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,14 @@ const IMG_EXT: &[&str] = &["png", "jpg", "jpeg"];

#[cfg(target_arch = "wasm32")]
mod impl_io {
use super::Alert as _;
use super::Alert;
use std::path::{Path, PathBuf};
use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsValue};

#[wasm_bindgen]
extern "C" {
fn open_file(ext: &str, done: JsValue, is_multiple: bool, is_bin: bool);
fn save_file(s: &str, path: &str);
#[wasm_bindgen(js_name = save_file)]
fn save_bin(s: &[u8], path: &str);
fn save_file(s: &[u8], path: &str);
}

fn js_ext(ext: &[&str]) -> String {
Expand Down Expand Up @@ -72,33 +70,31 @@ mod impl_io {
open_file(&js_ext(ext), Closure::once_into_js(done), false, true);
}

pub(super) fn save_ask<C>(s: &str, name: &str, _fmt: &str, _ext: &[&str], done: C)
pub(super) fn save_ask<W, E, C>(name: &str, _fmt: &str, _ext: &[&str], write: W, done: C)
where
C: FnOnce(PathBuf) + 'static,
W: FnOnce(std::io::Cursor<&mut [u8]>) -> E,
E: Alert,
C: FnOnce(PathBuf),
{
let name = PathBuf::from(name);
save(s, &name);
done(name);
let path = PathBuf::from(name);
save(&path, write);
done(path);
}

pub(super) fn save(s: &str, name: &Path) {
save_file(s, name.to_str().unwrap());
}

pub(super) fn save_bin_ask<C, E>(name: &str, _fmt: &str, _ext: &[&str], write: C)
pub(super) fn save<W, E>(name: &Path, write: W)
where
C: FnOnce(std::io::Cursor<&mut [u8]>) -> Result<(), E>,
E: std::error::Error,
W: FnOnce(std::io::Cursor<&mut [u8]>) -> E,
E: Alert,
{
let mut buf = Vec::new();
write(std::io::Cursor::new(&mut buf)).alert("Save File");
save_bin(&buf, name);
write(std::io::Cursor::new(&mut buf)).alert("Write File");
save_file(&buf, name.as_os_str().to_str().unwrap());
}
}

#[cfg(not(target_arch = "wasm32"))]
mod impl_io {
use super::Alert as _;
use super::Alert;
use std::path::{Path, PathBuf};

pub(super) fn open<C>(fmt: &str, ext: &[&str], done: C)
Expand Down Expand Up @@ -141,35 +137,29 @@ mod impl_io {
}
}

pub(super) fn save_ask<C>(s: &str, name: &str, fmt: &str, ext: &[&str], done: C)
pub(super) fn save_ask<W, E, C>(name: &str, fmt: &str, ext: &[&str], write: W, done: C)
where
C: FnOnce(PathBuf) + 'static,
W: FnOnce(std::fs::File) -> E,
E: Alert,
C: FnOnce(PathBuf),
{
if let Some(path) = rfd::FileDialog::new()
.set_file_name(name)
.add_filter(fmt, ext)
.save_file()
{
std::fs::write(&path, s).alert_then("Save File", |_| done(path));
std::fs::File::create(&path).alert_then("Save File", |w| {
write(w).alert_then("Write File", |_| done(path));
});
}
}

pub(super) fn save(s: &str, path: &Path) {
std::fs::write(path, s).alert("Save File");
}

pub(super) fn save_bin_ask<C, E>(name: &str, fmt: &str, ext: &[&str], write: C)
pub(super) fn save<W, E>(path: &Path, write: W)
where
C: FnOnce(std::fs::File) -> Result<(), E>,
E: std::error::Error,
W: FnOnce(std::fs::File) -> E,
E: Alert,
{
if let Some(path) = rfd::FileDialog::new()
.set_file_name(name)
.add_filter(fmt, ext)
.save_file()
{
std::fs::File::create(path).alert_then("Create File", |f| write(f).alert("Save File"));
}
std::fs::File::create(path).alert_then("Save File", |w| write(w).alert("Write File"));
}
}

Expand Down Expand Up @@ -296,54 +286,66 @@ where
open_bin_single(IMG_FMT, IMG_EXT, done);
}

pub(crate) fn save_csv_ask<S>(curve: &[S])
pub(crate) fn save_csv_ask<S>(c: &[S])
where
S: serde::Serialize + Clone,
{
let s = csv::dump_csv(curve).unwrap();
save_ask(&s, "curve.csv", CSV_FMT, CSV_EXT, |_| ());
save_ask(
"curve.csv",
CSV_FMT,
CSV_EXT,
|w| csv::dump_csv(w, c),
|_| (),
);
}

pub(crate) fn save_cb_ask<C, D>(cb: &cb::Codebook<C, D>)
where
C: cb::Code<D> + Send,
D: efd::EfdDim,
{
save_bin_ask("cb.npz", CB_FMT, CB_EXT, |w| cb.write(w));
save_ask("cb.npz", CB_FMT, CB_EXT, |w| cb.write(w), |_| ());
}

fn write_ron<W, S>(w: W, s: &S) -> Result<(), ron::Error>
where
W: std::io::Write,
S: serde::Serialize,
{
ron::ser::to_writer_pretty(w, s, Default::default())
}

pub(crate) fn save_ron_ask<S, C>(s: &S, name: &str, done: C)
where
S: serde::Serialize,
C: FnOnce(PathBuf) + 'static,
{
save_ask(&ron_string(s), name, FMT, EXT, done);
save_ask(name, FMT, EXT, |w| write_ron(w, s), done);
}

pub(crate) fn save_ron<S>(s: &S, path: &Path)
where
S: serde::Serialize,
{
save(&ron_string(s), path);
save(path, |w| write_ron(w, s));
}

pub(crate) fn save_svg_ask(buf: &str, name: &str) {
save_ask(buf, name, SVG_FMT, SVG_EXT, |_| ());
use std::io::Write as _;
save_ask(
name,
SVG_FMT,
SVG_EXT,
|mut w| w.write_all(buf.as_bytes()),
|_| (),
);
}

pub(crate) fn save_history_ask(history: &[f64], name: &str) {
let mut buf = String::new();
let svg = plot::SVGBackend::with_string(&mut buf, (800, 600));
plot2d::history(svg, history).unwrap();
save_ask(&buf, name, SVG_FMT, SVG_EXT, |_| ());
}

pub(crate) fn ron_string<S>(value: &S) -> String
where
S: serde::Serialize,
{
// Use default options to serialize the data
ron::ser::to_string_pretty(value, Default::default()).unwrap()
save_svg_ask(&buf, name);
}

#[derive(serde::Deserialize, serde::Serialize, Clone)]
Expand Down
25 changes: 18 additions & 7 deletions four-bar/src/csv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pub use csv::Error;
use csv::{ReaderBuilder, Writer};
use serde::{de::DeserializeOwned, Serialize};
use std::borrow::Cow;

/// Parse CSV from string.
pub fn parse_csv<D, R>(r: R) -> Result<Vec<D>, Error>
Expand All @@ -23,14 +22,26 @@ where
}
}

/// Dump CSV to a writer.
pub fn dump_csv<'a, W, C, S>(w: W, c: C) -> Result<(), csv::Error>
where
W: std::io::Write,
C: AsRef<[S]>,
S: Serialize + Clone + 'a,
{
let mut w = Writer::from_writer(w);
c.as_ref().iter().try_for_each(|c| w.serialize(c))?;
w.flush()?;
Ok(())
}

/// Dump CSV to string.
pub fn dump_csv<'a, C, S>(c: C) -> Result<String, Box<dyn std::error::Error>>
pub fn csv_string<'a, C, S>(c: C) -> Result<String, csv::Error>
where
Cow<'a, [S]>: From<C>,
C: AsRef<[S]>,
S: Serialize + Clone + 'a,
{
let mut w = Writer::from_writer(Vec::new());
let v = Cow::from(c).into_owned();
v.into_iter().try_for_each(|c| w.serialize(c))?;
Ok(String::from_utf8(w.into_inner()?)?)
let mut w = Vec::new();
dump_csv(&mut w, c)?;
Ok(String::from_utf8(w).unwrap())
}

0 comments on commit 1f74201

Please sign in to comment.