From 1d72fcac572f17039900905e232981f1ad8e07ef Mon Sep 17 00:00:00 2001 From: Jinzhou Zhang Date: Sat, 27 Jun 2020 22:13:42 +0800 Subject: [PATCH] remove the usage of ItemWrapper to reduce memory --- src/bin/main.rs | 5 +--- src/engine/all.rs | 6 ++--- src/engine/andor.rs | 8 +++--- src/engine/exact.rs | 7 +++-- src/engine/fuzzy.rs | 4 +-- src/engine/regexp.rs | 4 +-- src/header.rs | 2 +- src/item.rs | 63 ++++++-------------------------------------- src/lib.rs | 4 +-- src/model.rs | 25 +++++++++--------- src/previewer.rs | 38 ++++++++++++++------------ src/reader.rs | 20 +++++--------- src/selection.rs | 24 +++++++---------- src/util.rs | 4 +-- 14 files changed, 77 insertions(+), 137 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 9bdbeb97..7ac31406 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -10,7 +10,6 @@ use std::env; use std::fs::File; use std::io::{BufRead, BufReader, BufWriter, Write}; use std::os::unix::io::AsRawFd; -use std::sync::atomic::AtomicU32; use clap::{App, Arg, ArgMatches}; use nix::unistd::isatty; @@ -454,7 +453,6 @@ pub fn filter(options: &SkimOptions, source: Option) -> Result //------------------------------------------------------------------------------ // start - let item_index = AtomicU32::new(0); let components_to_stop = Arc::new(AtomicUsize::new(0)); let collector_option = CollectorOption::with_options(&options); @@ -467,8 +465,7 @@ pub fn filter(options: &SkimOptions, source: Option) -> Result let mut num_matched = 0; stream_of_item .into_iter() - .map(|item| ItemWrapper::new(item, (0, item_index.fetch_add(0, Ordering::SeqCst)))) - .filter_map(|wrapped| engine.match_item(Arc::new(wrapped))) + .filter_map(|item| engine.match_item(item)) .try_for_each(|matched| { num_matched += 1; if options.print_score { diff --git a/src/engine/all.rs b/src/engine/all.rs index 7cbbf5be..9e7ec884 100644 --- a/src/engine/all.rs +++ b/src/engine/all.rs @@ -1,8 +1,8 @@ use std::fmt::{Display, Error, Formatter}; use std::sync::Arc; -use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank}; -use crate::MatchEngine; +use crate::item::{MatchedItem, MatchedRange, Rank}; +use crate::{MatchEngine, SkimItem}; //------------------------------------------------------------------------------ #[derive(Debug)] @@ -19,7 +19,7 @@ impl MatchAllEngine { } impl MatchEngine for MatchAllEngine { - fn match_item(&self, item: Arc) -> Option { + fn match_item(&self, item: Arc) -> Option { let rank = Rank { score: 0, begin: 0, diff --git a/src/engine/andor.rs b/src/engine/andor.rs index dc8680a5..4bc0c08e 100644 --- a/src/engine/andor.rs +++ b/src/engine/andor.rs @@ -1,8 +1,8 @@ use std::fmt::{Display, Error, Formatter}; use std::sync::Arc; -use crate::item::{ItemWrapper, MatchedItem, MatchedRange}; -use crate::MatchEngine; +use crate::item::{MatchedItem, MatchedRange}; +use crate::{MatchEngine, SkimItem}; //------------------------------------------------------------------------------ // OrEngine, a combinator @@ -26,7 +26,7 @@ impl OrEngine { } impl MatchEngine for OrEngine { - fn match_item(&self, item: Arc) -> Option { + fn match_item(&self, item: Arc) -> Option { for engine in &self.engines { let result = engine.match_item(Arc::clone(&item)); if result.is_some() { @@ -98,7 +98,7 @@ impl AndEngine { } impl MatchEngine for AndEngine { - fn match_item(&self, item: Arc) -> Option { + fn match_item(&self, item: Arc) -> Option { // mock let mut results = vec![]; for engine in &self.engines { diff --git a/src/engine/exact.rs b/src/engine/exact.rs index 9f92ce54..e4573ef3 100644 --- a/src/engine/exact.rs +++ b/src/engine/exact.rs @@ -1,7 +1,6 @@ use crate::engine::util::{contains_upper, regex_match}; -use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank}; -use crate::SkimItem; -use crate::{CaseMatching, MatchEngine}; +use crate::item::{MatchedItem, MatchedRange, Rank}; +use crate::{CaseMatching, MatchEngine, SkimItem}; use regex::{escape, Regex}; use std::fmt::{Display, Error, Formatter}; use std::sync::Arc; @@ -66,7 +65,7 @@ impl ExactEngine { } impl MatchEngine for ExactEngine { - fn match_item(&self, item: Arc) -> Option { + fn match_item(&self, item: Arc) -> Option { let mut matched_result = None; for &(start, end) in item.get_matching_ranges().as_ref() { if self.query_regex.is_none() { diff --git a/src/engine/fuzzy.rs b/src/engine/fuzzy.rs index e410d219..5949a62a 100644 --- a/src/engine/fuzzy.rs +++ b/src/engine/fuzzy.rs @@ -5,7 +5,7 @@ use fuzzy_matcher::clangd::ClangdMatcher; use fuzzy_matcher::skim::{SkimMatcher, SkimMatcherV2}; use fuzzy_matcher::FuzzyMatcher; -use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank}; +use crate::item::{MatchedItem, MatchedRange, Rank}; use crate::SkimItem; use crate::{CaseMatching, MatchEngine}; @@ -113,7 +113,7 @@ impl FuzzyEngine { } impl MatchEngine for FuzzyEngine { - fn match_item(&self, item: Arc) -> Option { + fn match_item(&self, item: Arc) -> Option { // iterate over all matching fields: let mut matched_result = None; for &(start, end) in item.get_matching_ranges().as_ref() { diff --git a/src/engine/regexp.rs b/src/engine/regexp.rs index ba17229a..1c84879a 100644 --- a/src/engine/regexp.rs +++ b/src/engine/regexp.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use regex::Regex; use crate::engine::util::regex_match; -use crate::item::{ItemWrapper, MatchedItem, MatchedRange, Rank}; +use crate::item::{MatchedItem, MatchedRange, Rank}; use crate::SkimItem; use crate::{CaseMatching, MatchEngine}; @@ -38,7 +38,7 @@ impl RegexEngine { } impl MatchEngine for RegexEngine { - fn match_item(&self, item: Arc) -> Option { + fn match_item(&self, item: Arc) -> Option { let mut matched_result = None; for &(start, end) in item.get_matching_ranges().as_ref() { if self.query_regex.is_none() { diff --git a/src/header.rs b/src/header.rs index 2c265023..251d4643 100644 --- a/src/header.rs +++ b/src/header.rs @@ -130,7 +130,7 @@ impl Draw for Header { .text_width(screen_width - 2) .hscroll_offset(self.hscroll_offset) .build(); - print_item(canvas, &mut printer, &item, self.theme.header()); + print_item(canvas, &mut printer, item, self.theme.header()); } Ok(()) diff --git a/src/item.rs b/src/item.rs index 68310702..10a1eeb5 100644 --- a/src/item.rs +++ b/src/item.rs @@ -12,7 +12,7 @@ use regex::Regex; use crate::ansi::{ANSIParser, AnsiString}; use crate::field::{parse_matching_fields, parse_transform_fields, FieldRange}; use crate::spinlock::{SpinLock, SpinLockGuard}; -use crate::{ItemPreview, SkimItem}; +use crate::SkimItem; //------------------------------------------------------------------------------ /// An item will store everything that one line input will need to be operated and displayed. @@ -123,53 +123,6 @@ impl SkimItem for DefaultSkimItem { //------------------------------------------------------------------------------ pub type ItemIndex = (u32, u32); -pub struct ItemWrapper { - inner: Arc, - // (num of run, number of index) - id: ItemIndex, -} - -impl ItemWrapper { - pub fn new(item: Arc, index: ItemIndex) -> Self { - Self { id: index, inner: item } - } - - pub fn get_id(&self) -> ItemIndex { - self.id - } - - pub fn get_index(&self) -> usize { - self.id.1 as usize - } - - pub fn get_inner(&self) -> Arc { - self.inner.clone() - } -} - -/// delegate to inner -impl SkimItem for ItemWrapper { - fn display(&self) -> Cow { - self.inner.display() - } - - fn text(&self) -> Cow { - self.inner.text() - } - - fn preview(&self) -> ItemPreview { - self.inner.preview() - } - - fn output(&self) -> Cow { - self.inner.output() - } - - fn get_matching_ranges(&self) -> Cow<[(usize, usize)]> { - self.inner.get_matching_ranges() - } -} - //------------------------------------------------------------------------------ #[derive(Debug, Copy, Clone, PartialEq, Default)] pub struct Rank { @@ -188,13 +141,13 @@ pub enum MatchedRange { #[derive(Clone)] pub struct MatchedItem { - pub item: Arc, + pub item: Arc, pub rank: Rank, pub matched_range: Option, // range of chars that matched the pattern } impl MatchedItem { - pub fn builder(item: Arc) -> Self { + pub fn builder(item: Arc) -> Self { MatchedItem { item, rank: Rank::default(), @@ -233,12 +186,12 @@ const ITEM_POOL_CAPACITY: usize = 1024; pub struct ItemPool { length: AtomicUsize, - pool: SpinLock>>, + pool: SpinLock>>, /// number of items that was `take`n taken: AtomicUsize, /// reverse first N lines as header - reserved_items: SpinLock>>, + reserved_items: SpinLock>>, lines_to_reserve: usize, } @@ -281,7 +234,7 @@ impl ItemPool { self.taken.store(0, Ordering::SeqCst); } - pub fn append(&self, mut items: Vec>) { + pub fn append(&self, mut items: Vec>) { let mut pool = self.pool.lock(); let mut header_items = self.reserved_items.lock(); @@ -296,13 +249,13 @@ impl ItemPool { self.length.store(pool.len(), Ordering::SeqCst); } - pub fn take(&self) -> ItemPoolGuard> { + pub fn take(&self) -> ItemPoolGuard> { let guard = self.pool.lock(); let taken = self.taken.swap(guard.len(), Ordering::SeqCst); ItemPoolGuard { guard, start: taken } } - pub fn reserved(&self) -> ItemPoolGuard> { + pub fn reserved(&self) -> ItemPoolGuard> { let guard = self.reserved_items.lock(); ItemPoolGuard { guard, start: 0 } } diff --git a/src/lib.rs b/src/lib.rs index 179f2262..22e26f2c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,7 @@ use tuikit::prelude::{Event as TermEvent, *}; pub use crate::ansi::AnsiString; pub use crate::engine::fuzzy::FuzzyAlgorithm; use crate::event::{EventReceiver, EventSender}; -pub use crate::item::{ItemWrapper, MatchedItem}; +pub use crate::item::MatchedItem; use crate::model::Model; pub use crate::options::SkimOptions; pub use crate::output::SkimOutput; @@ -174,7 +174,7 @@ impl Default for CaseMatching { } pub trait MatchEngine: Sync + Send + Display { - fn match_item(&self, item: Arc) -> Option; + fn match_item(&self, item: Arc) -> Option; } pub trait MatchEngineFactory { diff --git a/src/model.rs b/src/model.rs index 89c668a3..1b8b6c95 100644 --- a/src/model.rs +++ b/src/model.rs @@ -15,7 +15,7 @@ use crate::engine::factory::{AndOrEngineFactory, ExactOrFuzzyEngineFactory, Rege use crate::event::{Event, EventHandler, EventReceiver, EventSender}; use crate::header::Header; use crate::input::parse_action_arg; -use crate::item::{ItemPool, ItemWrapper}; +use crate::item::ItemPool; use crate::matcher::{Matcher, MatcherControl}; use crate::options::SkimOptions; use crate::output::SkimOutput; @@ -352,27 +352,27 @@ impl Model { } fn act_execute_silent(&mut self, cmd: &str) { - let item = self.selection.get_current_item(); - if depends_on_items(cmd) && item.is_none() { + let current_index = self.selection.get_current_item_idx(); + let current_item = self.selection.get_current_item(); + if depends_on_items(cmd) && current_item.is_none() { debug!("act_execute_silent: command refers to items and there is no item for now"); debug!("command to execute: [{}]", cmd); return; } - let current_selection = item + let current_selection = current_item .as_ref() .map(|item| item.output()) .unwrap_or_else(|| Cow::Borrowed("")); let query = self.query.get_fz_query(); let cmd_query = self.query.get_cmd_query(); - let selections = self.selection.get_selected_wrapped_items(); + let (indices, selections) = self.selection.get_selected_indices_and_items(); let tmp: Vec> = selections.iter().map(|item| item.text()).collect(); let selected_texts: Vec<&str> = tmp.iter().map(|cow| cow.as_ref()).collect(); - let indices: Vec = selections.iter().map(|x| x.get_index()).collect(); let context = InjectContext { - current_index: item.as_ref().map(|x| x.get_index()).unwrap_or(0), + current_index, delimiter: &self.delimiter, current_selection: ¤t_selection, selections: &selected_texts, @@ -395,10 +395,7 @@ impl Model { let item_index = (std::u32::MAX, self.next_idx_to_append); - let item: Arc = Arc::new(ItemWrapper::new( - Arc::new(query), - (std::u32::MAX, self.next_idx_to_append), - )); + let item: Arc = Arc::new(query); self.next_idx_to_append += 1; @@ -475,7 +472,7 @@ impl Model { accept_key, query: self.query.get_fz_query(), cmd: self.query.get_cmd_query(), - selected_items: self.selection.get_selected_items(), + selected_items: self.selection.get_selected_indices_and_items().1, }); } @@ -555,11 +552,13 @@ impl Model { // re-draw if !self.preview_hidden { + let item_index = self.selection.get_current_item_idx(); let item = self.selection.get_current_item(); if let Some(previewer) = self.previewer.as_mut() { let selections = &self.selection; - let get_selected_items = || selections.get_selected_wrapped_items(); + let get_selected_items = || selections.get_selected_indices_and_items(); previewer.on_item_change( + item_index, item, env.query.to_string(), env.cmd_query.to_string(), diff --git a/src/previewer.rs b/src/previewer.rs index 98cea7e7..2b3e14d5 100644 --- a/src/previewer.rs +++ b/src/previewer.rs @@ -1,12 +1,3 @@ -use crate::ansi::{ANSIParser, AnsiString}; -use crate::event::{Event, EventHandler, UpdateScreen}; -use crate::item::ItemWrapper; -use crate::spinlock::SpinLock; -use crate::util::{depends_on_items, inject_command, InjectContext}; -use crate::{ItemPreview, SkimItem}; -use derive_builder::Builder; -use nix::libc; -use regex::Regex; use std::borrow::Cow; use std::cmp::{max, min}; use std::env; @@ -16,8 +7,18 @@ use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; use std::thread; use std::thread::JoinHandle; + +use derive_builder::Builder; +use nix::libc; +use regex::Regex; use tuikit::prelude::{Event as TermEvent, *}; +use crate::ansi::{ANSIParser, AnsiString}; +use crate::event::{Event, EventHandler, UpdateScreen}; +use crate::spinlock::SpinLock; +use crate::util::{depends_on_items, inject_command, InjectContext}; +use crate::{ItemPreview, SkimItem}; + const TAB_STOP: usize = 8; const DELIMITER_STR: &str = r"[\t\n ]+"; @@ -31,7 +32,8 @@ pub struct Previewer { vscroll_offset: usize, wrap: bool, - prev_item: Option>, + prev_item: Option>, + prev_item_index: Option, prev_query: Option, prev_cmd_query: Option, prev_num_selected: usize, @@ -67,6 +69,7 @@ impl Previewer { wrap: false, prev_item: None, + prev_item_index: None, prev_query: None, prev_cmd_query: None, prev_num_selected: 0, @@ -89,21 +92,22 @@ impl Previewer { pub fn on_item_change( &mut self, - new_item: impl Into>>, + new_item_index: usize, + new_item: impl Into>>, new_query: impl Into>, new_cmd_query: impl Into>, num_selected: usize, - get_selected_items: impl Fn() -> Vec>, // lazy get + get_selected_items: impl Fn() -> (Vec, Vec>), // lazy get ) { let new_item = new_item.into(); let new_query = new_query.into(); let new_cmd_query = new_cmd_query.into(); - let item_changed = match (self.prev_item.as_ref(), new_item.as_ref()) { + let item_changed = match (self.prev_item_index.as_ref(), new_item.as_ref()) { (None, None) => false, (None, Some(_)) => true, (Some(_), None) => true, - (Some(prev), Some(cur)) => prev.get_id() != cur.get_id(), + (Some(&prev_index), Some(_)) => prev_index == new_item_index, }; let query_changed = match (self.prev_query.as_ref(), new_query.as_ref()) { @@ -127,6 +131,7 @@ impl Previewer { } self.prev_item = new_item.clone(); + self.prev_item_index = Some(new_item_index); self.prev_query = new_query; self.prev_cmd_query = new_cmd_query; self.prev_num_selected = num_selected; @@ -156,13 +161,12 @@ impl Previewer { let query = self.prev_query.as_ref().map(|s| &**s).unwrap_or(""); let cmd_query = self.prev_cmd_query.as_ref().map(|s| &**s).unwrap_or(""); - let selections = get_selected_items(); + let (indices, selections) = get_selected_items(); let tmp: Vec> = selections.iter().map(|item| item.text()).collect(); let selected_texts: Vec<&str> = tmp.iter().map(|cow| cow.as_ref()).collect(); - let indices: Vec = selections.iter().map(|x| x.get_index()).collect(); let context = InjectContext { - current_index: item.get_index(), + current_index: new_item_index, delimiter: &self.delimiter, current_selection: ¤t_selection, selections: &selected_texts, diff --git a/src/reader.rs b/src/reader.rs index c1cd0540..1ea19da0 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,12 +1,11 @@ -use crate::global::{current_run_num, mark_new_run}; +use crate::global::mark_new_run; ///! Reader is used for reading items from datasource (e.g. stdin or command output) ///! ///! After reading in a line, reader will save an item into the pool(items) -use crate::item::ItemWrapper; use crate::item_collector::{read_and_collect_from_command, CollectorInput, CollectorOption}; use crate::options::SkimOptions; use crate::spinlock::SpinLock; -use crate::SkimItemReceiver; +use crate::{SkimItem, SkimItemReceiver}; use crossbeam::channel::{bounded, select, Sender}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; @@ -18,7 +17,7 @@ pub struct ReaderControl { tx_interrupt: Sender, tx_interrupt_cmd: Option>, components_to_stop: Arc, - items: Arc>>>, + items: Arc>>>, } impl ReaderControl { @@ -33,7 +32,7 @@ impl ReaderControl { while self.components_to_stop.load(Ordering::SeqCst) != 0 {} } - pub fn take(&self) -> Vec> { + pub fn take(&self) -> Vec> { let mut items = self.items.lock(); let mut ret = Vec::with_capacity(items.len()); ret.append(&mut items); @@ -66,7 +65,6 @@ impl Reader { pub fn run(&mut self, cmd: &str) -> ReaderControl { mark_new_run(cmd); - let run_num = current_run_num(); let components_to_stop: Arc = Arc::new(AtomicUsize::new(0)); let items = Arc::new(SpinLock::new(Vec::new())); @@ -82,7 +80,7 @@ impl Reader { }); let components_to_stop_clone = components_to_stop.clone(); - let tx_interrupt = collect_item(components_to_stop_clone, rx_item, run_num, items_clone); + let tx_interrupt = collect_item(components_to_stop_clone, rx_item, items_clone); ReaderControl { tx_interrupt, @@ -96,8 +94,7 @@ impl Reader { fn collect_item( components_to_stop: Arc, rx_item: SkimItemReceiver, - run_num: u32, - items: Arc>>>, + items: Arc>>>, ) -> Sender { let (tx_interrupt, rx_interrupt) = bounded(CHANNEL_SIZE); @@ -108,15 +105,12 @@ fn collect_item( components_to_stop.fetch_add(1, Ordering::SeqCst); started_clone.store(true, Ordering::SeqCst); // notify parent that it is started - let mut index = 0; loop { select! { recv(rx_item) -> new_item => match new_item { Ok(item) => { - let item_wrapped = ItemWrapper::new(item, (run_num, index)); let mut vec = items.lock(); - vec.push(Arc::new(item_wrapped)); - index += 1; + vec.push(item); } Err(_) => break, }, diff --git a/src/selection.rs b/src/selection.rs index 284624a3..76f1b6ee 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -11,7 +11,7 @@ use tuikit::prelude::{Event as TermEvent, *}; use crate::event::{Event, EventHandler, UpdateScreen}; use crate::global::current_run_num; use crate::item::{parse_criteria, ItemIndex, RankCriteria}; -use crate::item::{ItemWrapper, MatchedItem, MatchedRange}; +use crate::item::{MatchedItem, MatchedRange}; use crate::orderedvec::CompareFunction; use crate::orderedvec::OrderedVec; use crate::spinlock::SpinLock; @@ -29,7 +29,7 @@ lazy_static! { pub struct Selection { // all items items: OrderedVec, - selected: BTreeMap>, + selected: BTreeMap>, // // |>------ items[items.len()-1] @@ -219,7 +219,7 @@ impl Selection { } } - pub fn act_select_item(&mut self, item_index: ItemIndex, item: Arc) { + pub fn act_select_item(&mut self, item_index: ItemIndex, item: Arc) { if !self.multi_selection { return; } @@ -250,10 +250,11 @@ impl Selection { self.hscroll_offset = hscroll_offset as usize; } - pub fn get_selected_wrapped_items(&self) -> Vec> { + pub fn get_selected_indices_and_items(&self) -> (Vec, Vec>) { // select the current one let select_cursor = !self.multi_selection || self.selected.is_empty(); - let mut selected: Vec> = self.selected.values().cloned().collect(); + let mut selected: Vec> = self.selected.values().cloned().collect(); + let mut item_indices: Vec = self.selected.keys().map(|(_run, idx)| *idx as usize).collect(); if select_cursor && !self.items.is_empty() { let cursor = self.item_cursor + self.line_cursor; @@ -262,18 +263,11 @@ impl Selection { .get(cursor) .unwrap_or_else(|| panic!("model:act_output: failed to get item {}", cursor)); let item = current_item.item.clone(); + item_indices.push(cursor); selected.push(item); } - selected.sort_by_key(|item| item.get_id()); - selected - } - - pub fn get_selected_items(&self) -> Vec> { - self.get_selected_wrapped_items() - .into_iter() - .map(|wrapped| wrapped.get_inner()) - .collect() + (item_indices, selected) } pub fn get_num_of_selected_exclude_current(&self) -> usize { @@ -292,7 +286,7 @@ impl Selection { self.multi_selection } - pub fn get_current_item(&self) -> Option> { + pub fn get_current_item(&self) -> Option> { let item_idx = self.get_current_item_idx(); self.items.get(item_idx).map(|item| item.item.clone()) } diff --git a/src/util.rs b/src/util.rs index d7c66023..48c90fbb 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,8 +7,8 @@ use tuikit::prelude::*; use unicode_width::UnicodeWidthChar; use crate::field::get_string_by_range; -use crate::item::ItemWrapper; use crate::SkimItem; +use std::sync::Arc; lazy_static! { static ref RE_ESCAPE: Regex = Regex::new(r"['\U{00}]").unwrap(); @@ -179,7 +179,7 @@ impl LinePrinter { } } -pub fn print_item(canvas: &mut dyn Canvas, printer: &mut LinePrinter, item: &ItemWrapper, default_attr: Attr) { +pub fn print_item(canvas: &mut dyn Canvas, printer: &mut LinePrinter, item: &Arc, default_attr: Attr) { for (ch, attr) in item.display().iter() { printer.print_char(canvas, ch, default_attr.extend(attr), false); }