From f9e2c68cd0256b7adc27e9804c9befc528c69f1e Mon Sep 17 00:00:00 2001
From: Michael Aaron Murphy <mmstickman@gmail.com>
Date: Fri, 14 Apr 2017 11:14:52 -0400
Subject: [PATCH] Use FNV-based Hash Maps

---
 Cargo.toml                     |  6 ++++++
 src/builtins/functions.rs      |  6 +++---
 src/builtins/mod.rs            |  8 ++++----
 src/builtins/variables.rs      |  4 ++--
 src/main.rs                    |  6 ++++++
 src/parser/mod.rs              |  2 +-
 src/parser/shell_expand/mod.rs |  4 ++--
 src/shell/assignments.rs       |  6 +++---
 src/shell/mod.rs               | 14 ++++++++------
 src/variables.rs               | 12 ++++++------
 10 files changed, 41 insertions(+), 27 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 71033d8a..008532a9 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -18,6 +18,7 @@ build = "build.rs"
 name = "ion"
 
 [dependencies]
+fnv = "1.0"
 glob = "0.2"
 liner = "0.1.4"
 peg-syntax-ext = "0.4"
@@ -30,3 +31,8 @@ users = "0.5.1"
 [build-dependencies]
 ansi_term = "0.9"
 version_check = "0.1"
+
+[profile.release]
+# debug = true
+# rustflags = [ "-C", "target-cpu=native"]
+# lto = true
diff --git a/src/builtins/functions.rs b/src/builtins/functions.rs
index 0ec64634..69d53e51 100644
--- a/src/builtins/functions.rs
+++ b/src/builtins/functions.rs
@@ -1,9 +1,9 @@
 use flow_control::{Function};
-use std::collections::HashMap;
+use fnv::FnvHashMap;
 use status::*;
 use std::io::{self, Write};
 
-fn print_functions(functions: &HashMap<String, Function>) {
+fn print_functions(functions: &FnvHashMap<String, Function>) {
     let stdout = io::stdout();
     let stdout = &mut stdout.lock();
 
@@ -13,7 +13,7 @@ fn print_functions(functions: &HashMap<String, Function>) {
     }
 }
 
-pub fn fn_(functions: &mut HashMap<String, Function>) -> i32
+pub fn fn_(functions: &mut FnvHashMap<String, Function>) -> i32
 {
     print_functions(functions);
     SUCCESS
diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs
index e6459b14..be1cd9a6 100644
--- a/src/builtins/mod.rs
+++ b/src/builtins/mod.rs
@@ -6,7 +6,7 @@ use self::variables::{alias, drop_alias, drop_variable, export_variable};
 use self::functions::fn_;
 use self::source::source;
 
-use std::collections::HashMap;
+use fnv::FnvHashMap;
 use std::io::{self, Write};
 use std::process;
 
@@ -35,8 +35,8 @@ pub struct Builtin {
 
 impl Builtin {
     /// Return the map from command names to commands
-    pub fn map() -> HashMap<&'static str, Self> {
-        let mut commands: HashMap<&str, Self> = HashMap::new();
+    pub fn map() -> FnvHashMap<&'static str, Self> {
+        let mut commands: FnvHashMap<&str, Self> = FnvHashMap::with_capacity_and_hasher(32, Default::default());
 
         /* Directories */
         commands.insert("cd",
@@ -230,7 +230,7 @@ impl Builtin {
                             },
                         });
 
-        let command_helper: HashMap<&'static str, &'static str> = commands.iter()
+        let command_helper: FnvHashMap<&'static str, &'static str> = commands.iter()
                                                                           .map(|(k, v)| {
                                                                               (*k, v.help)
                                                                           })
diff --git a/src/builtins/variables.rs b/src/builtins/variables.rs
index 78060902..b6246a18 100644
--- a/src/builtins/variables.rs
+++ b/src/builtins/variables.rs
@@ -1,13 +1,13 @@
 // TODO: Move into grammar
 
-use std::collections::HashMap;
+use fnv::FnvHashMap;
 use std::env;
 use std::io::{self, Write};
 
 use status::*;
 use variables::Variables;
 
-fn print_list(list: &HashMap<String, String>) {
+fn print_list(list: &FnvHashMap<String, String>) {
     let stdout = io::stdout();
     let stdout = &mut stdout.lock();
 
diff --git a/src/main.rs b/src/main.rs
index f745a3db..869a60f0 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,12 @@
 #![feature(box_syntax)]
 #![feature(plugin)]
 #![plugin(peg_syntax_ext)]
+
+// For a performance boost on Linux
+// #![feature(alloc_system)]
+// extern crate alloc_system;
+
+extern crate fnv;
 extern crate glob;
 extern crate liner;
 
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 00c3ffa2..3b011de4 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -39,7 +39,7 @@ pub mod shell_expand;
 mod statements;
 mod quotes;
 
-pub use self::shell_expand::{Index, IndexEnd, ExpanderFunctions, expand_string};
+pub use self::shell_expand::{Index, IndexEnd, ExpanderFunctions, expand_string, expand_tokens, WordToken, WordIterator};
 pub use self::arguments::ArgumentSplitter;
 pub use self::loops::for_grammar::ForExpression;
 pub use self::statements::{StatementSplitter, StatementError, check_statement};
diff --git a/src/parser/shell_expand/mod.rs b/src/parser/shell_expand/mod.rs
index 6f1f74de..12407c65 100644
--- a/src/parser/shell_expand/mod.rs
+++ b/src/parser/shell_expand/mod.rs
@@ -10,7 +10,7 @@ mod words;
 
 use self::braces::BraceToken;
 use self::ranges::parse_range;
-use self::words::{WordIterator, WordToken};
+pub use self::words::{WordIterator, WordToken};
 
 pub use self::words::{Index, IndexEnd};
 
@@ -130,7 +130,7 @@ pub fn expand_string(original: &str, expand_func: &ExpanderFunctions, reverse_qu
 }
 
 #[allow(cyclomatic_complexity)]
-fn expand_tokens(mut token_buffer: Vec<WordToken>, expand_func: &ExpanderFunctions,
+pub fn expand_tokens(mut token_buffer: Vec<WordToken>, expand_func: &ExpanderFunctions,
     reverse_quoting: bool, contains_brace: bool) -> Vec<String>
 {
     let mut output = String::new();
diff --git a/src/shell/assignments.rs b/src/shell/assignments.rs
index f9cfb3af..b3313d3c 100644
--- a/src/shell/assignments.rs
+++ b/src/shell/assignments.rs
@@ -1,4 +1,4 @@
-use std::collections::HashMap;
+use fnv::FnvHashMap;
 use std::io::{self, Write};
 
 use variables::Variables;
@@ -7,7 +7,7 @@ use parser::assignments::{self, Binding, Operator, Value};
 use parser::{ExpanderFunctions, Index, IndexEnd};
 use status::*;
 
-fn print_vars(list: &HashMap<String, String>) {
+fn print_vars(list: &FnvHashMap<String, String>) {
     let stdout = io::stdout();
     let stdout = &mut stdout.lock();
 
@@ -20,7 +20,7 @@ fn print_vars(list: &HashMap<String, String>) {
     }
 }
 
-fn print_arrays(list: &HashMap<String, Vec<String>>) {
+fn print_arrays(list: &FnvHashMap<String, Vec<String>>) {
     let stdout = io::stdout();
     let stdout = &mut stdout.lock();
 
diff --git a/src/shell/mod.rs b/src/shell/mod.rs
index 45b132cd..0a93de62 100644
--- a/src/shell/mod.rs
+++ b/src/shell/mod.rs
@@ -7,7 +7,7 @@ pub use self::history::ShellHistory;
 pub use self::job::{Job, JobKind};
 pub use self::flow::FlowLogic;
 
-use std::collections::HashMap;
+use fnv::FnvHashMap;
 use std::fs::File;
 use std::io::{self, Read, Write};
 use std::env;
@@ -30,25 +30,25 @@ use parser::peg::Pipeline;
 /// This struct will contain all of the data structures related to this
 /// instance of the shell.
 pub struct Shell<'a> {
-    pub builtins: &'a HashMap<&'static str, Builtin>,
+    pub builtins: &'a FnvHashMap<&'static str, Builtin>,
     pub context: Context,
     pub variables: Variables,
     flow_control: FlowControl,
     pub directory_stack: DirectoryStack,
-    pub functions: HashMap<String, Function>,
+    pub functions: FnvHashMap<String, Function>,
     pub previous_status: i32,
 }
 
 impl<'a> Shell<'a> {
     /// Panics if DirectoryStack construction fails
-    pub fn new(builtins: &'a HashMap<&'static str, Builtin>) -> Shell<'a> {
+    pub fn new(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> {
         Shell {
             builtins: builtins,
             context: Context::new(),
             variables: Variables::default(),
             flow_control: FlowControl::default(),
             directory_stack: DirectoryStack::new().expect(""),
-            functions: HashMap::default(),
+            functions: FnvHashMap::default(),
             previous_status: 0,
         }
     }
@@ -336,7 +336,9 @@ impl<'a> Shell<'a> {
             // Branch else if -> input == shell function and set the exit_status
             } else if let Some(function) = self.functions.get(pipeline.jobs[0].command.as_str()).cloned() {
                 if pipeline.jobs[0].args.len() - 1 == function.args.len() {
-                    let mut variables_backup: HashMap<&str, Option<String>> = HashMap::new();
+                    let mut variables_backup: FnvHashMap<&str, Option<String>> = FnvHashMap::with_capacity_and_hasher (
+                        64, Default::default()
+                    );
                     for (name, value) in function.args.iter().zip(pipeline.jobs[0].args.iter().skip(1)) {
                         variables_backup.insert(name, self.variables.get_var(name));
                         self.variables.set_var(name, value);
diff --git a/src/variables.rs b/src/variables.rs
index 4453686e..8a7e0995 100644
--- a/src/variables.rs
+++ b/src/variables.rs
@@ -1,5 +1,5 @@
 // TODO: Move into shell module
-use std::collections::HashMap;
+use fnv::FnvHashMap;
 use std::env;
 use std::path::PathBuf;
 use std::process;
@@ -9,14 +9,14 @@ use liner::Context;
 use status::{SUCCESS, FAILURE};
 
 pub struct Variables {
-    pub arrays:    HashMap<String, Vec<String>>,
-    pub variables: HashMap<String, String>,
-    pub aliases:   HashMap<String, String>
+    pub arrays:    FnvHashMap<String, Vec<String>>,
+    pub variables: FnvHashMap<String, String>,
+    pub aliases:   FnvHashMap<String, String>
 }
 
 impl Default for Variables {
     fn default() -> Variables {
-        let mut map = HashMap::new();
+        let mut map = FnvHashMap::with_capacity_and_hasher(64, Default::default());
         map.insert("DIRECTORY_STACK_SIZE".to_string(), "1000".to_string());
         map.insert("HISTORY_SIZE".into(), "1000".into());
         map.insert("HISTORY_FILE_ENABLED".into(), "0".into());
@@ -36,7 +36,7 @@ impl Default for Variables {
 
         // Initialize the HOME variable
         env::home_dir().map_or_else(|| env::set_var("HOME", "?"), |path| env::set_var("HOME", path.to_str().unwrap_or("?")));
-        Variables { arrays: HashMap::new(), variables: map, aliases: HashMap::new() }
+        Variables { arrays: FnvHashMap::with_capacity_and_hasher(64, Default::default()), variables: map, aliases: FnvHashMap::with_capacity_and_hasher(64, Default::default()) }
     }
 }
 
-- 
GitLab