From dda6ee95dff239a386cb7d6d8f052b21545e1875 Mon Sep 17 00:00:00 2001
From: Armin Ronacher <armin.ronacher@active-4.com>
Date: Fri, 22 Jun 2018 09:33:32 +0200
Subject: [PATCH] Changes the router to use atoms internally (#341)

---
 Cargo.toml    |  7 ++++---
 src/lib.rs    |  1 +
 src/param.rs  | 23 ++++++++++++-----------
 src/router.rs | 20 ++++++++------------
 4 files changed, 25 insertions(+), 26 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 787ade1e1..e60d07937 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -16,6 +16,9 @@ license = "MIT/Apache-2.0"
 exclude = [".gitignore", ".travis.yml", ".cargo/config", "appveyor.yml"]
 build = "build.rs"
 
+[package.metadata.docs.rs]
+features = ["tls", "alpn", "session", "brotli", "flate2-c"]
+
 [badges]
 travis-ci = { repository = "actix/actix-web", branch = "master" }
 appveyor = { repository = "fafhrd91/actix-web-hdy9d" }
@@ -46,9 +49,6 @@ flate2-c = ["flate2/miniz-sys"]
 # rust backend for flate2 crate
 flate2-rust = ["flate2/rust_backend"]
 
-[package.metadata.docs.rs]
-features = ["tls", "alpn", "session", "brotli", "flate2-c"]
-
 [dependencies]
 # actix = "0.6.1"
 actix = { git="https://github.com/actix/actix.git" }
@@ -103,6 +103,7 @@ tokio-tls = { version="0.1", optional = true }
 # openssl
 openssl = { version="0.10", optional = true }
 tokio-openssl = { version="0.2", optional = true }
+string_cache = "0.7.3"
 
 [dev-dependencies]
 env_logger = "0.5"
diff --git a/src/lib.rs b/src/lib.rs
index 7cb2b9086..5c7bfbaca 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -114,6 +114,7 @@ extern crate net2;
 extern crate parking_lot;
 extern crate rand;
 extern crate slab;
+extern crate string_cache;
 extern crate tokio;
 extern crate tokio_io;
 extern crate tokio_reactor;
diff --git a/src/param.rs b/src/param.rs
index 1329ff680..54325ee13 100644
--- a/src/param.rs
+++ b/src/param.rs
@@ -5,6 +5,7 @@ use std::str::FromStr;
 
 use http::StatusCode;
 use smallvec::SmallVec;
+use string_cache::DefaultAtom as Atom;
 
 use error::{InternalError, ResponseError, UriSegmentError};
 use uri::Url;
@@ -19,9 +20,9 @@ pub trait FromParam: Sized {
     fn from_param(s: &str) -> Result<Self, Self::Err>;
 }
 
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
 pub(crate) enum ParamItem {
-    Static(&'static str),
+    Static(Atom),
     UrlSegment(u16, u16),
 }
 
@@ -32,7 +33,7 @@ pub(crate) enum ParamItem {
 pub struct Params {
     url: Url,
     pub(crate) tail: u16,
-    segments: SmallVec<[(&'static str, ParamItem); 3]>,
+    segments: SmallVec<[(Atom, ParamItem); 3]>,
 }
 
 impl Params {
@@ -56,12 +57,12 @@ impl Params {
         self.tail = tail;
     }
 
-    pub(crate) fn add(&mut self, name: &'static str, value: ParamItem) {
+    pub(crate) fn add(&mut self, name: Atom, value: ParamItem) {
         self.segments.push((name, value));
     }
 
-    pub(crate) fn add_static(&mut self, name: &'static str, value: &'static str) {
-        self.segments.push((name, ParamItem::Static(value)));
+    pub(crate) fn add_static(&mut self, name: &str, value: &'static str) {
+        self.segments.push((Atom::from(name), ParamItem::Static(Atom::from(value))));
     }
 
     /// Check if there are any matched patterns
@@ -77,9 +78,9 @@ impl Params {
     /// Get matched parameter by name without type conversion
     pub fn get(&self, key: &str) -> Option<&str> {
         for item in self.segments.iter() {
-            if key == item.0 {
+            if key == &item.0 {
                 return match item.1 {
-                    ParamItem::Static(s) => Some(s),
+                    ParamItem::Static(ref s) => Some(&s),
                     ParamItem::UrlSegment(s, e) => {
                         Some(&self.url.path()[(s as usize)..(e as usize)])
                     }
@@ -138,13 +139,13 @@ impl<'a> Iterator for ParamsIter<'a> {
         if self.idx < self.params.len() {
             let idx = self.idx;
             let res = match self.params.segments[idx].1 {
-                ParamItem::Static(s) => s,
+                ParamItem::Static(ref s) => &s,
                 ParamItem::UrlSegment(s, e) => {
                     &self.params.url.path()[(s as usize)..(e as usize)]
                 }
             };
             self.idx += 1;
-            return Some((self.params.segments[idx].0, res));
+            return Some((&self.params.segments[idx].0, res));
         }
         None
     }
@@ -164,7 +165,7 @@ impl<'a> Index<usize> for &'a Params {
 
     fn index(&self, idx: usize) -> &str {
         match self.segments[idx].1 {
-            ParamItem::Static(s) => s,
+            ParamItem::Static(ref s) => &s,
             ParamItem::UrlSegment(s, e) => &self.url.path()[(s as usize)..(e as usize)],
         }
     }
diff --git a/src/router.rs b/src/router.rs
index 9aa173f72..03e299e15 100644
--- a/src/router.rs
+++ b/src/router.rs
@@ -1,6 +1,5 @@
 use std::collections::HashMap;
 use std::hash::{Hash, Hasher};
-use std::mem;
 use std::rc::Rc;
 
 use regex::{escape, Regex};
@@ -12,6 +11,8 @@ use param::ParamItem;
 use resource::ResourceHandler;
 use server::ServerSettings;
 
+use string_cache::DefaultAtom as Atom;
+
 /// Interface for application router.
 pub struct Router(Rc<Inner>);
 
@@ -144,7 +145,7 @@ enum PatternElement {
 enum PatternType {
     Static(String),
     Prefix(String),
-    Dynamic(Regex, Vec<&'static str>, usize),
+    Dynamic(Regex, Vec<Atom>, usize),
 }
 
 #[derive(Debug, Copy, Clone, PartialEq)]
@@ -221,12 +222,7 @@ impl Resource {
             let names = re
                 .capture_names()
                 .filter_map(|name| {
-                    name.map(|name| {
-                        let name = name.to_owned();
-                        let s: &'static str = unsafe { mem::transmute(name.as_str()) };
-                        mem::forget(name);
-                        s
-                    })
+                    name.map(|name| Atom::from(name))
                 })
                 .collect();
             PatternType::Dynamic(re, names, len)
@@ -316,8 +312,8 @@ impl Resource {
         let len = req.path().len();
         let params = req.match_info_mut();
         params.set_tail(len as u16);
-        for (idx, segment) in segments.iter().enumerate() {
-            params.add(names[idx], *segment);
+        for (idx, segment) in segments.into_iter().enumerate() {
+            params.add(names[idx].clone(), segment);
         }
         true
     }
@@ -382,8 +378,8 @@ impl Resource {
 
         let params = req.match_info_mut();
         params.set_tail(tail_len as u16);
-        for (idx, segment) in segments.iter().enumerate() {
-            params.add(names[idx], *segment);
+        for (idx, segment) in segments.into_iter().enumerate() {
+            params.add(names[idx].clone(), segment);
         }
         Some(tail_len)
     }