From bdcd543b1e34948f5d8e942378ec1e227275fce0 Mon Sep 17 00:00:00 2001
From: jD91mZM2 <me@krake.one>
Date: Sat, 16 Jun 2018 07:20:34 +0200
Subject: [PATCH] Re-use hashmap

---
 src/lib/shell/flow_control.rs  |  6 ++---
 src/lib/shell/variables/mod.rs | 46 +++++++++++++++++++++++++---------
 2 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/src/lib/shell/flow_control.rs b/src/lib/shell/flow_control.rs
index 0c9d302b..c8179b1f 100644
--- a/src/lib/shell/flow_control.rs
+++ b/src/lib/shell/flow_control.rs
@@ -200,8 +200,7 @@ impl Function {
         let index = index.expect("execute called with invalid function");
 
         // Pop off all scopes since function temporarily
-        let temporary: Vec<_> = shell.variables.scopes.drain(index+1..).collect();
-        shell.variables.new_scope();
+        let temporary: Vec<_> = shell.variables.pop_scopes(index).collect();
 
         for (type_, value) in values {
             match value {
@@ -216,8 +215,7 @@ impl Function {
 
         shell.execute_statements(self.statements);
 
-        shell.variables.pop_scope();
-        shell.variables.scopes.extend(temporary);
+        shell.variables.append_scopes(temporary);
         Ok(())
     }
 
diff --git a/src/lib/shell/variables/mod.rs b/src/lib/shell/variables/mod.rs
index efcb1c1a..dd70a9f1 100644
--- a/src/lib/shell/variables/mod.rs
+++ b/src/lib/shell/variables/mod.rs
@@ -35,8 +35,9 @@ pub enum VariableType {
 
 #[derive(Clone, Debug)]
 pub struct Variables {
+    flags:   u8,
     pub scopes: Vec<FnvHashMap<Identifier, VariableType>>,
-    flags:      u8,
+    current: usize,
 }
 
 impl Default for Variables {
@@ -96,7 +97,8 @@ impl Default for Variables {
 
         Variables {
             flags: 0,
-            scopes: vec![map]
+            scopes: vec![map],
+            current: 0
         }
     }
 }
@@ -104,12 +106,32 @@ impl Default for Variables {
 const PLUGIN: u8 = 1;
 
 impl Variables {
-    pub(crate) fn new_scope(&mut self) {
-        self.scopes.push(FnvHashMap::with_capacity_and_hasher(64, Default::default()));
+    pub fn new_scope(&mut self) {
+        self.current += 1;
+        if self.current >= self.scopes.len() {
+            self.scopes.push(FnvHashMap::with_capacity_and_hasher(64, Default::default()));
+        }
+    }
+    pub fn pop_scope(&mut self) {
+        self.scopes.get_mut(self.current).unwrap().clear();
+        self.current -= 1;
+    }
+    pub fn pop_scopes<'a>(&'a mut self, index: usize) -> impl Iterator<Item = FnvHashMap<Identifier, VariableType>> + 'a {
+        self.current = index;
+        self.scopes.drain(index+1..)
     }
-    pub(crate) fn pop_scope(&mut self) {
-        // TODO use counter and re-use hashmap
-        self.scopes.pop();
+    pub fn append_scopes(&mut self, scopes: Vec<FnvHashMap<Identifier, VariableType>>) {
+        self.scopes.drain(self.current+1..);
+        self.current += scopes.len();
+        self.scopes.extend(scopes);
+    }
+    pub fn scopes(&self) -> impl Iterator<Item = &FnvHashMap<Identifier, VariableType>> {
+        let amount = self.scopes.len() - self.current - 1;
+        self.scopes.iter().rev().skip(amount)
+    }
+    pub fn scopes_mut(&mut self) -> impl Iterator<Item = &mut FnvHashMap<Identifier, VariableType>> {
+        let amount = self.scopes.len() - self.current - 1;
+        self.scopes.iter_mut().rev().skip(amount)
     }
 
     #[allow(dead_code)]
@@ -207,10 +229,10 @@ impl Variables {
     }
 
     pub fn shadow(&mut self, name: SmallString, value: VariableType) -> Option<VariableType> {
-        self.scopes.last_mut().unwrap().insert(name, value)
+        self.scopes.get_mut(self.current).unwrap().insert(name, value)
     }
     pub fn lookup_any(&self, name: &str) -> Option<&VariableType> {
-        for scope in self.scopes.iter().rev() {
+        for scope in self.scopes() {
             if let val @ Some(_) = scope.get(name) {
                 return val;
             }
@@ -218,7 +240,7 @@ impl Variables {
         None
     }
     pub fn lookup_any_mut(&mut self, name: &str) -> Option<&mut VariableType> {
-        for scope in self.scopes.iter_mut().rev() {
+        for scope in self.scopes_mut() {
             if let val @ Some(_) = scope.get_mut(name) {
                 return val;
             }
@@ -226,7 +248,7 @@ impl Variables {
         None
     }
     pub fn remove_any(&mut self, name: &str) -> Option<VariableType> {
-        for scope in self.scopes.iter_mut().rev() {
+        for scope in self.scopes_mut() {
             if let val @ Some(_) = scope.remove(name) {
                 return val;
             }
@@ -235,7 +257,7 @@ impl Variables {
     }
 
     pub fn variables(&self) -> impl Iterator<Item = (&SmallString, &Value)> {
-        self.scopes.iter().rev()
+        self.scopes()
             .map(|map| {
                 map.iter()
                     .filter_map(|(key, val)| if let VariableType::Variable(val) = val {
-- 
GitLab