Use cow for keys

This commit is contained in:
Austen Adler 2022-10-02 16:40:56 -04:00
parent e0be452bdb
commit 0642b909c3
3 changed files with 42 additions and 21 deletions

View File

@ -168,7 +168,8 @@ fn reduce_selections(
options.ignore_case, options.ignore_case,
); );
if key_set_operation_result.contains(&key) { // TODO: Do not allocate
if key_set_operation_result.contains(&key.into_owned()) {
Some(swd.desc) Some(swd.desc)
} else { } else {
None None
@ -268,6 +269,7 @@ fn to_ordered_counts(options: &Options, sels: Vec<Selection>) -> LinkedHashMap<S
for i in sels { for i in sels {
let key = crate::utils::get_key( let key = crate::utils::get_key(
// TODO: Do not allocate
&i, &i,
options.skip_whitespace, options.skip_whitespace,
options.regex.as_ref(), options.regex.as_ref(),
@ -279,7 +281,8 @@ fn to_ordered_counts(options: &Options, sels: Vec<Selection>) -> LinkedHashMap<S
continue; continue;
} }
let entry: &mut usize = ret.entry(key).or_insert(0); // TODO: Do not allocate
let entry: &mut usize = ret.entry(key.into_owned()).or_insert(0);
*entry = entry.saturating_add(1); *entry = entry.saturating_add(1);
} }
ret ret

View File

@ -1,56 +1,74 @@
use kakplugin::Selection; // use kakplugin::Selection;
use regex::Regex; use regex::Regex;
use std::{ use std::{
borrow::{Borrow, Cow},
collections::hash_map::DefaultHasher, collections::hash_map::DefaultHasher,
hash::{Hash, Hasher}, hash::{Hash, Hasher},
}; };
pub fn get_key( /// Gets a key out of a selection
// TODO: Use Cow ///
selection: &Selection, /// # Examples
skip_whitespace: bool, ///
/// ```
/// assert_eq!(get_key(" asdf\n", false, None, false), "asdf\n");
/// assert_eq!(get_key(" asdf\n", true, None, false), " asdf\n");
/// assert_eq!(get_key(" as1f\n", false, Some("\w+"), false), "as");
/// assert_eq!(get_key(" aS1F\n", false, Some("\w+"), true), "as1f");
/// ```
pub fn get_key<'sel>(
selection: &'sel str,
preserve_whitespace: bool,
regex: Option<&Regex>, regex: Option<&Regex>,
ignore_case: bool, ignore_case: bool,
) -> String { ) -> Cow<'sel, str> {
// Strip whitespace if requested // Strip whitespace if requested
let mut key = if skip_whitespace { let mut key = if preserve_whitespace {
selection.as_str() // TODO: Does this need to be swapped?
selection
} else { } else {
selection.trim() selection.trim()
}; };
// If they requested a regex match, set the key to the string slice of that match // If they requested a regex match, set the key to the string slice of that match
if let Some(regex_match) = (|| { if let Some(regex_match) = (|| {
let captures = regex.as_ref()?.captures(key)?; // let captures = regex.as_ref()?.captures(&key)?;
let captures = regex.as_ref()?.captures(key.borrow())?;
captures captures
.get(1) .get(1)
.or_else(|| captures.get(0)) .or_else(|| captures.get(0))
.map(|m| m.as_str()) .map(|m| m.as_str())
})() { })() {
key = regex_match; key = regex_match;
// Cow::Borrowed(regex_match)
} }
// Ignore case if requested // Ignore case if requested
// Lowercase at the end to not mangle regex
if ignore_case { if ignore_case {
key.to_lowercase() // Lowercase at the end to not mangle regex
// TODO: Do not allocate if it is already lowercased
// Need to_lowercase(&self) -> Cow<str>
if !key.as_bytes().iter().any(u8::is_ascii_uppercase) {
Cow::Borrowed(key)
} else { } else {
// TODO: Do not perform an allocation here Cow::Owned(key.to_ascii_lowercase())
key.to_string() }
} else {
Cow::Borrowed(key)
} }
} }
/// Get a key out of a selection based on options /// Get a key out of a selection based on options
pub fn get_hash( pub fn get_hash(
// TODO: Accept any Into<AsRef<Selection>> // TODO: Accept any Into<AsRef<Selection>>
selection: &Selection, selection: &str,
skip_whitespace: bool, preserve_whitespace: bool,
regex: Option<&Regex>, regex: Option<&Regex>,
ignore_case: bool, ignore_case: bool,
) -> u64 { ) -> u64 {
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
get_key(selection, skip_whitespace, regex, ignore_case).hash(&mut hasher); get_key(&selection, preserve_whitespace, regex, ignore_case).hash(&mut hasher);
hasher.finish() hasher.finish()
} }

View File

@ -22,7 +22,7 @@ pub fn xlookup(options: &Options) -> Result<String, KakError> {
set_selections(selections.iter().map(|key| { set_selections(selections.iter().map(|key| {
lookup_table lookup_table
.get(&get_hash(key, false, None, false)) .get(&get_hash(&key, false, None, false))
.map_or_else( .map_or_else(
|| { || {
eprintln!("Key '{key}' not found",); eprintln!("Key '{key}' not found",);
@ -48,7 +48,7 @@ pub fn xlookup(options: &Options) -> Result<String, KakError> {
fn build_lookuptable(mut selections: Vec<Selection>) -> Result<BTreeMap<u64, Selection>, KakError> { fn build_lookuptable(mut selections: Vec<Selection>) -> Result<BTreeMap<u64, Selection>, KakError> {
let mut iter = selections.array_chunks_mut(); let mut iter = selections.array_chunks_mut();
let ret = iter.try_fold(BTreeMap::new(), |mut acc, [key, value]| { let ret = iter.try_fold(BTreeMap::new(), |mut acc, [key, value]| {
match acc.entry(get_hash(key, false, None, false)) { match acc.entry(get_hash(&key, false, None, false)) {
Occupied(_) => Err(KakError::Custom(format!("Duplicate key '{key}'"))), Occupied(_) => Err(KakError::Custom(format!("Duplicate key '{key}'"))),
Vacant(v) => { Vacant(v) => {
v.insert(value.clone()); v.insert(value.clone());
@ -76,7 +76,7 @@ mod tests {
} }
macro_rules! hsh { macro_rules! hsh {
($expr:expr) => { ($expr:expr) => {
get_hash(&$expr.to_string(), false, None, false) get_hash($expr, false, None, false)
}; };
} }
#[test] #[test]