refactor: Move off of IndexMap/HashMap

This dropped 17KB

Again, performance shouldn't be too bad as the total number of argument
id's passed in by the user shouldn't be huge, with the upper end being
5-15 except for in extreme cases like rustc accepting arguments from
cargo via a file.
This commit is contained in:
Ed Page 2022-08-11 13:48:23 -05:00
parent d441ebbf62
commit 6e7fd6d4bc
8 changed files with 206 additions and 16 deletions

1
Cargo.lock generated
View file

@ -127,7 +127,6 @@ dependencies = [
"clap_derive",
"clap_lex",
"humantime",
"indexmap",
"once_cell",
"rustversion",
"shlex",

View file

@ -63,7 +63,7 @@ debug = ["clap_derive/debug", "dep:backtrace"] # Enables debug messages
unstable-doc = ["derive", "cargo", "wrap_help", "env", "unicode", "unstable-replace", "unstable-grouped"] # for docs.rs
# Used in default
std = ["indexmap/std"] # support for no_std in a backwards-compatible way
std = [] # support for no_std in a backwards-compatible way
color = ["dep:atty", "dep:termcolor"]
suggestions = ["dep:strsim"]
@ -89,7 +89,6 @@ clap_lex = { path = "./clap_lex", version = "0.2.2" }
bitflags = "1.2"
textwrap = { version = "0.15.0", default-features = false, features = [] }
unicase = { version = "2.6", optional = true }
indexmap = "1.0"
strsim = { version = "0.10", optional = true }
atty = { version = "0.2", optional = true }
termcolor = { version = "1.1.1", optional = true }

View file

@ -1,5 +1,4 @@
// Std
use std::collections::HashMap;
use std::env;
use std::ffi::OsString;
use std::fmt;
@ -20,6 +19,7 @@ use crate::output::fmt::Stream;
use crate::output::{fmt::Colorizer, Help, HelpWriter, Usage};
use crate::parser::{ArgMatcher, ArgMatches, Parser};
use crate::util::ChildGraph;
use crate::util::FlatMap;
use crate::util::{color::ColorChoice, Id, Key};
use crate::{Error, INTERNAL_ERROR_MSG};
@ -93,7 +93,7 @@ pub struct Command<'help> {
g_settings: AppFlags,
args: MKeyMap<'help>,
subcommands: Vec<Command<'help>>,
replacers: HashMap<&'help str, &'help [&'help str]>,
replacers: FlatMap<&'help str, &'help [&'help str]>,
groups: Vec<ArgGroup<'help>>,
current_help_heading: Option<&'help str>,
current_disp_ord: Option<usize>,

View file

@ -1,5 +1,4 @@
// Std
use std::collections::HashMap;
use std::ffi::OsString;
use std::mem;
use std::ops::Deref;
@ -10,6 +9,7 @@ use crate::parser::AnyValue;
use crate::parser::Identifier;
use crate::parser::PendingArg;
use crate::parser::{ArgMatches, MatchedArg, SubCommand, ValueSource};
use crate::util::FlatMap;
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;
@ -46,14 +46,14 @@ impl ArgMatcher {
"ArgMatcher::get_global_values: global_arg_vec={:?}",
global_arg_vec
);
let mut vals_map = HashMap::new();
let mut vals_map = FlatMap::new();
self.fill_in_global_values(global_arg_vec, &mut vals_map);
}
fn fill_in_global_values(
&mut self,
global_arg_vec: &[Id],
vals_map: &mut HashMap<Id, MatchedArg>,
vals_map: &mut FlatMap<Id, MatchedArg>,
) {
for global_arg in global_arg_vec {
if let Some(ma) = self.get(global_arg) {
@ -99,18 +99,18 @@ impl ArgMatcher {
}
pub(crate) fn remove(&mut self, arg: &Id) {
self.matches.args.swap_remove(arg);
self.matches.args.remove(arg);
}
pub(crate) fn contains(&self, arg: &Id) -> bool {
self.matches.args.contains_key(arg)
}
pub(crate) fn arg_ids(&self) -> indexmap::map::Keys<Id, MatchedArg> {
pub(crate) fn arg_ids(&self) -> std::slice::Iter<'_, Id> {
self.matches.args.keys()
}
pub(crate) fn entry(&mut self, arg: &Id) -> indexmap::map::Entry<Id, MatchedArg> {
pub(crate) fn entry(&mut self, arg: &Id) -> crate::util::Entry<Id, MatchedArg> {
self.matches.args.entry(arg.clone())
}

View file

@ -5,15 +5,13 @@ use std::fmt::Debug;
use std::iter::{Cloned, Flatten, Map};
use std::slice::Iter;
// Third Party
use indexmap::IndexMap;
// Internal
use crate::parser::AnyValue;
use crate::parser::AnyValueId;
use crate::parser::MatchedArg;
use crate::parser::MatchesError;
use crate::parser::ValueSource;
use crate::util::FlatMap;
use crate::util::{Id, Key};
use crate::INTERNAL_ERROR_MSG;
@ -68,7 +66,7 @@ pub struct ArgMatches {
pub(crate) valid_args: Vec<Id>,
#[cfg(debug_assertions)]
pub(crate) valid_subcommands: Vec<Id>,
pub(crate) args: IndexMap<Id, MatchedArg>,
pub(crate) args: FlatMap<Id, MatchedArg>,
pub(crate) subcommand: Option<Box<SubCommand>>,
}

View file

@ -5,6 +5,7 @@ use crate::output::fmt::Stream;
use crate::output::Usage;
use crate::parser::{ArgMatcher, ParseState};
use crate::util::ChildGraph;
use crate::util::FlatMap;
use crate::util::FlatSet;
use crate::util::Id;
use crate::INTERNAL_ERROR_MSG;
@ -383,7 +384,7 @@ impl<'help, 'cmd> Validator<'help, 'cmd> {
#[derive(Default, Clone, Debug)]
struct Conflicts {
potential: std::collections::HashMap<Id, Vec<Id>>,
potential: FlatMap<Id, Vec<Id>>,
}
impl Conflicts {

190
src/util/flat_map.rs Normal file
View file

@ -0,0 +1,190 @@
use std::borrow::Borrow;
/// Flat (Vec) backed map
///
/// This preserves insertion order
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct FlatMap<K, V> {
keys: Vec<K>,
values: Vec<V>,
}
impl<K: PartialEq + Eq, V> FlatMap<K, V> {
pub(crate) fn new() -> Self {
Default::default()
}
pub(crate) fn insert(&mut self, key: K, mut value: V) -> Option<V> {
for (index, existing) in self.keys.iter().enumerate() {
if *existing == key {
std::mem::swap(&mut self.values[index], &mut value);
return Some(value);
}
}
self.keys.push(key);
self.values.push(value);
None
}
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
where
K: Borrow<Q>,
Q: std::hash::Hash + Eq,
{
for existing in &self.keys {
if existing.borrow() == key {
return true;
}
}
false
}
pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: std::hash::Hash + Eq,
{
let index = self
.keys
.iter()
.enumerate()
.find_map(|(i, k)| (k.borrow() == key).then(|| i))?;
self.keys.remove(index);
Some(self.values.remove(index))
}
pub(crate) fn is_empty(&self) -> bool {
self.keys.is_empty()
}
pub fn entry(&mut self, key: K) -> Entry<K, V> {
for (index, existing) in self.keys.iter().enumerate() {
if *existing == key {
return Entry::Occupied(OccupiedEntry { v: self, index });
}
}
Entry::Vacant(VacantEntry { v: self, key })
}
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: std::hash::Hash + Eq,
{
for (index, existing) in self.keys.iter().enumerate() {
if existing.borrow() == k {
return Some(&self.values[index]);
}
}
None
}
pub fn get_mut<Q: ?Sized>(&mut self, k: &Q) -> Option<&mut V>
where
K: Borrow<Q>,
Q: std::hash::Hash + Eq,
{
for (index, existing) in self.keys.iter().enumerate() {
if existing.borrow() == k {
return Some(&mut self.values[index]);
}
}
None
}
pub fn keys(&self) -> std::slice::Iter<'_, K> {
self.keys.iter()
}
pub fn iter_mut(&mut self) -> IterMut<K, V> {
IterMut {
keys: self.keys.iter_mut(),
values: self.values.iter_mut(),
}
}
}
impl<K: PartialEq + Eq, V> Default for FlatMap<K, V> {
fn default() -> Self {
Self {
keys: Default::default(),
values: Default::default(),
}
}
}
pub enum Entry<'a, K: 'a, V: 'a> {
Vacant(VacantEntry<'a, K, V>),
Occupied(OccupiedEntry<'a, K, V>),
}
impl<'a, K: 'a, V: 'a> Entry<'a, K, V> {
pub fn or_insert(self, default: V) -> &'a mut V {
match self {
Entry::Occupied(entry) => &mut entry.v.values[entry.index],
Entry::Vacant(entry) => {
entry.v.keys.push(entry.key);
entry.v.values.push(default);
entry.v.values.last_mut().unwrap()
}
}
}
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
match self {
Entry::Occupied(entry) => &mut entry.v.values[entry.index],
Entry::Vacant(entry) => {
entry.v.keys.push(entry.key);
entry.v.values.push(default());
entry.v.values.last_mut().unwrap()
}
}
}
}
pub struct VacantEntry<'a, K: 'a, V: 'a> {
v: &'a mut FlatMap<K, V>,
key: K,
}
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
v: &'a mut FlatMap<K, V>,
index: usize,
}
pub struct IterMut<'a, K: 'a, V: 'a> {
keys: std::slice::IterMut<'a, K>,
values: std::slice::IterMut<'a, V>,
}
impl<'a, K, V> Iterator for IterMut<'a, K, V> {
type Item = (&'a K, &'a mut V);
fn next(&mut self) -> Option<(&'a K, &'a mut V)> {
match self.keys.next() {
Some(k) => {
let v = self.values.next().unwrap();
Some((k, v))
}
None => None,
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.keys.size_hint()
}
}
impl<'a, K, V> DoubleEndedIterator for IterMut<'a, K, V> {
fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> {
match self.keys.next_back() {
Some(k) => {
let v = self.values.next_back().unwrap();
Some((k, v))
}
None => None,
}
}
}
impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> {}

View file

@ -1,5 +1,6 @@
#![allow(clippy::single_component_path_imports)]
mod flat_map;
mod flat_set;
mod fnv;
mod graph;
@ -8,6 +9,8 @@ mod str_to_bool;
pub use self::fnv::Key;
pub(crate) use self::flat_map::Entry;
pub(crate) use self::flat_map::FlatMap;
pub(crate) use self::flat_set::FlatSet;
pub(crate) use self::str_to_bool::str_to_bool;
pub(crate) use self::str_to_bool::FALSE_LITERALS;