Querying and progress changes
parent
651d2ec542
commit
0a4c759188
|
@ -184,49 +184,52 @@ fn parse_cmd_line_args() -> anyhow::Result<InstArgs> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn print_progress(done: usize, total: usize, real_start: Instant,
|
fn print_editing_status(done: usize, total: usize, real_start: Instant,
|
||||||
eta_start: Instant)
|
eta_start: Instant, show_progress: bool)
|
||||||
{
|
{
|
||||||
let progress = match total {
|
|
||||||
0 => 0.0,
|
|
||||||
_ => done as f32 / total as f32
|
|
||||||
};
|
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let real_elapsed = now.duration_since(real_start);
|
let real_elapsed = now.duration_since(real_start);
|
||||||
let eta_elapsed = now.duration_since(eta_start);
|
|
||||||
|
|
||||||
let remaining = if progress >= 0.1 {
|
if show_progress {
|
||||||
Some(Duration::from_secs_f32(
|
let eta_elapsed = now.duration_since(eta_start);
|
||||||
eta_elapsed.as_secs_f32() / progress * (1.0 - progress)
|
let progress = match total {
|
||||||
))
|
0 => 0.,
|
||||||
} else {
|
_ => done as f32 / total as f32
|
||||||
None
|
};
|
||||||
};
|
|
||||||
|
|
||||||
const TOTAL_BARS: usize = 25;
|
let remaining = if progress >= 0.1 {
|
||||||
let num_bars = (progress * TOTAL_BARS as f32) as usize;
|
Some(Duration::from_secs_f32(
|
||||||
let bars = "=".repeat(num_bars);
|
eta_elapsed.as_secs_f32() / progress * (1. - progress)
|
||||||
|
))
|
||||||
eprint!(
|
|
||||||
"\r[{bars:<total_bars$}] {progress:.1}% | {elapsed} elapsed \
|
|
||||||
| {remaining} remaining",
|
|
||||||
bars=bars,
|
|
||||||
total_bars=TOTAL_BARS,
|
|
||||||
progress=progress * 100.0,
|
|
||||||
elapsed=fmt_duration(real_elapsed),
|
|
||||||
remaining=if let Some(d) = remaining {
|
|
||||||
fmt_duration(d)
|
|
||||||
} else {
|
} else {
|
||||||
String::from("--:--")
|
None
|
||||||
}
|
};
|
||||||
);
|
|
||||||
|
const TOTAL_BARS: usize = 25;
|
||||||
|
let num_bars = (progress * TOTAL_BARS as f32) as usize;
|
||||||
|
let bars = "=".repeat(num_bars);
|
||||||
|
|
||||||
|
eprint!(
|
||||||
|
"\r[{bars:<total_bars$}] {progress:.1}% | {elapsed} elapsed \
|
||||||
|
| {remaining} remaining",
|
||||||
|
bars=bars,
|
||||||
|
total_bars=TOTAL_BARS,
|
||||||
|
progress=progress * 100.,
|
||||||
|
elapsed=fmt_duration(real_elapsed),
|
||||||
|
remaining=if let Some(d) = remaining {
|
||||||
|
fmt_duration(d)
|
||||||
|
} else {
|
||||||
|
String::from("--:--")
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
eprint!("\rProcessing... {} elapsed", fmt_duration(real_elapsed));
|
||||||
|
}
|
||||||
|
|
||||||
std::io::stdout().flush().unwrap();
|
std::io::stdout().flush().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn print_log(log_type: LogType, msg: String) {
|
fn print_log(log_type: LogType, msg: String) {
|
||||||
eprintln!("{}: {}", log_type, msg)
|
eprintln!("{}: {}", log_type, msg)
|
||||||
}
|
}
|
||||||
|
@ -246,7 +249,7 @@ pub fn run_cmd_line() {
|
||||||
let (handle, status) = spawn_compute_thread(args);
|
let (handle, status) = spawn_compute_thread(args);
|
||||||
|
|
||||||
const TICK: Duration = Duration::from_millis(25);
|
const TICK: Duration = Duration::from_millis(25);
|
||||||
const UPDATE_INTERVAL: Duration = Duration::from_millis(250);
|
const UPDATE_INTERVAL: Duration = Duration::from_millis(500);
|
||||||
|
|
||||||
let mut last_update = Instant::now();
|
let mut last_update = Instant::now();
|
||||||
let mut querying_start = last_update;
|
let mut querying_start = last_update;
|
||||||
|
@ -311,8 +314,8 @@ pub fn run_cmd_line() {
|
||||||
{
|
{
|
||||||
let s = status.get();
|
let s = status.get();
|
||||||
// TODO: Update duration format? e.g. 1m 42s remaining
|
// TODO: Update duration format? e.g. 1m 42s remaining
|
||||||
print_progress(s.blocks_done, s.blocks_total,
|
print_editing_status(s.blocks_done, s.blocks_total,
|
||||||
querying_start, editing_start);
|
querying_start, editing_start, s.show_progress);
|
||||||
last_update = now;
|
last_update = now;
|
||||||
need_newline = true;
|
need_newline = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ fn clone(inst: &mut InstBundle) {
|
||||||
let offset = inst.args.offset.unwrap();
|
let offset = inst.args.offset.unwrap();
|
||||||
let dst_area = src_area + offset;
|
let dst_area = src_area + offset;
|
||||||
let mut keys = query_keys(&mut inst.db, &inst.status,
|
let mut keys = query_keys(&mut inst.db, &inst.status,
|
||||||
None, Some(dst_area), false, true);
|
Vec::new(), Some(dst_area), false, true);
|
||||||
|
|
||||||
// Sort blocks according to offset such that we don't read blocks that
|
// Sort blocks according to offset such that we don't read blocks that
|
||||||
// have already been written.
|
// have already been written.
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::utils::query_keys;
|
||||||
|
|
||||||
|
|
||||||
fn delete_blocks(inst: &mut InstBundle) {
|
fn delete_blocks(inst: &mut InstBundle) {
|
||||||
let keys = query_keys(&mut inst.db, &inst.status, None,
|
let keys = query_keys(&mut inst.db, &inst.status, Vec::new(),
|
||||||
inst.args.area, inst.args.invert, false);
|
inst.args.area, inst.args.invert, false);
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ fn delete_metadata(inst: &mut InstBundle) {
|
||||||
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
||||||
|
|
||||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||||
node.as_deref(), inst.args.area, inst.args.invert, true);
|
node.iter().collect(), inst.args.area, inst.args.invert, true);
|
||||||
|
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
|
|
|
@ -66,7 +66,7 @@ fn delete_objects(inst: &mut InstBundle) {
|
||||||
.map(|s| TwoWaySearcher::new(s));
|
.map(|s| TwoWaySearcher::new(s));
|
||||||
|
|
||||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||||
search_obj.as_deref(), inst.args.area, inst.args.invert, true);
|
search_obj.iter().collect(), inst.args.area, inst.args.invert, true);
|
||||||
|
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
|
|
|
@ -11,7 +11,7 @@ fn delete_timers(inst: &mut InstBundle) {
|
||||||
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
||||||
|
|
||||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||||
node.as_deref(), inst.args.area, inst.args.invert, true);
|
node.iter().collect(), inst.args.area, inst.args.invert, true);
|
||||||
|
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
|
|
|
@ -26,7 +26,7 @@ fn fill(inst: &mut InstBundle) {
|
||||||
let node = inst.args.new_node.as_ref().unwrap().as_bytes().to_owned();
|
let node = inst.args.new_node.as_ref().unwrap().as_bytes().to_owned();
|
||||||
|
|
||||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||||
None, Some(area), false, true);
|
Vec::new(), Some(area), false, true);
|
||||||
|
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,8 @@ fn overlay_no_offset(inst: &mut InstBundle) {
|
||||||
let invert = inst.args.invert;
|
let invert = inst.args.invert;
|
||||||
|
|
||||||
// Get keys from input database.
|
// Get keys from input database.
|
||||||
let keys = query_keys(&mut idb, &inst.status, None,
|
let keys = query_keys(&mut idb, &inst.status,
|
||||||
inst.args.area, invert, true);
|
Vec::new(), inst.args.area, invert, true);
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
|
|
||||||
for key in keys {
|
for key in keys {
|
||||||
|
@ -106,8 +106,8 @@ fn overlay_with_offset(inst: &mut InstBundle) {
|
||||||
let idb = inst.idb.as_mut().unwrap();
|
let idb = inst.idb.as_mut().unwrap();
|
||||||
|
|
||||||
// Get keys from output database.
|
// Get keys from output database.
|
||||||
let keys = query_keys(&mut inst.db, &inst.status, None,
|
let keys = query_keys(&mut inst.db, &inst.status,
|
||||||
dst_area, inst.args.invert, true);
|
Vec::new(), dst_area, inst.args.invert, true);
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
|
|
||||||
for key in keys {
|
for key in keys {
|
||||||
|
|
|
@ -59,7 +59,7 @@ fn replace_in_inv(inst: &mut InstBundle) {
|
||||||
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
||||||
|
|
||||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||||
node.as_deref(), inst.args.area, inst.args.invert, true);
|
node.iter().collect(), inst.args.area, inst.args.invert, true);
|
||||||
|
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
let mut item_mods: u64 = 0;
|
let mut item_mods: u64 = 0;
|
||||||
|
|
|
@ -113,10 +113,10 @@ fn do_replace(
|
||||||
|
|
||||||
|
|
||||||
fn replace_nodes(inst: &mut InstBundle) {
|
fn replace_nodes(inst: &mut InstBundle) {
|
||||||
let node = inst.args.node.as_ref().unwrap().as_bytes();
|
let node = inst.args.node.as_ref().unwrap().as_bytes().to_owned();
|
||||||
let new_node = inst.args.new_node.as_ref().unwrap().as_bytes();
|
let new_node = inst.args.new_node.as_ref().unwrap().as_bytes().to_owned();
|
||||||
let keys = query_keys(&mut inst.db, &inst.status,
|
let keys = query_keys(&mut inst.db, &inst.status,
|
||||||
Some(node), inst.args.area, inst.args.invert, true);
|
vec![&node], inst.args.area, inst.args.invert, true);
|
||||||
|
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
|
|
|
@ -13,7 +13,7 @@ fn set_meta_var(inst: &mut InstBundle) {
|
||||||
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
||||||
|
|
||||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||||
node.as_deref(), inst.args.area, inst.args.invert, true);
|
node.iter().collect(), inst.args.area, inst.args.invert, true);
|
||||||
|
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
let mut count: u64 = 0;
|
let mut count: u64 = 0;
|
||||||
|
|
|
@ -46,7 +46,7 @@ fn set_param2(inst: &mut InstBundle) {
|
||||||
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
let node = inst.args.node.as_ref().map(|s| s.as_bytes().to_owned());
|
||||||
|
|
||||||
let keys = query_keys(&mut inst.db, &mut inst.status,
|
let keys = query_keys(&mut inst.db, &mut inst.status,
|
||||||
node.as_deref(), inst.args.area, false, true);
|
node.iter().collect(), inst.args.area, false, true);
|
||||||
|
|
||||||
inst.status.begin_editing();
|
inst.status.begin_editing();
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
use super::Command;
|
use super::Command;
|
||||||
|
|
||||||
use std::time::Instant;
|
|
||||||
use crate::instance::InstBundle;
|
use crate::instance::InstBundle;
|
||||||
use crate::utils::fmt_duration;
|
|
||||||
|
|
||||||
|
|
||||||
fn vacuum(inst: &mut InstBundle) {
|
fn vacuum(inst: &mut InstBundle) {
|
||||||
inst.status.log_info("Starting vacuum.");
|
inst.status.log_info("Starting vacuum.");
|
||||||
let start = Instant::now();
|
|
||||||
|
|
||||||
// TODO: Show simple timer in main thread.
|
inst.status.set_show_progress(false); // No ETA for vacuum.
|
||||||
match inst.db.vacuum() {
|
inst.status.begin_editing();
|
||||||
|
let res = inst.db.vacuum();
|
||||||
|
inst.status.end_editing();
|
||||||
|
|
||||||
|
match res {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let time = fmt_duration(start.elapsed());
|
inst.status.log_info(format!("Completed vacuum."));
|
||||||
inst.status.log_info(format!("Completed vacuum in {}.", time));
|
|
||||||
},
|
},
|
||||||
Err(e) => inst.status.log_error(format!("Vacuum failed: {}.", e))
|
Err(e) => inst.status.log_error(format!("Vacuum failed: {}.", e))
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@ pub fn get_command() -> Command {
|
||||||
Command {
|
Command {
|
||||||
func: vacuum,
|
func: vacuum,
|
||||||
verify_args: None,
|
verify_args: None,
|
||||||
args: vec![],
|
args: Vec::new(),
|
||||||
help: "Rebuild map database to reduce its size"
|
help: "Rebuild map database to reduce its size."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ pub enum InstState {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct InstStatus {
|
pub struct InstStatus {
|
||||||
|
pub show_progress: bool,
|
||||||
pub blocks_total: usize,
|
pub blocks_total: usize,
|
||||||
pub blocks_done: usize,
|
pub blocks_done: usize,
|
||||||
pub state: InstState
|
pub state: InstState
|
||||||
|
@ -66,6 +67,7 @@ pub struct InstStatus {
|
||||||
impl InstStatus {
|
impl InstStatus {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
show_progress: true,
|
||||||
blocks_total: 0,
|
blocks_total: 0,
|
||||||
blocks_done: 0,
|
blocks_done: 0,
|
||||||
state: InstState::Ignore
|
state: InstState::Ignore
|
||||||
|
@ -115,6 +117,10 @@ impl StatusServer {
|
||||||
self.status.lock().unwrap().blocks_done += 1;
|
self.status.lock().unwrap().blocks_done += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_show_progress(&self, sp: bool) {
|
||||||
|
self.status.lock().unwrap().show_progress = sp;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn begin_editing(&self) {
|
pub fn begin_editing(&self) {
|
||||||
self.set_state(InstState::Editing);
|
self.set_state(InstState::Editing);
|
||||||
}
|
}
|
||||||
|
|
52
src/utils.rs
52
src/utils.rs
|
@ -12,7 +12,7 @@ pub fn query_keys(
|
||||||
db: &mut MapDatabase,
|
db: &mut MapDatabase,
|
||||||
status: &StatusServer,
|
status: &StatusServer,
|
||||||
// TODO: Allow multiple names for setmetavar and replaceininv.
|
// TODO: Allow multiple names for setmetavar and replaceininv.
|
||||||
search_str: Option<&[u8]>,
|
search_strs: Vec<&Vec<u8>>,
|
||||||
area: Option<Area>,
|
area: Option<Area>,
|
||||||
invert: bool,
|
invert: bool,
|
||||||
include_partial: bool
|
include_partial: bool
|
||||||
|
@ -21,15 +21,15 @@ pub fn query_keys(
|
||||||
|
|
||||||
// Prepend 16-bit search string length to reduce false positives.
|
// Prepend 16-bit search string length to reduce false positives.
|
||||||
// This will break if the name-ID map format changes.
|
// This will break if the name-ID map format changes.
|
||||||
let search_bytes = search_str.map(|s| {
|
let string16s: Vec<Vec<u8>> = search_strs.iter().map(|&s| {
|
||||||
let mut res = Vec::new();
|
let mut res = Vec::new();
|
||||||
res.write_u16::<BigEndian>(s.len() as u16).unwrap();
|
res.write_u16::<BigEndian>(s.len() as u16).unwrap();
|
||||||
res.extend(s);
|
res.extend(s);
|
||||||
res
|
res
|
||||||
});
|
}).collect();
|
||||||
let data_searcher = search_bytes.as_ref().map(|b| {
|
let data_searchers: Vec<TwoWaySearcher> = string16s.iter().map(|b| {
|
||||||
TwoWaySearcher::new(b)
|
TwoWaySearcher::new(b)
|
||||||
});
|
}).collect();
|
||||||
let mut keys = Vec::new();
|
let mut keys = Vec::new();
|
||||||
|
|
||||||
// Area of included block positions.
|
// Area of included block positions.
|
||||||
|
@ -49,8 +49,9 @@ pub fn query_keys(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(s) = &data_searcher {
|
if !data_searchers.is_empty() {
|
||||||
if s.search_in(&data).is_none() {
|
// Data must match at least one search string.
|
||||||
|
if data_searchers.iter().any(|s| s.search_in(&data).is_some()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,13 +92,13 @@ pub fn fmt_duration(dur: Duration) -> String {
|
||||||
|
|
||||||
pub fn fmt_big_num(num: u64) -> String {
|
pub fn fmt_big_num(num: u64) -> String {
|
||||||
let f_num = num as f32;
|
let f_num = num as f32;
|
||||||
let abbrevs = vec![
|
const ABBREVS: [(&str, f32); 4] = [
|
||||||
("T".to_string(), 1_000_000_000_000.),
|
("T", 1_000_000_000_000.),
|
||||||
("B".to_string(), 1_000_000_000.),
|
("B", 1_000_000_000.),
|
||||||
("M".to_string(), 1_000_000.),
|
("M", 1_000_000.),
|
||||||
("k".to_string(), 1_000.)
|
("k", 1_000.)
|
||||||
];
|
];
|
||||||
for (suffix, unit) in abbrevs {
|
for &(suffix, unit) in &ABBREVS {
|
||||||
if f_num >= unit {
|
if f_num >= unit {
|
||||||
let mantissa = f_num / unit;
|
let mantissa = f_num / unit;
|
||||||
let place_vals =
|
let place_vals =
|
||||||
|
@ -109,3 +110,28 @@ pub fn fmt_big_num(num: u64) -> String {
|
||||||
}
|
}
|
||||||
format!("{}", f_num.round())
|
format!("{}", f_num.round())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nums() {
|
||||||
|
let pairs = [
|
||||||
|
(0, "0"),
|
||||||
|
(3, "3"),
|
||||||
|
(42, "42"),
|
||||||
|
(999, "999"),
|
||||||
|
(1_000, "1.00k"),
|
||||||
|
(33_870, "33.9k"),
|
||||||
|
(470_999, "471k"),
|
||||||
|
(555_678_000, "556M"),
|
||||||
|
(1_672_234_000, "1.67B"),
|
||||||
|
(77_864_672_234_000, "77.9T"),
|
||||||
|
];
|
||||||
|
for pair in &pairs {
|
||||||
|
assert_eq!(fmt_big_num(pair.0), pair.1.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue