Repository: cobalt-org/liquid-rust
Pull request #374 (2019-12-22 01:45:35)

Measure and optimize the now renamed KString crate
Hoping to see some good gains in macro benchmarks.  I expect for most keys, we'll be able to avoid the heap which saves a good amount.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 69ab8bb..cbdf2d6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,7 @@ members = [
   "crates/derive",
   "crates/help-md",
   "crates/bin",
-  "crates/sstring",
+  "crates/kstring",
 ]
 
 [package]
@@ -40,7 +40,7 @@ unicode-segmentation = "1.2"
 itertools = "0.8.0"
 url = "1.5"
 doc-comment = "0.3"
-sstring = { version = "0.1.0", path = "crates/sstring" }
+kstring = { version = "0.1.0", path = "crates/kstring" }
 liquid-error = { version = "^0.19", path = "crates/error" }
 liquid-value = { version = "^0.19", path = "crates/value" }
 liquid-compiler = { version = "^0.19", path = "crates/compiler" }
diff --git a/crates/derive/src/filter_parameters.rs b/crates/derive/src/filter_parameters.rs
index 2649066..28f9054 100644
--- a/crates/derive/src/filter_parameters.rs
+++ b/crates/derive/src/filter_parameters.rs
@@ -523,7 +523,7 @@ fn generate_evaluate_field(field: &FilterParameter) -> TokenStream {
             )?
         },
         FilterParameterType::Str => quote! {
-            .to_sstr()
+            .to_kstr()
         },
     };
 
@@ -671,7 +671,7 @@ fn generate_evaluated_struct(filter_parameters: &FilterParameters) -> TokenStrea
             FilterParameterType::Bool => quote! { bool },
             FilterParameterType::DateTime => quote! { ::liquid::value::DateTime },
             FilterParameterType::Date => quote! { ::liquid::value::Date },
-            FilterParameterType::Str => quote! { ::liquid::value::sstring::SStringCow<'a> },
+            FilterParameterType::Str => quote! { ::liquid::value::kstring::KStringCow<'a> },
         };
 
         if field.is_optional() {
diff --git a/crates/derive/src/lib.rs b/crates/derive/src/lib.rs
index e438394..ce23e2a 100644
--- a/crates/derive/src/lib.rs
+++ b/crates/derive/src/lib.rs
@@ -64,7 +64,7 @@ use proc_macro::TokenStream;
 ///     - "date" -> only `Scalar(Date)` is accepted, `evaluate` will unwrap `Value`
 /// into `Date`.
 ///     - "str" -> only `Scalar(Str)` is accepted, `evaluate` will unwrap `Value`
-/// into `SStringCow`.
+/// into `KStringCow`.
 ///
 /// # Examples
 ///
diff --git a/crates/error/Cargo.toml b/crates/error/Cargo.toml
index ed7aa57..140c3ca 100644
--- a/crates/error/Cargo.toml
+++ b/crates/error/Cargo.toml
@@ -16,4 +16,4 @@ azure-devops = { project = "cobalt-org", pipeline = "liquid-rust" }
 
 [dependencies]
 # Exposed in API
-sstring = { version = "0.1.0", path = "../sstring" }
+kstring = { version = "0.1.0", path = "../kstring" }
diff --git a/crates/error/src/error.rs b/crates/error/src/error.rs
index f266111..71e074e 100644
--- a/crates/error/src/error.rs
+++ b/crates/error/src/error.rs
@@ -22,18 +22,18 @@ pub struct Error {
 // shouldn't hurt.
 #[derive(Debug, Clone)]
 struct InnerError {
-    msg: sstring::SString,
+    msg: kstring::KString,
     user_backtrace: Vec<Trace>,
     cause: Option<BoxedError>,
 }
 
 impl Error {
     /// Create a new compiler error with the given message
-    pub fn with_msg<S: Into<sstring::SString>>(msg: S) -> Self {
+    pub fn with_msg<S: Into<kstring::KString>>(msg: S) -> Self {
         Self::with_msg_cow(msg.into())
     }
 
-    fn with_msg_cow(msg: sstring::SString) -> Self {
+    fn with_msg_cow(msg: kstring::KString) -> Self {
         let error = InnerError {
             msg,
             user_backtrace: vec![Trace::empty()],
@@ -47,12 +47,12 @@ impl Error {
     /// Add a new call to the user-visible backtrace
     pub fn trace<T>(self, trace: T) -> Self
     where
-        T: Into<sstring::SString>,
+        T: Into<kstring::KString>,
     {
         self.trace_trace(trace.into())
     }
 
-    fn trace_trace(mut self, trace: sstring::SString) -> Self {
+    fn trace_trace(mut self, trace: kstring::KString) -> Self {
         let trace = Trace::new(trace);
         self.inner.user_backtrace.push(trace);
         self
@@ -63,13 +63,13 @@ impl Error {
     /// Example context: Value that parameters from the `trace` evaluate to.
     pub fn context<K, V>(self, key: K, value: V) -> Self
     where
-        K: Into<sstring::SString>,
-        V: Into<sstring::SString>,
+        K: Into<kstring::KString>,
+        V: Into<kstring::KString>,
     {
         self.context_cow_string(key.into(), value.into())
     }
 
-    fn context_cow_string(mut self, key: sstring::SString, value: sstring::SString) -> Self {
+    fn context_cow_string(mut self, key: kstring::KString, value: kstring::KString) -> Self {
         self.inner
             .user_backtrace
             .last_mut()
diff --git a/crates/error/src/result_ext.rs b/crates/error/src/result_ext.rs
index 74cb9a3..ab1000d 100644
--- a/crates/error/src/result_ext.rs
+++ b/crates/error/src/result_ext.rs
@@ -10,13 +10,13 @@ use super::Result;
 pub trait ResultLiquidChainExt<T> {
     /// Create an `Error` with `E` as the cause.
     #[must_use]
-    fn chain<S: Into<sstring::SString>>(self, msg: S) -> Result<T>;
+    fn chain<S: Into<kstring::KString>>(self, msg: S) -> Result<T>;
 
     /// Create an `Error` with `E` as the cause.
     #[must_use]
     fn chain_with<F>(self, msg: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString;
+        F: FnOnce() -> kstring::KString;
 }
 
 /// `Result` extension methods for adapting third party errors to `Error`.
@@ -34,7 +34,7 @@ pub trait ResultLiquidReplaceExt<T> {
     /// let error: Result<i32> = error.lossy_chain("Missing liquid partial");
     /// ```
     #[must_use]
-    fn lossy_chain<S: Into<sstring::SString>>(self, msg: S) -> Result<T>;
+    fn lossy_chain<S: Into<kstring::KString>>(self, msg: S) -> Result<T>;
 
     /// Create an `Error` ignoring `E` as the cause.
     ///
@@ -53,7 +53,7 @@ pub trait ResultLiquidReplaceExt<T> {
     #[must_use]
     fn lossy_chain_with<F>(self, msg: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString;
+        F: FnOnce() -> kstring::KString;
 
     /// Create an `Error` ignoring `E` as the cause.
     ///
@@ -68,7 +68,7 @@ pub trait ResultLiquidReplaceExt<T> {
     /// let error: Result<i32> = error.replace("Missing liquid partial");
     /// ```
     #[must_use]
-    fn replace<S: Into<sstring::SString>>(self, msg: S) -> Result<T>;
+    fn replace<S: Into<kstring::KString>>(self, msg: S) -> Result<T>;
 
     /// Create an `Error` ignoring `E` as the cause.
     ///
@@ -87,20 +87,20 @@ pub trait ResultLiquidReplaceExt<T> {
     #[must_use]
     fn replace_with<F>(self, msg: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString;
+        F: FnOnce() -> kstring::KString;
 }
 
 impl<T, E> ResultLiquidChainExt<T> for result::Result<T, E>
 where
     E: ErrorClone,
 {
-    fn chain<S: Into<sstring::SString>>(self, msg: S) -> Result<T> {
+    fn chain<S: Into<kstring::KString>>(self, msg: S) -> Result<T> {
         self.map_err(|err| Error::with_msg(msg).cause(err))
     }
 
     fn chain_with<F>(self, msg: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString,
+        F: FnOnce() -> kstring::KString,
     {
         self.map_err(|err| Error::with_msg(msg()).cause(err))
     }
@@ -110,24 +110,24 @@ impl<T, E> ResultLiquidReplaceExt<T> for result::Result<T, E>
 where
     E: error::Error + Send + Sync + 'static,
 {
-    fn lossy_chain<S: Into<sstring::SString>>(self, msg: S) -> Result<T> {
+    fn lossy_chain<S: Into<kstring::KString>>(self, msg: S) -> Result<T> {
         self.map_err(|err| Error::with_msg(msg).cause(CloneableError::new(err)))
     }
 
     fn lossy_chain_with<F>(self, msg: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString,
+        F: FnOnce() -> kstring::KString,
     {
         self.map_err(|err| Error::with_msg(msg()).cause(CloneableError::new(err)))
     }
 
-    fn replace<S: Into<sstring::SString>>(self, msg: S) -> Result<T> {
+    fn replace<S: Into<kstring::KString>>(self, msg: S) -> Result<T> {
         self.map_err(|_| Error::with_msg(msg))
     }
 
     fn replace_with<F>(self, msg: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString,
+        F: FnOnce() -> kstring::KString,
     {
         self.map_err(|_| Error::with_msg(msg()))
     }
@@ -153,7 +153,7 @@ where
     #[must_use]
     fn trace<S>(self, trace: S) -> Result<T>
     where
-        S: Into<sstring::SString>;
+        S: Into<kstring::KString>;
 
     /// Add a new stack frame to the `liquid_error::Error`.
     ///
@@ -171,7 +171,7 @@ where
     #[must_use]
     fn trace_with<F>(self, trace: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString;
+        F: FnOnce() -> kstring::KString;
 
     /// Add state the current stack frame.
     ///
@@ -194,7 +194,7 @@ where
     #[must_use]
     fn context_key<S>(self, key: S) -> Key<T>
     where
-        S: Into<sstring::SString>;
+        S: Into<kstring::KString>;
 
     /// Add state the current stack frame.
     ///
@@ -217,34 +217,34 @@ where
     #[must_use]
     fn context_key_with<F>(self, key: F) -> FnKey<T, F>
     where
-        F: FnOnce() -> sstring::SString;
+        F: FnOnce() -> kstring::KString;
 }
 
 impl<T> ResultLiquidExt<T> for Result<T> {
     fn trace<S>(self, trace: S) -> Result<T>
     where
-        S: Into<sstring::SString>,
+        S: Into<kstring::KString>,
     {
         self.map_err(|err| err.trace(trace))
     }
 
     fn trace_with<F>(self, trace: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString,
+        F: FnOnce() -> kstring::KString,
     {
         self.map_err(|err| err.trace(trace()))
     }
 
     fn context_key<S>(self, key: S) -> Key<T>
     where
-        S: Into<sstring::SString>,
+        S: Into<kstring::KString>,
     {
         Key::new(self, key)
     }
 
     fn context_key_with<F>(self, key: F) -> FnKey<T, F>
     where
-        F: FnOnce() -> sstring::SString,
+        F: FnOnce() -> kstring::KString,
     {
         FnKey::new(self, key)
     }
@@ -254,7 +254,7 @@ impl<T> ResultLiquidExt<T> for Result<T> {
 #[allow(missing_debug_implementations)]
 pub struct Key<T> {
     builder: Result<T>,
-    key: sstring::SString,
+    key: kstring::KString,
 }
 
 impl<T> Key<T> {
@@ -262,7 +262,7 @@ impl<T> Key<T> {
     #[must_use]
     pub fn new<S>(builder: Result<T>, key: S) -> Self
     where
-        S: Into<sstring::SString>,
+        S: Into<kstring::KString>,
     {
         Self {
             builder,
@@ -274,7 +274,7 @@ impl<T> Key<T> {
     #[must_use]
     pub fn value<S>(self, value: S) -> Result<T>
     where
-        S: Into<sstring::SString>,
+        S: Into<kstring::KString>,
     {
         let builder = self.builder;
         let key = self.key;
@@ -285,7 +285,7 @@ impl<T> Key<T> {
     #[must_use]
     pub fn value_with<F>(self, value: F) -> Result<T>
     where
-        F: FnOnce() -> sstring::SString,
+        F: FnOnce() -> kstring::KString,
     {
         let builder = self.builder;
         let key = self.key;
@@ -297,7 +297,7 @@ impl<T> Key<T> {
 #[allow(missing_debug_implementations)]
 pub struct FnKey<T, F>
 where
-    F: FnOnce() -> sstring::SString,
+    F: FnOnce() -> kstring::KString,
 {
     builder: Result<T>,
     key: F,
@@ -305,7 +305,7 @@ where
 
 impl<T, F> FnKey<T, F>
 where
-    F: FnOnce() -> sstring::SString,
+    F: FnOnce() -> kstring::KString,
 {
     /// Save off a key for a context that will be added to `builder`.
     #[must_use]
@@ -317,7 +317,7 @@ where
     #[must_use]
     pub fn value<S>(self, value: S) -> Result<T>
     where
-        S: Into<sstring::SString>,
+        S: Into<kstring::KString>,
     {
         let builder = self.builder;
         let key = self.key;
@@ -328,7 +328,7 @@ where
     #[must_use]
     pub fn value_with<V>(self, value: V) -> Result<T>
     where
-        V: FnOnce() -> sstring::SString,
+        V: FnOnce() -> kstring::KString,
     {
         let builder = self.builder;
         let key = self.key;
diff --git a/crates/error/src/trace.rs b/crates/error/src/trace.rs
index 714d842..cb881e1 100644
--- a/crates/error/src/trace.rs
+++ b/crates/error/src/trace.rs
@@ -1,12 +1,12 @@
 /// User-visible call trace
 #[derive(Clone, PartialEq, Eq, Debug, Default)]
 pub(crate) struct Trace {
-    trace: Option<sstring::SString>,
-    context: Vec<(sstring::SString, sstring::SString)>,
+    trace: Option<kstring::KString>,
+    context: Vec<(kstring::KString, kstring::KString)>,
 }
 
 impl Trace {
-    pub(crate) fn new(trace: sstring::SString) -> Self {
+    pub(crate) fn new(trace: kstring::KString) -> Self {
         Self {
             trace: Some(trace),
             context: vec![],
@@ -20,7 +20,7 @@ impl Trace {
         }
     }
 
-    pub(crate) fn append_context(&mut self, key: sstring::SString, value: sstring::SString) {
+    pub(crate) fn append_context(&mut self, key: kstring::KString, value: kstring::KString) {
         self.context.push((key, value));
     }
 
@@ -28,7 +28,7 @@ impl Trace {
         self.trace.as_ref().map(|s| s.as_str())
     }
 
-    pub(crate) fn get_context(&self) -> &[(sstring::SString, sstring::SString)] {
+    pub(crate) fn get_context(&self) -> &[(kstring::KString, kstring::KString)] {
         self.context.as_ref()
     }
 }
diff --git a/crates/interpreter/Cargo.toml b/crates/interpreter/Cargo.toml
index 0bda5ef..5c9af65 100644
--- a/crates/interpreter/Cargo.toml
+++ b/crates/interpreter/Cargo.toml
@@ -18,7 +18,7 @@ azure-devops = { project = "cobalt-org", pipeline = "liquid-rust" }
 itertools = "0.8.0"
 anymap = "0.12"
 # Exposed in API
-sstring = { version = "0.1.0", path = "../sstring" }
+kstring = { version = "0.1.0", path = "../kstring" }
 liquid-error = { version = "^0.19", path = "../error" }
 liquid-value = { version = "^0.19", path = "../value" }
 
diff --git a/crates/interpreter/src/context.rs b/crates/interpreter/src/context.rs
index b3bd239..376da8e 100644
--- a/crates/interpreter/src/context.rs
+++ b/crates/interpreter/src/context.rs
@@ -188,7 +188,7 @@ impl<'g> Context<'g> {
     /// Sets up a new stack frame, executes the supplied function and then
     /// tears the stack frame down before returning the function's result
     /// to the caller.
-    pub fn run_in_named_scope<RvalT, S: Into<sstring::SString>, FnT>(
+    pub fn run_in_named_scope<RvalT, S: Into<kstring::KString>, FnT>(
         &mut self,
         name: S,
         f: FnT,
diff --git a/crates/interpreter/src/stack.rs b/crates/interpreter/src/stack.rs
index 5725f64..ef65872 100644
--- a/crates/interpreter/src/stack.rs
+++ b/crates/interpreter/src/stack.rs
@@ -6,7 +6,7 @@ use super::ValueStore;
 
 #[derive(Clone, Default, Debug)]
 struct Frame {
-    name: Option<sstring::SString>,
+    name: Option<kstring::KString>,
     data: Object,
 }
 
@@ -15,7 +15,7 @@ impl Frame {
         Default::default()
     }
 
-    fn with_name<S: Into<sstring::SString>>(name: S) -> Self {
+    fn with_name<S: Into<kstring::KString>>(name: S) -> Self {
         Self {
             name: Some(name.into()),
             data: Object::new(),
@@ -56,7 +56,7 @@ impl<'g> Stack<'g> {
     }
 
     /// Creates a new variable scope chained to a parent scope.
-    pub(crate) fn push_named_frame<S: Into<sstring::SString>>(&mut self, name: S) {
+    pub(crate) fn push_named_frame<S: Into<kstring::KString>>(&mut self, name: S) {
         self.stack.push(Frame::with_name(name));
     }
 
@@ -75,7 +75,7 @@ impl<'g> Stack<'g> {
     }
 
     /// The name of the currently active template.
-    pub fn frame_name(&self) -> Option<sstring::SStringRef<'_>> {
+    pub fn frame_name(&self) -> Option<kstring::KStringRef<'_>> {
         self.stack
             .iter()
             .rev()
@@ -99,14 +99,14 @@ impl<'g> Stack<'g> {
                 .unwrap_or_else(|| Scalar::new("nil"));
             let globals = itertools::join(self.globals().iter(), ", ");
             Error::with_msg("Unknown variable")
-                .context("requested variable", key.to_sstr())
+                .context("requested variable", key.to_kstr())
                 .context("available variables", globals)
         })?;
 
         frame.get_variable(path)
     }
 
-    fn globals(&self) -> Vec<sstring::SStringRef<'_>> {
+    fn globals(&self) -> Vec<kstring::KStringRef<'_>> {
         let mut globals = self.globals.map(|g| g.roots()).unwrap_or_default();
         for frame in self.stack.iter() {
             globals.extend(frame.data.roots());
@@ -118,7 +118,7 @@ impl<'g> Stack<'g> {
 
     fn find_path_frame<'a>(&'a self, path: PathRef<'_, '_>) -> Option<&'a dyn ValueStore> {
         let key = path.iter().next()?;
-        let key = key.to_sstr();
+        let key = key.to_kstr();
         self.find_frame(key.as_str())
     }
 
@@ -143,7 +143,7 @@ impl<'g> Stack<'g> {
     /// Used by increment and decrement tags
     pub fn set_index<S>(&mut self, name: S, val: Value) -> Option<Value>
     where
-        S: Into<sstring::SString>,
+        S: Into<kstring::KString>,
     {
         self.indexes.insert(name.into(), val)
     }
@@ -156,7 +156,7 @@ impl<'g> Stack<'g> {
     /// Sets a value in the global context.
     pub fn set_global<S>(&mut self, name: S, val: Value) -> Option<Value>
     where
-        S: Into<sstring::SString>,
+        S: Into<kstring::KString>,
     {
         let name = name.into();
         self.global_frame().insert(name, val)
@@ -172,7 +172,7 @@ impl<'g> Stack<'g> {
     /// this should never happen in a well-formed program.
     pub fn set<S>(&mut self, name: S, val: Value) -> Option<Value>
     where
-        S: Into<sstring::SString>,
+        S: Into<kstring::KString>,
     {
         self.current_frame().insert(name.into(), val)
     }
diff --git a/crates/interpreter/src/store.rs b/crates/interpreter/src/store.rs
index f74e7cf..5fae39b 100644
--- a/crates/interpreter/src/store.rs
+++ b/crates/interpreter/src/store.rs
@@ -13,7 +13,7 @@ pub trait ValueStore: fmt::Debug {
     fn contains_root(&self, name: &str) -> bool;
 
     /// Enumerate all root variables.
-    fn roots(&self) -> Vec<sstring::SStringRef<'_>>;
+    fn roots(&self) -> Vec<kstring::KStringRef<'_>>;
 
     /// Check if variable exists.
     ///
@@ -42,7 +42,7 @@ impl ValueStore for Object {
         self.contains_key(name)
     }
 
-    fn roots(&self) -> Vec<sstring::SStringRef<'_>> {
+    fn roots(&self) -> Vec<kstring::KStringRef<'_>> {
         self.keys().map(|s| s.as_ref()).collect()
     }
 
@@ -86,7 +86,7 @@ impl ValueStore for Object {
             let requested = path
                 .get(0)
                 .expect("`Path` guarantees at least one element")
-                .to_sstr()
+                .to_kstr()
                 .into_owned();
             let available = itertools::join(self.keys(), ", ");
             return Error::with_msg("Unknown variable")
@@ -100,7 +100,7 @@ impl ValueStore for Object {
 fn get_variable_option<'o>(obj: &'o Object, path: PathRef<'_, '_>) -> Option<&'o Value> {
     let mut indexes = path.iter();
     let key = indexes.next()?;
-    let key = key.to_sstr();
+    let key = key.to_kstr();
     let value = obj.get(key.as_str())?;
 
     indexes.fold(Some(value), |value, index| {
diff --git a/crates/sstring/Cargo.toml b/crates/kstring/Cargo.toml
similarity index 56%
rename from crates/sstring/Cargo.toml
rename to crates/kstring/Cargo.toml
index 246c032..6fa0015 100644
--- a/crates/sstring/Cargo.toml
+++ b/crates/kstring/Cargo.toml
@@ -1,10 +1,12 @@
 [package]
-name = "sstring"
+name = "kstring"
 version = "0.1.0"
 authors = ["Ed Page <eopage@gmail.com>"]
-description = "String library optimized for static declarations"
+description = "Key String: optimized for map keys"
+keywords = ["serde", "serialization", "string"]
+categories = ["data-structures", "text-processing"]
 repository = "https://github.com/cobalt-org/liquid-rust"
-documentation = "https://docs.rs/sstring"
+documentation = "https://docs.rs/kstring"
 readme = "README.md"
 license = "MIT OR Apache-2.0"
 edition = "2018"
@@ -13,4 +15,4 @@ edition = "2018"
 azure-devops = { project = "cobalt-org", pipeline = "liquid-rust" }
 
 [dependencies]
-serde = { version = "1.0", features = ["derive"] }
+serde = "1.0"
diff --git a/crates/kstring/README.md b/crates/kstring/README.md
new file mode 100644
index 0000000..b5aaf66
--- /dev/null
+++ b/crates/kstring/README.md
@@ -0,0 +1,38 @@
+KString
+===========
+
+> Key String: Optimized for map keys.
+
+[![Build Status](https://dev.azure.com/cobalt-org/cobalt-org/_apis/build/status/liquid-rust?branchName=master)](https://dev.azure.com/cobalt-org/cobalt-org/_build/latest?definitionId=1&branchName=master)
+[![Crates Status](https://img.shields.io/crates/v/kstring.svg)](https://crates.io/crates/kstring)
+
+Considerations:
+- Large maps
+- Most keys live and drop without being used in any other way
+- Most keys are relatively small (single to double digit bytes)
+- Keys are immutable
+- Allow zero-cost abstractions between structs and maps (e.g. no allocating
+  when dealing with struct field names)
+
+Ramifications:
+- Inline small strings rather than going to the heap.
+- Preserve `&'static str` across strings (`KString`),
+  references (`KStringRef`), and lifetime abstractions (`KStringCow`) to avoid
+  allocating for struct field names.
+- Use `Box<str>` rather than `String` to use less memory.
+
+## License
+
+Licensed under either of
+
+ * Apache License, Version 2.0, (http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license (http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally
+submitted for inclusion in the work by you, as defined in the Apache-2.0
+license, shall be dual licensed as above, without any additional terms or
+conditions.
diff --git a/crates/kstring/benches/cow.rs b/crates/kstring/benches/cow.rs
new file mode 100644
index 0000000..b82c09e
--- /dev/null
+++ b/crates/kstring/benches/cow.rs
@@ -0,0 +1,109 @@
+#![feature(test)]
+
+extern crate test;
+
+mod macros;
+
+type StringCow<'s> = std::borrow::Cow<'s, str>;
+
+mod cow {
+    use super::*;
+
+    bench_clone_static!(bench_clone_static_00, 0, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_01, 1, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_02, 2, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_03, 3, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_04, 4, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_05, 5, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_06, 6, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_07, 7, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_08, 8, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_09, 9, |fixture| StringCow::from(fixture));
+    bench_clone_static!(bench_clone_static_10, 10, |fixture| StringCow::from(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_11, 11, |fixture| StringCow::from(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_12, 12, |fixture| StringCow::from(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_13, 13, |fixture| StringCow::from(
+        fixture
+    ));
+
+    bench_clone_ref!(bench_clone_ref_00, 0, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_01, 1, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_02, 2, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_03, 3, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_04, 4, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_05, 5, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_06, 6, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_07, 7, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_08, 8, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_09, 9, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_10, 10, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_11, 11, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_12, 12, |fixture| StringCow::from(fixture));
+    bench_clone_ref!(bench_clone_ref_13, 13, |fixture| StringCow::from(fixture));
+
+    bench_clone_owned!(bench_clone_owned_00, 0, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_01, 1, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_02, 2, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_03, 3, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_04, 4, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_05, 5, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_06, 6, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_07, 7, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_08, 8, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_09, 9, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_10, 10, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_11, 11, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_12, 12, |fixture| StringCow::from(fixture));
+    bench_clone_owned!(bench_clone_owned_13, 13, |fixture| StringCow::from(fixture));
+
+    bench_eq_static!(bench_eq_static_00, 0, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_01, 1, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_02, 2, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_03, 3, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_04, 4, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_05, 5, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_06, 6, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_07, 7, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_08, 8, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_09, 9, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_10, 10, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_11, 11, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_12, 12, |fixture| StringCow::from(fixture));
+    bench_eq_static!(bench_eq_static_13, 13, |fixture| StringCow::from(fixture));
+
+    bench_eq_ref!(bench_eq_ref_00, 0, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_01, 1, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_02, 2, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_03, 3, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_04, 4, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_05, 5, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_06, 6, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_07, 7, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_08, 8, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_09, 9, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_10, 10, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_11, 11, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_12, 12, |fixture| StringCow::from(fixture));
+    bench_eq_ref!(bench_eq_ref_13, 13, |fixture| StringCow::from(fixture));
+
+    bench_eq_owned!(bench_eq_owned_00, 0, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_01, 1, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_02, 2, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_03, 3, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_04, 4, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_05, 5, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_06, 6, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_07, 7, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_08, 8, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_09, 9, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_10, 10, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_11, 11, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_12, 12, |fixture| StringCow::from(fixture));
+    bench_eq_owned!(bench_eq_owned_13, 13, |fixture| StringCow::from(fixture));
+}
diff --git a/crates/kstring/benches/kstring.rs b/crates/kstring/benches/kstring.rs
new file mode 100644
index 0000000..d0019b6
--- /dev/null
+++ b/crates/kstring/benches/kstring.rs
@@ -0,0 +1,213 @@
+#![feature(test)]
+
+extern crate test;
+
+use kstring::KString;
+
+mod macros;
+
+mod kstring_ {
+    use super::*;
+
+    bench_clone_static!(bench_clone_static_00, 0, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_01, 1, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_02, 2, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_03, 3, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_04, 4, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_05, 5, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_06, 6, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_07, 7, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_08, 8, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_09, 9, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_10, 10, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_11, 11, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_12, 12, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_13, 13, |fixture| KString::from_static(
+        fixture
+    ));
+
+    bench_clone_ref!(bench_clone_ref_00, 0, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_01, 1, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_02, 2, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_03, 3, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_04, 4, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_05, 5, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_06, 6, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_07, 7, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_08, 8, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_09, 9, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_10, 10, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_11, 11, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_12, 12, |fixture| KString::from_ref(fixture));
+    bench_clone_ref!(bench_clone_ref_13, 13, |fixture| KString::from_ref(fixture));
+
+    bench_clone_owned!(bench_clone_owned_00, 0, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_01, 1, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_02, 2, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_03, 3, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_04, 4, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_05, 5, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_06, 6, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_07, 7, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_08, 8, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_09, 9, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_10, 10, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_11, 11, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_12, 12, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_13, 13, |fixture| KString::from_string(
+        fixture
+    ));
+
+    bench_eq_static!(bench_eq_static_00, 0, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_01, 1, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_02, 2, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_03, 3, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_04, 4, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_05, 5, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_06, 6, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_07, 7, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_08, 8, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_09, 9, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_10, 10, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_11, 11, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_12, 12, |fixture| KString::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_13, 13, |fixture| KString::from_static(
+        fixture
+    ));
+
+    bench_eq_ref!(bench_eq_ref_00, 0, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_01, 1, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_02, 2, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_03, 3, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_04, 4, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_05, 5, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_06, 6, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_07, 7, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_08, 8, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_09, 9, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_10, 10, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_11, 11, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_12, 12, |fixture| KString::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_13, 13, |fixture| KString::from_ref(fixture));
+
+    bench_eq_owned!(bench_eq_owned_00, 0, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_01, 1, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_02, 2, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_03, 3, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_04, 4, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_05, 5, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_06, 6, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_07, 7, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_08, 8, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_09, 9, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_10, 10, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_11, 11, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_12, 12, |fixture| KString::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_13, 13, |fixture| KString::from_string(
+        fixture
+    ));
+}
diff --git a/crates/kstring/benches/kstringcow.rs b/crates/kstring/benches/kstringcow.rs
new file mode 100644
index 0000000..b1fdf58
--- /dev/null
+++ b/crates/kstring/benches/kstringcow.rs
@@ -0,0 +1,249 @@
+#![feature(test)]
+
+extern crate test;
+
+use kstring::KStringCow;
+
+mod macros;
+
+mod kstringcow {
+    use super::*;
+
+    bench_clone_static!(bench_clone_static_00, 0, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_01, 1, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_02, 2, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_03, 3, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_04, 4, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_05, 5, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_06, 6, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_07, 7, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_08, 8, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_09, 9, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_clone_static!(
+        bench_clone_static_10,
+        10,
+        |fixture| KStringCow::from_static(fixture)
+    );
+    bench_clone_static!(
+        bench_clone_static_11,
+        11,
+        |fixture| KStringCow::from_static(fixture)
+    );
+    bench_clone_static!(
+        bench_clone_static_12,
+        12,
+        |fixture| KStringCow::from_static(fixture)
+    );
+    bench_clone_static!(
+        bench_clone_static_13,
+        13,
+        |fixture| KStringCow::from_static(fixture)
+    );
+
+    bench_clone_ref!(bench_clone_ref_00, 0, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_01, 1, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_02, 2, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_03, 3, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_04, 4, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_05, 5, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_06, 6, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_07, 7, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_08, 8, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_09, 9, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_10, 10, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_11, 11, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_12, 12, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_13, 13, |fixture| KStringCow::from_ref(
+        fixture
+    ));
+
+    bench_clone_owned!(bench_clone_owned_00, 0, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_01, 1, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_02, 2, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_03, 3, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_04, 4, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_05, 5, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_06, 6, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_07, 7, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_08, 8, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_09, 9, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_10, 10, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_11, 11, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_12, 12, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_clone_owned!(bench_clone_owned_13, 13, |fixture| KStringCow::from_string(
+        fixture
+    ));
+
+    bench_eq_static!(bench_eq_static_00, 0, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_01, 1, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_02, 2, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_03, 3, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_04, 4, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_05, 5, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_06, 6, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_07, 7, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_08, 8, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_09, 9, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_10, 10, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_11, 11, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_12, 12, |fixture| KStringCow::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_13, 13, |fixture| KStringCow::from_static(
+        fixture
+    ));
+
+    bench_eq_ref!(bench_eq_ref_00, 0, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_01, 1, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_02, 2, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_03, 3, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_04, 4, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_05, 5, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_06, 6, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_07, 7, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_08, 8, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_09, 9, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_10, 10, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_11, 11, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_12, 12, |fixture| KStringCow::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_13, 13, |fixture| KStringCow::from_ref(fixture));
+
+    bench_eq_owned!(bench_eq_owned_00, 0, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_01, 1, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_02, 2, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_03, 3, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_04, 4, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_05, 5, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_06, 6, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_07, 7, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_08, 8, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_09, 9, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_10, 10, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_11, 11, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_12, 12, |fixture| KStringCow::from_string(
+        fixture
+    ));
+    bench_eq_owned!(bench_eq_owned_13, 13, |fixture| KStringCow::from_string(
+        fixture
+    ));
+}
diff --git a/crates/kstring/benches/kstringref.rs b/crates/kstring/benches/kstringref.rs
new file mode 100644
index 0000000..b229cd8
--- /dev/null
+++ b/crates/kstring/benches/kstringref.rs
@@ -0,0 +1,163 @@
+#![feature(test)]
+
+extern crate test;
+
+use kstring::KStringRef;
+
+mod macros;
+
+mod kstringref {
+    use super::*;
+
+    bench_clone_static!(bench_clone_static_00, 0, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_01, 1, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_02, 2, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_03, 3, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_04, 4, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_05, 5, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_06, 6, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_07, 7, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_08, 8, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(bench_clone_static_09, 9, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_clone_static!(
+        bench_clone_static_10,
+        10,
+        |fixture| KStringRef::from_static(fixture)
+    );
+    bench_clone_static!(
+        bench_clone_static_11,
+        11,
+        |fixture| KStringRef::from_static(fixture)
+    );
+    bench_clone_static!(
+        bench_clone_static_12,
+        12,
+        |fixture| KStringRef::from_static(fixture)
+    );
+    bench_clone_static!(
+        bench_clone_static_13,
+        13,
+        |fixture| KStringRef::from_static(fixture)
+    );
+
+    bench_clone_ref!(bench_clone_ref_00, 0, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_01, 1, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_02, 2, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_03, 3, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_04, 4, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_05, 5, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_06, 6, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_07, 7, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_08, 8, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_09, 9, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_10, 10, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_11, 11, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_12, 12, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+    bench_clone_ref!(bench_clone_ref_13, 13, |fixture| KStringRef::from_ref(
+        fixture
+    ));
+
+    bench_eq_static!(bench_eq_static_00, 0, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_01, 1, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_02, 2, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_03, 3, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_04, 4, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_05, 5, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_06, 6, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_07, 7, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_08, 8, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_09, 9, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_10, 10, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_11, 11, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_12, 12, |fixture| KStringRef::from_static(
+        fixture
+    ));
+    bench_eq_static!(bench_eq_static_13, 13, |fixture| KStringRef::from_static(
+        fixture
+    ));
+
+    bench_eq_ref!(bench_eq_ref_00, 0, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_01, 1, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_02, 2, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_03, 3, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_04, 4, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_05, 5, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_06, 6, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_07, 7, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_08, 8, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_09, 9, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_10, 10, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_11, 11, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_12, 12, |fixture| KStringRef::from_ref(fixture));
+    bench_eq_ref!(bench_eq_ref_13, 13, |fixture| KStringRef::from_ref(fixture));
+}
diff --git a/crates/kstring/benches/macros.rs b/crates/kstring/benches/macros.rs
new file mode 100644
index 0000000..764bb8e
--- /dev/null
+++ b/crates/kstring/benches/macros.rs
@@ -0,0 +1,88 @@
+pub static FIXTURES: &[&'static str] = &[
+    "",
+    "0",
+    "01",
+    "012",
+    "0123",
+    "01234",
+    "012345",
+    "0123456",
+    "01234567",
+    "012345678",
+    "0123456789",
+    "01234567890123456789",
+    "0123456789012345678901234567890123456789",
+    "01234567890123456789012345678901234567890123456789012345678901234567890123456789",
+];
+
+#[macro_export]
+macro_rules! bench_clone_static {
+    ($name:ident, $index:literal, $expr:expr) => {
+        #[bench]
+        fn $name(b: &mut test::Bencher) {
+            let fixture = crate::macros::FIXTURES[$index];
+            let uut = $expr(fixture);
+            b.iter(|| uut.clone());
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! bench_clone_ref {
+    ($name:ident, $index:literal, $expr:expr) => {
+        #[bench]
+        fn $name(b: &mut test::Bencher) {
+            let fixture = String::from(crate::macros::FIXTURES[$index]);
+            let uut = $expr(fixture.as_str());
+            b.iter(|| uut.clone());
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! bench_clone_owned {
+    ($name:ident, $index:literal, $expr:expr) => {
+        #[bench]
+        fn $name(b: &mut test::Bencher) {
+            let fixture = String::from(crate::macros::FIXTURES[$index]);
+            let uut = $expr(fixture);
+            b.iter(|| uut.clone());
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! bench_eq_static {
+    ($name:ident, $index:literal, $expr:expr) => {
+        #[bench]
+        fn $name(b: &mut test::Bencher) {
+            let fixture = crate::macros::FIXTURES[$index];
+            let uut = $expr(fixture);
+            b.iter(|| uut == fixture);
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! bench_eq_ref {
+    ($name:ident, $index:literal, $expr:expr) => {
+        #[bench]
+        fn $name(b: &mut test::Bencher) {
+            let fixture = String::from(crate::macros::FIXTURES[$index]);
+            let uut = $expr(fixture.as_str());
+            b.iter(|| uut == fixture);
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! bench_eq_owned {
+    ($name:ident, $index:literal, $expr:expr) => {
+        #[bench]
+        fn $name(b: &mut test::Bencher) {
+            let fixture = String::from(crate::macros::FIXTURES[$index]);
+            let uut = $expr(fixture.clone());
+            b.iter(|| uut == fixture);
+        }
+    };
+}
diff --git a/crates/kstring/benches/str.rs b/crates/kstring/benches/str.rs
new file mode 100644
index 0000000..3473711
--- /dev/null
+++ b/crates/kstring/benches/str.rs
@@ -0,0 +1,65 @@
+#![feature(test)]
+
+extern crate test;
+
+mod macros;
+
+bench_clone_static!(bench_clone_static_00, 0, |fixture| fixture);
+bench_clone_static!(bench_clone_static_01, 1, |fixture| fixture);
+bench_clone_static!(bench_clone_static_02, 2, |fixture| fixture);
+bench_clone_static!(bench_clone_static_03, 3, |fixture| fixture);
+bench_clone_static!(bench_clone_static_04, 4, |fixture| fixture);
+bench_clone_static!(bench_clone_static_05, 5, |fixture| fixture);
+bench_clone_static!(bench_clone_static_06, 6, |fixture| fixture);
+bench_clone_static!(bench_clone_static_07, 7, |fixture| fixture);
+bench_clone_static!(bench_clone_static_08, 8, |fixture| fixture);
+bench_clone_static!(bench_clone_static_09, 9, |fixture| fixture);
+bench_clone_static!(bench_clone_static_10, 10, |fixture| fixture);
+bench_clone_static!(bench_clone_static_11, 11, |fixture| fixture);
+bench_clone_static!(bench_clone_static_12, 12, |fixture| fixture);
+bench_clone_static!(bench_clone_static_13, 13, |fixture| fixture);
+
+bench_clone_ref!(bench_clone_ref_00, 0, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_01, 1, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_02, 2, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_03, 3, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_04, 4, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_05, 5, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_06, 6, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_07, 7, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_08, 8, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_09, 9, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_10, 10, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_11, 11, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_12, 12, |fixture| fixture);
+bench_clone_ref!(bench_clone_ref_13, 13, |fixture| fixture);
+
+bench_eq_static!(bench_eq_static_00, 0, |fixture| fixture);
+bench_eq_static!(bench_eq_static_01, 1, |fixture| fixture);
+bench_eq_static!(bench_eq_static_02, 2, |fixture| fixture);
+bench_eq_static!(bench_eq_static_03, 3, |fixture| fixture);
+bench_eq_static!(bench_eq_static_04, 4, |fixture| fixture);
+bench_eq_static!(bench_eq_static_05, 5, |fixture| fixture);
+bench_eq_static!(bench_eq_static_06, 6, |fixture| fixture);
+bench_eq_static!(bench_eq_static_07, 7, |fixture| fixture);
+bench_eq_static!(bench_eq_static_08, 8, |fixture| fixture);
+bench_eq_static!(bench_eq_static_09, 9, |fixture| fixture);
+bench_eq_static!(bench_eq_static_10, 10, |fixture| fixture);
+bench_eq_static!(bench_eq_static_11, 11, |fixture| fixture);
+bench_eq_static!(bench_eq_static_12, 12, |fixture| fixture);
+bench_eq_static!(bench_eq_static_13, 13, |fixture| fixture);
+
+bench_eq_ref!(bench_eq_ref_00, 0, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_01, 1, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_02, 2, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_03, 3, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_04, 4, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_05, 5, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_06, 6, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_07, 7, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_08, 8, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_09, 9, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_10, 10, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_11, 11, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_12, 12, |fixture| fixture);
+bench_eq_ref!(bench_eq_ref_13, 13, |fixture| fixture);
diff --git a/crates/kstring/benches/string.rs b/crates/kstring/benches/string.rs
new file mode 100644
index 0000000..4d4679f
--- /dev/null
+++ b/crates/kstring/benches/string.rs
@@ -0,0 +1,65 @@
+#![feature(test)]
+
+extern crate test;
+
+mod macros;
+
+bench_clone_static!(bench_clone_static_00, 0, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_01, 1, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_02, 2, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_03, 3, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_04, 4, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_05, 5, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_06, 6, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_07, 7, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_08, 8, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_09, 9, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_10, 10, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_11, 11, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_12, 12, |fixture| String::from(fixture));
+bench_clone_static!(bench_clone_static_13, 13, |fixture| String::from(fixture));
+
+bench_clone_owned!(bench_clone_owned_00, 0, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_01, 1, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_02, 2, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_03, 3, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_04, 4, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_05, 5, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_06, 6, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_07, 7, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_08, 8, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_09, 9, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_10, 10, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_11, 11, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_12, 12, |fixture| String::from(fixture));
+bench_clone_owned!(bench_clone_owned_13, 13, |fixture| String::from(fixture));
+
+bench_eq_static!(bench_eq_static_00, 0, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_01, 1, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_02, 2, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_03, 3, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_04, 4, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_05, 5, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_06, 6, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_07, 7, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_08, 8, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_09, 9, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_10, 10, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_11, 11, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_12, 12, |fixture| String::from(fixture));
+bench_eq_static!(bench_eq_static_13, 13, |fixture| String::from(fixture));
+
+bench_eq_owned!(bench_eq_owned_00, 0, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_01, 1, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_02, 2, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_03, 3, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_04, 4, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_05, 5, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_06, 6, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_07, 7, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_08, 8, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_09, 9, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_10, 10, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_11, 11, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_12, 12, |fixture| String::from(fixture));
+bench_eq_owned!(bench_eq_owned_13, 13, |fixture| String::from(fixture));
diff --git a/crates/kstring/src/cow.rs b/crates/kstring/src/cow.rs
new file mode 100644
index 0000000..19984e1
--- /dev/null
+++ b/crates/kstring/src/cow.rs
@@ -0,0 +1,336 @@
+use std::fmt;
+
+use crate::KString;
+use crate::KStringRef;
+use crate::KStringRefInner;
+
+type StdString = std::string::String;
+type BoxedStr = Box<str>;
+
+/// A reference to a UTF-8 encoded, immutable string.
+#[derive(Clone, Debug)]
+#[repr(transparent)]
+pub struct KStringCow<'s> {
+    pub(crate) inner: KStringCowInner<'s>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub(crate) enum KStringCowInner<'s> {
+    Owned(KString),
+    Borrowed(&'s str),
+}
+
+impl<'s> KStringCow<'s> {
+    /// Create a new empty `KStringCow`.
+    #[inline]
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    /// Create an owned `KStringCow`.
+    #[inline]
+    pub fn from_boxed(other: BoxedStr) -> Self {
+        Self {
+            inner: KStringCowInner::Owned(KString::from_boxed(other)),
+        }
+    }
+
+    /// Create an owned `KStringCow`.
+    #[inline]
+    pub fn from_string(other: StdString) -> Self {
+        Self::from_boxed(other.into_boxed_str())
+    }
+
+    /// Create a reference to a borrowed data.
+    #[inline]
+    pub fn from_ref(other: &'s str) -> Self {
+        Self {
+            inner: KStringCowInner::Borrowed(other),
+        }
+    }
+
+    /// Create a reference to a `'static` data.
+    #[inline]
+    pub fn from_static(other: &'static str) -> Self {
+        Self {
+            inner: KStringCowInner::Owned(KString::from_static(other)),
+        }
+    }
+
+    /// Get a reference to the `KString`.
+    #[inline]
+    pub fn as_ref(&self) -> KStringRef<'_> {
+        self.inner.as_ref()
+    }
+
+    /// Clone the data into an owned-type.
+    #[inline]
+    pub fn into_owned(self) -> KString {
+        self.inner.into_owned()
+    }
+
+    /// Extracts a string slice containing the entire `KStringCow`.
+    #[inline]
+    pub fn as_str(&self) -> &str {
+        self.inner.as_str()
+    }
+
+    /// Convert to a mutable string type, cloning the data if necessary.
+    #[inline]
+    pub fn into_string(self) -> StdString {
+        String::from(self.into_boxed_str())
+    }
+
+    /// Convert to a mutable string type, cloning the data if necessary.
+    #[inline]
+    pub fn into_boxed_str(self) -> BoxedStr {
+        self.inner.into_boxed_str()
+    }
+}
+
+impl<'s> KStringCowInner<'s> {
+    #[inline]
+    fn as_ref(&self) -> KStringRef<'_> {
+        match self {
+            Self::Owned(ref s) => s.as_ref(),
+            Self::Borrowed(ref s) => KStringRef::from_ref(s),
+        }
+    }
+
+    #[inline]
+    fn into_owned(self) -> KString {
+        match self {
+            Self::Owned(s) => s,
+            Self::Borrowed(s) => KString::from_ref(s),
+        }
+    }
+
+    #[inline]
+    fn as_str(&self) -> &str {
+        match self {
+            Self::Owned(ref s) => s.as_str(),
+            Self::Borrowed(ref s) => s,
+        }
+    }
+
+    #[inline]
+    fn into_boxed_str(self) -> BoxedStr {
+        match self {
+            Self::Owned(s) => s.into_boxed_str(),
+            Self::Borrowed(s) => BoxedStr::from(s),
+        }
+    }
+}
+
+impl<'s> std::ops::Deref for KStringCow<'s> {
+    type Target = str;
+
+    #[inline]
+    fn deref(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl<'s> Eq for KStringCow<'s> {}
+
+impl<'s> PartialEq<KStringCow<'s>> for KStringCow<'s> {
+    #[inline]
+    fn eq(&self, other: &KStringCow<'s>) -> bool {
+        PartialEq::eq(self.as_str(), other.as_str())
+    }
+}
+
+impl<'s> PartialEq<str> for KStringCow<'s> {
+    #[inline]
+    fn eq(&self, other: &str) -> bool {
+        PartialEq::eq(self.as_str(), other)
+    }
+}
+
+impl<'s> PartialEq<&'s str> for KStringCow<'s> {
+    #[inline]
+    fn eq(&self, other: &&str) -> bool {
+        PartialEq::eq(self.as_str(), *other)
+    }
+}
+
+impl<'s> PartialEq<String> for KStringCow<'s> {
+    #[inline]
+    fn eq(&self, other: &StdString) -> bool {
+        PartialEq::eq(self.as_str(), other.as_str())
+    }
+}
+
+impl<'s> Ord for KStringCow<'s> {
+    #[inline]
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.as_str().cmp(other.as_str())
+    }
+}
+
+impl<'s> PartialOrd for KStringCow<'s> {
+    #[inline]
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        self.as_str().partial_cmp(other.as_str())
+    }
+}
+
+impl<'s> std::hash::Hash for KStringCow<'s> {
+    #[inline]
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.as_str().hash(state);
+    }
+}
+
+impl<'s> fmt::Display for KStringCow<'s> {
+    #[inline]
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(self.as_str(), f)
+    }
+}
+
+impl<'s> AsRef<str> for KStringCow<'s> {
+    #[inline]
+    fn as_ref(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl<'s> AsRef<[u8]> for KStringCow<'s> {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        self.as_bytes()
+    }
+}
+
+impl<'s> AsRef<std::ffi::OsStr> for KStringCow<'s> {
+    #[inline]
+    fn as_ref(&self) -> &std::ffi::OsStr {
+        (&**self).as_ref()
+    }
+}
+
+impl<'s> AsRef<std::path::Path> for KStringCow<'s> {
+    #[inline]
+    fn as_ref(&self) -> &std::path::Path {
+        std::path::Path::new(self)
+    }
+}
+
+impl<'s> std::borrow::Borrow<str> for KStringCow<'s> {
+    #[inline]
+    fn borrow(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl<'s> Default for KStringCow<'s> {
+    #[inline]
+    fn default() -> Self {
+        Self::from_static("")
+    }
+}
+
+impl From<KString> for KStringCow<'static> {
+    #[inline]
+    fn from(other: KString) -> Self {
+        let inner = KStringCowInner::Owned(other);
+        Self { inner }
+    }
+}
+
+impl<'s> From<KStringRef<'s>> for KStringCow<'s> {
+    #[inline]
+    fn from(other: KStringRef<'s>) -> Self {
+        match other.inner {
+            KStringRefInner::Borrowed(s) => Self::from_ref(s),
+            KStringRefInner::Singleton(s) => Self::from_static(s),
+        }
+    }
+}
+
+impl<'s> From<&'s KStringRef<'s>> for KStringCow<'s> {
+    #[inline]
+    fn from(other: &'s KStringRef<'s>) -> Self {
+        match other.inner {
+            KStringRefInner::Borrowed(s) => Self::from_ref(s),
+            KStringRefInner::Singleton(s) => Self::from_static(s),
+        }
+    }
+}
+
+impl From<StdString> for KStringCow<'static> {
+    #[inline]
+    fn from(other: StdString) -> Self {
+        // Since the memory is already allocated, don't bother moving it into a FixedString
+        Self::from_boxed(other.into_boxed_str())
+    }
+}
+
+impl<'s> From<&'s StdString> for KStringCow<'s> {
+    #[inline]
+    fn from(other: &'s StdString) -> Self {
+        Self::from_ref(other.as_str())
+    }
+}
+
+impl From<BoxedStr> for KStringCow<'static> {
+    #[inline]
+    fn from(other: BoxedStr) -> Self {
+        // Since the memory is already allocated, don't bother moving it into a FixedString
+        Self::from_boxed(other)
+    }
+}
+
+impl<'s> From<&'s BoxedStr> for KStringCow<'s> {
+    #[inline]
+    fn from(other: &'s BoxedStr) -> Self {
+        Self::from_ref(other)
+    }
+}
+
+impl<'s> From<&'s str> for KStringCow<'s> {
+    #[inline]
+    fn from(other: &'s str) -> Self {
+        Self::from_ref(other)
+    }
+}
+
+impl<'s> serde::Serialize for KStringCow<'s> {
+    #[inline]
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        serializer.serialize_str(self.as_str())
+    }
+}
+
+impl<'de, 's> serde::Deserialize<'de> for KStringCow<'s> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        use std::borrow::Cow;
+        let s: Cow<'_, str> = Cow::deserialize(deserializer)?;
+        let s = match s {
+            Cow::Owned(s) => KStringCow::from_boxed(s.into_boxed_str()),
+            Cow::Borrowed(s) => KStringCow::from_ref(s),
+        };
+        Ok(s)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_size() {
+        println!(
+            "Cow: {}",
+            std::mem::size_of::<std::borrow::Cow<'static, str>>()
+        );
+        println!("KStringCow: {}", std::mem::size_of::<KStringCow<'static>>());
+    }
+}
diff --git a/crates/kstring/src/fixed.rs b/crates/kstring/src/fixed.rs
new file mode 100644
index 0000000..8030697
--- /dev/null
+++ b/crates/kstring/src/fixed.rs
@@ -0,0 +1,36 @@
+macro_rules! fixed_string {
+    ($name:ident, $len:literal) => {
+        #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+        pub(crate) struct $name {
+            array: [u8; $len],
+        }
+
+        impl $name {
+            pub(crate) fn new(s: &str) -> Self {
+                assert_eq!(s.as_bytes().len(), $len);
+                let mut array = [0; $len];
+                array.copy_from_slice(&s.as_bytes()[0..$len]);
+                Self { array }
+            }
+
+            #[inline]
+            pub(crate) fn into_boxed_str(&self) -> Box<str> {
+                Box::from(self.as_str())
+            }
+
+            #[inline]
+            pub(crate) fn as_str(&self) -> &str {
+                unsafe { std::str::from_utf8_unchecked(&self.array) }
+            }
+        }
+    };
+}
+
+fixed_string!(FixedString1, 1);
+fixed_string!(FixedString2, 2);
+fixed_string!(FixedString3, 3);
+fixed_string!(FixedString4, 4);
+fixed_string!(FixedString5, 5);
+fixed_string!(FixedString6, 6);
+fixed_string!(FixedString7, 7);
+fixed_string!(FixedString8, 8);
diff --git a/crates/sstring/src/lib.rs b/crates/kstring/src/lib.rs
similarity index 76%
rename from crates/sstring/src/lib.rs
rename to crates/kstring/src/lib.rs
index 0abdbf4..73e8861 100644
--- a/crates/sstring/src/lib.rs
+++ b/crates/kstring/src/lib.rs
@@ -1,7 +1,10 @@
 mod cow;
+mod fixed;
 mod r#ref;
 mod string;
 
 pub use cow::*;
 pub use r#ref::*;
 pub use string::*;
+
+use fixed::*;
diff --git a/crates/kstring/src/ref.rs b/crates/kstring/src/ref.rs
new file mode 100644
index 0000000..b29d003
--- /dev/null
+++ b/crates/kstring/src/ref.rs
@@ -0,0 +1,261 @@
+use std::fmt;
+
+use crate::KString;
+use crate::KStringCow;
+
+type StdString = std::string::String;
+type BoxedStr = Box<str>;
+
+/// A reference to a UTF-8 encoded, immutable string.
+#[derive(Copy, Clone, Debug)]
+#[repr(transparent)]
+pub struct KStringRef<'s> {
+    pub(crate) inner: KStringRefInner<'s>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub(crate) enum KStringRefInner<'s> {
+    Borrowed(&'s str),
+    Singleton(&'static str),
+}
+
+impl<'s> KStringRef<'s> {
+    /// Create a new empty `KString`.
+    #[inline]
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    /// Create a reference to a borrowed data.
+    #[inline]
+    pub fn from_ref(other: &'s str) -> Self {
+        Self {
+            inner: KStringRefInner::Borrowed(other),
+        }
+    }
+
+    /// Create a reference to a `'static` data.
+    #[inline]
+    pub fn from_static(other: &'static str) -> Self {
+        Self {
+            inner: KStringRefInner::Singleton(other),
+        }
+    }
+
+    /// Clone the data into an owned-type.
+    #[inline]
+    pub fn to_owned(&self) -> KString {
+        self.inner.to_owned()
+    }
+
+    /// Extracts a string slice containing the entire `KStringRef`.
+    #[inline]
+    pub fn as_str(&self) -> &str {
+        self.inner.as_str()
+    }
+
+    /// Convert to a mutable string type, cloning the data if necessary.
+    #[inline]
+    pub fn into_mut(self) -> StdString {
+        self.inner.into_mut()
+    }
+}
+
+impl<'s> KStringRefInner<'s> {
+    #[inline]
+    fn to_owned(&self) -> KString {
+        match self {
+            Self::Borrowed(s) => KString::from_ref(s),
+            Self::Singleton(s) => KString::from_static(s),
+        }
+    }
+
+    #[inline]
+    fn as_str(&self) -> &str {
+        match self {
+            Self::Borrowed(ref s) => s,
+            Self::Singleton(ref s) => s,
+        }
+    }
+
+    #[inline]
+    fn into_mut(self) -> StdString {
+        self.as_str().to_owned()
+    }
+}
+
+impl<'s> std::ops::Deref for KStringRef<'s> {
+    type Target = str;
+
+    #[inline]
+    fn deref(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl<'s> Eq for KStringRef<'s> {}
+
+impl<'s> PartialEq<KStringRef<'s>> for KStringRef<'s> {
+    #[inline]
+    fn eq(&self, other: &KStringRef<'s>) -> bool {
+        PartialEq::eq(self.as_str(), other.as_str())
+    }
+}
+
+impl<'s> PartialEq<str> for KStringRef<'s> {
+    #[inline]
+    fn eq(&self, other: &str) -> bool {
+        PartialEq::eq(self.as_str(), other)
+    }
+}
+
+impl<'s> PartialEq<&'s str> for KStringRef<'s> {
+    #[inline]
+    fn eq(&self, other: &&str) -> bool {
+        PartialEq::eq(self.as_str(), *other)
+    }
+}
+
+impl<'s> PartialEq<String> for KStringRef<'s> {
+    #[inline]
+    fn eq(&self, other: &StdString) -> bool {
+        PartialEq::eq(self.as_str(), other.as_str())
+    }
+}
+
+impl<'s> Ord for KStringRef<'s> {
+    #[inline]
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.as_str().cmp(other.as_str())
+    }
+}
+
+impl<'s> PartialOrd for KStringRef<'s> {
+    #[inline]
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        self.as_str().partial_cmp(other.as_str())
+    }
+}
+
+impl<'s> std::hash::Hash for KStringRef<'s> {
+    #[inline]
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.as_str().hash(state);
+    }
+}
+
+impl<'s> fmt::Display for KStringRef<'s> {
+    #[inline]
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(self.as_str(), f)
+    }
+}
+
+impl<'s> AsRef<str> for KStringRef<'s> {
+    #[inline]
+    fn as_ref(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl<'s> AsRef<[u8]> for KStringRef<'s> {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        self.as_bytes()
+    }
+}
+
+impl<'s> AsRef<std::ffi::OsStr> for KStringRef<'s> {
+    #[inline]
+    fn as_ref(&self) -> &std::ffi::OsStr {
+        (&**self).as_ref()
+    }
+}
+
+impl<'s> AsRef<std::path::Path> for KStringRef<'s> {
+    #[inline]
+    fn as_ref(&self) -> &std::path::Path {
+        std::path::Path::new(self)
+    }
+}
+
+impl<'s> std::borrow::Borrow<str> for KStringRef<'s> {
+    #[inline]
+    fn borrow(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl<'s> Default for KStringRef<'s> {
+    #[inline]
+    fn default() -> Self {
+        Self::from_static("")
+    }
+}
+
+impl<'s> From<&'s KString> for KStringRef<'s> {
+    #[inline]
+    fn from(other: &'s KString) -> Self {
+        other.as_ref()
+    }
+}
+
+impl<'s> From<&'s KStringCow<'s>> for KStringRef<'s> {
+    #[inline]
+    fn from(other: &'s KStringCow<'s>) -> Self {
+        other.as_ref()
+    }
+}
+
+impl<'s> From<&'s StdString> for KStringRef<'s> {
+    #[inline]
+    fn from(other: &'s StdString) -> Self {
+        KStringRef::from_ref(other.as_str())
+    }
+}
+
+impl<'s> From<&'s BoxedStr> for KStringRef<'s> {
+    #[inline]
+    fn from(other: &'s BoxedStr) -> Self {
+        Self::from_ref(other)
+    }
+}
+
+impl<'s> From<&'s str> for KStringRef<'s> {
+    #[inline]
+    fn from(other: &'s str) -> Self {
+        KStringRef::from_ref(other)
+    }
+}
+
+impl<'s> serde::Serialize for KStringRef<'s> {
+    #[inline]
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        serializer.serialize_str(self.as_str())
+    }
+}
+
+impl<'de: 's, 's> serde::Deserialize<'de> for KStringRef<'s> {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        let s: &'s str = serde::Deserialize::deserialize(deserializer)?;
+        let s = KStringRef::from_ref(s);
+        Ok(s)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_size() {
+        println!("str: {}", std::mem::size_of::<&'static str>());
+        println!("KStringRef: {}", std::mem::size_of::<KStringRef<'static>>());
+    }
+}
diff --git a/crates/kstring/src/string.rs b/crates/kstring/src/string.rs
new file mode 100644
index 0000000..bd4adae
--- /dev/null
+++ b/crates/kstring/src/string.rs
@@ -0,0 +1,363 @@
+use std::fmt;
+
+use crate::FixedString1;
+use crate::FixedString2;
+use crate::FixedString3;
+use crate::FixedString4;
+use crate::FixedString5;
+use crate::FixedString6;
+use crate::FixedString7;
+use crate::FixedString8;
+use crate::KStringCow;
+use crate::KStringRef;
+
+type StdString = std::string::String;
+type BoxedStr = Box<str>;
+
+/// A UTF-8 encoded, immutable string.
+#[derive(Clone, Debug)]
+#[repr(transparent)]
+pub struct KString {
+    pub(crate) inner: KStringInner,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub(crate) enum KStringInner {
+    Owned(BoxedStr),
+    Singleton(&'static str),
+    Fixed1(FixedString1),
+    Fixed2(FixedString2),
+    Fixed3(FixedString3),
+    Fixed4(FixedString4),
+    Fixed5(FixedString5),
+    Fixed6(FixedString6),
+    Fixed7(FixedString7),
+    Fixed8(FixedString8),
+}
+
+impl KString {
+    /// Create a new empty `KString`.
+    #[inline]
+    pub fn new() -> Self {
+        Default::default()
+    }
+
+    /// Create an owned `KString`.
+    #[inline]
+    pub fn from_boxed(other: BoxedStr) -> Self {
+        Self {
+            inner: KStringInner::Owned(other),
+        }
+    }
+
+    /// Create an owned `KString`.
+    #[inline]
+    pub fn from_string(other: StdString) -> Self {
+        Self::from_boxed(other.into_boxed_str())
+    }
+
+    /// Create an owned `KString` optimally from a reference.
+    #[inline]
+    pub fn from_ref(other: &str) -> Self {
+        let inner = match other.len() {
+            0 => KStringInner::Singleton(""),
+            1 => KStringInner::Fixed1(FixedString1::new(other)),
+            2 => KStringInner::Fixed2(FixedString2::new(other)),
+            3 => KStringInner::Fixed3(FixedString3::new(other)),
+            4 => KStringInner::Fixed4(FixedString4::new(other)),
+            5 => KStringInner::Fixed5(FixedString5::new(other)),
+            6 => KStringInner::Fixed6(FixedString6::new(other)),
+            7 => KStringInner::Fixed7(FixedString7::new(other)),
+            8 => KStringInner::Fixed8(FixedString8::new(other)),
+            _ => KStringInner::Owned(BoxedStr::from(other)),
+        };
+        Self { inner }
+    }
+
+    /// Create a reference to a `'static` data.
+    #[inline]
+    pub fn from_static(other: &'static str) -> Self {
+        Self {
+            inner: KStringInner::Singleton(other),
+        }
+    }
+
+    /// Get a reference to the `KString`.
+    #[inline]
+    pub fn as_ref(&self) -> KStringRef<'_> {
+        self.inner.as_ref()
+    }
+
+    /// Extracts a string slice containing the entire `KString`.
+    #[inline]
+    pub fn as_str(&self) -> &str {
+        self.inner.as_str()
+    }
+
+    /// Convert to a mutable string type, cloning the data if necessary.
+    #[inline]
+    pub fn into_string(self) -> StdString {
+        String::from(self.into_boxed_str())
+    }
+
+    /// Convert to a mutable string type, cloning the data if necessary.
+    #[inline]
+    pub fn into_boxed_str(self) -> BoxedStr {
+        self.inner.into_boxed_str()
+    }
+}
+
+impl KStringInner {
+    #[inline]
+    fn as_ref(&self) -> KStringRef<'_> {
+        match self {
+            Self::Owned(ref s) => KStringRef::from_ref(s),
+            Self::Singleton(ref s) => KStringRef::from_static(s),
+            Self::Fixed1(ref s) => KStringRef::from_ref(s.as_str()),
+            Self::Fixed2(ref s) => KStringRef::from_ref(s.as_str()),
+            Self::Fixed3(ref s) => KStringRef::from_ref(s.as_str()),
+            Self::Fixed4(ref s) => KStringRef::from_ref(s.as_str()),
+            Self::Fixed5(ref s) => KStringRef::from_ref(s.as_str()),
+            Self::Fixed6(ref s) => KStringRef::from_ref(s.as_str()),
+            Self::Fixed7(ref s) => KStringRef::from_ref(s.as_str()),
+            Self::Fixed8(ref s) => KStringRef::from_ref(s.as_str()),
+        }
+    }
+
+    #[inline]
+    fn as_str(&self) -> &str {
+        match self {
+            Self::Owned(ref s) => &s,
+            Self::Singleton(ref s) => s,
+            Self::Fixed1(ref s) => s.as_str(),
+            Self::Fixed2(ref s) => s.as_str(),
+            Self::Fixed3(ref s) => s.as_str(),
+            Self::Fixed4(ref s) => s.as_str(),
+            Self::Fixed5(ref s) => s.as_str(),
+            Self::Fixed6(ref s) => s.as_str(),
+            Self::Fixed7(ref s) => s.as_str(),
+            Self::Fixed8(ref s) => s.as_str(),
+        }
+    }
+
+    #[inline]
+    fn into_boxed_str(self) -> BoxedStr {
+        match self {
+            Self::Owned(s) => s,
+            Self::Singleton(s) => BoxedStr::from(s),
+            Self::Fixed1(s) => s.into_boxed_str(),
+            Self::Fixed2(s) => s.into_boxed_str(),
+            Self::Fixed3(s) => s.into_boxed_str(),
+            Self::Fixed4(s) => s.into_boxed_str(),
+            Self::Fixed5(s) => s.into_boxed_str(),
+            Self::Fixed6(s) => s.into_boxed_str(),
+            Self::Fixed7(s) => s.into_boxed_str(),
+            Self::Fixed8(s) => s.into_boxed_str(),
+        }
+    }
+}
+
+impl std::ops::Deref for KString {
+    type Target = str;
+
+    #[inline]
+    fn deref(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl Eq for KString {}
+
+impl<'s> PartialEq<KString> for KString {
+    #[inline]
+    fn eq(&self, other: &KString) -> bool {
+        PartialEq::eq(self.as_str(), other.as_str())
+    }
+}
+
+impl<'s> PartialEq<str> for KString {
+    #[inline]
+    fn eq(&self, other: &str) -> bool {
+        PartialEq::eq(self.as_str(), other)
+    }
+}
+
+impl<'s> PartialEq<&'s str> for KString {
+    #[inline]
+    fn eq(&self, other: &&str) -> bool {
+        PartialEq::eq(self.as_str(), *other)
+    }
+}
+
+impl<'s> PartialEq<String> for KString {
+    #[inline]
+    fn eq(&self, other: &StdString) -> bool {
+        PartialEq::eq(self.as_str(), other.as_str())
+    }
+}
+
+impl Ord for KString {
+    #[inline]
+    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+        self.as_str().cmp(other.as_str())
+    }
+}
+
+impl PartialOrd for KString {
+    #[inline]
+    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+        self.as_str().partial_cmp(other.as_str())
+    }
+}
+
+impl std::hash::Hash for KString {
+    #[inline]
+    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+        self.as_str().hash(state);
+    }
+}
+
+impl fmt::Display for KString {
+    #[inline]
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        fmt::Display::fmt(self.as_str(), f)
+    }
+}
+
+impl AsRef<str> for KString {
+    #[inline]
+    fn as_ref(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl AsRef<[u8]> for KString {
+    #[inline]
+    fn as_ref(&self) -> &[u8] {
+        self.as_bytes()
+    }
+}
+
+impl AsRef<std::ffi::OsStr> for KString {
+    #[inline]
+    fn as_ref(&self) -> &std::ffi::OsStr {
+        (&**self).as_ref()
+    }
+}
+
+impl AsRef<std::path::Path> for KString {
+    #[inline]
+    fn as_ref(&self) -> &std::path::Path {
+        std::path::Path::new(self)
+    }
+}
+
+impl std::borrow::Borrow<str> for KString {
+    #[inline]
+    fn borrow(&self) -> &str {
+        self.as_str()
+    }
+}
+
+impl Default for KString {
+    #[inline]
+    fn default() -> Self {
+        Self::from_static("")
+    }
+}
+
+impl<'s> From<KStringRef<'s>> for KString {
+    #[inline]
+    fn from(other: KStringRef<'s>) -> Self {
+        other.to_owned()
+    }
+}
+
+impl<'s> From<&'s KStringRef<'s>> for KString {
+    #[inline]
+    fn from(other: &'s KStringRef<'s>) -> Self {
+        other.to_owned()
+    }
+}
+
+impl<'s> From<KStringCow<'s>> for KString {
+    #[inline]
+    fn from(other: KStringCow<'s>) -> Self {
+        other.into_owned()
+    }
+}
+
+impl<'s> From<&'s KStringCow<'s>> for KString {
+    #[inline]
+    fn from(other: &'s KStringCow<'s>) -> Self {
+        other.clone().into_owned()
+    }
+}
+
+impl From<StdString> for KString {
+    #[inline]
+    fn from(other: StdString) -> Self {
+        // Since the memory is already allocated, don't bother moving it into a FixedString
+        Self::from_boxed(other.into_boxed_str())
+    }
+}
+
+impl From<BoxedStr> for KString {
+    #[inline]
+    fn from(other: BoxedStr) -> Self {
+        // Since the memory is already allocated, don't bother moving it into a FixedString
+        Self::from_boxed(other)
+    }
+}
+
+impl<'s> From<&'s BoxedStr> for KString {
+    #[inline]
+    fn from(other: &'s BoxedStr) -> Self {
+        Self::from_ref(other)
+    }
+}
+
+impl From<&'static str> for KString {
+    #[inline]
+    fn from(other: &'static str) -> Self {
+        Self::from_static(other)
+    }
+}
+
+impl serde::Serialize for KString {
+    #[inline]
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        serializer.serialize_str(self.as_str())
+    }
+}
+
+impl<'de> serde::Deserialize<'de> for KString {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        use std::borrow::Cow;
+        let s: Cow<'_, str> = Cow::deserialize(deserializer)?;
+        let s = match s {
+            Cow::Owned(s) => KString::from_boxed(s.into_boxed_str()),
+            Cow::Borrowed(s) => KString::from_ref(s),
+        };
+        Ok(s)
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+
+    #[test]
+    fn test_size() {
+        println!("String: {}", std::mem::size_of::<StdString>());
+        println!("Box<str>: {}", std::mem::size_of::<BoxedStr>());
+        println!("Box<Box<str>>: {}", std::mem::size_of::<Box<BoxedStr>>());
+        println!("KString: {}", std::mem::size_of::<KString>());
+    }
+}
diff --git a/crates/sstring/README.md b/crates/sstring/README.md
deleted file mode 100644
index 9888765..0000000
--- a/crates/sstring/README.md
+++ /dev/null
@@ -1,23 +0,0 @@
-SString
-===========
-
-> Immutable string type optimized for static data.
-
-[![Build Status](https://dev.azure.com/cobalt-org/cobalt-org/_apis/build/status/liquid-rust?branchName=master)](https://dev.azure.com/cobalt-org/cobalt-org/_build/latest?definitionId=1&branchName=master)
-[![Crates Status](https://img.shields.io/crates/v/sstring.svg)](https://crates.io/crates/sstring)
-
-## License
-
-Licensed under either of
-
- * Apache License, Version 2.0, (http://www.apache.org/licenses/LICENSE-2.0)
- * MIT license (http://opensource.org/licenses/MIT)
-
-at your option.
-
-### Contribution
-
-Unless you explicitly state otherwise, any contribution intentionally
-submitted for inclusion in the work by you, as defined in the Apache-2.0
-license, shall be dual licensed as above, without any additional terms or
-conditions.
diff --git a/crates/sstring/src/cow.rs b/crates/sstring/src/cow.rs
deleted file mode 100644
index f77599d..0000000
--- a/crates/sstring/src/cow.rs
+++ /dev/null
@@ -1,265 +0,0 @@
-use std::fmt;
-
-use crate::SString;
-use crate::SStringInner;
-use crate::SStringRef;
-use crate::SStringRefInner;
-
-type StdString = std::string::String;
-
-/// A reference to a UTF-8 encoded, immutable string.
-#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
-#[serde(transparent)]
-#[repr(transparent)]
-pub struct SStringCow<'s> {
-    #[serde(with = "serde_string_cow")]
-    pub(crate) inner: SStringCowInner<'s>,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub(crate) enum SStringCowInner<'s> {
-    Owned(StdString),
-    Borrowed(&'s str),
-    Singleton(&'static str),
-}
-
-impl<'s> SStringCow<'s> {
-    /// Create a new empty `SString`.
-    pub fn new() -> Self {
-        Default::default()
-    }
-
-    /// Create an owned `SString`.
-    pub fn owned(other: impl Into<SString>) -> Self {
-        let other = other.into();
-        other.into()
-    }
-
-    /// Create a reference to a borrowed data.
-    pub fn borrow(other: impl Into<SStringRef<'s>>) -> Self {
-        let other = other.into();
-        other.into()
-    }
-
-    /// Create a reference to a `'static` data.
-    pub fn singleton(other: &'static str) -> Self {
-        Self {
-            inner: SStringCowInner::Singleton(other),
-        }
-    }
-
-    /// Get a reference to the `SString`.
-    pub fn as_ref(&self) -> SStringRef<'_> {
-        match self.inner {
-            SStringCowInner::Owned(ref s) => SStringRef::borrow(s),
-            SStringCowInner::Borrowed(ref s) => SStringRef::borrow(*s),
-            SStringCowInner::Singleton(ref s) => SStringRef::singleton(s),
-        }
-    }
-
-    /// Clone the data into an owned-type.
-    pub fn into_owned(self) -> SString {
-        match self.inner {
-            SStringCowInner::Owned(s) => s.into(),
-            SStringCowInner::Borrowed(s) => s.to_owned().into(),
-            SStringCowInner::Singleton(s) => s.into(),
-        }
-    }
-
-    /// Extracts a string slice containing the entire `SStringCow`.
-    pub fn as_str(&self) -> &str {
-        match self.inner {
-            SStringCowInner::Owned(ref s) => s.as_str(),
-            SStringCowInner::Borrowed(ref s) => s,
-            SStringCowInner::Singleton(ref s) => s,
-        }
-    }
-
-    /// Convert to a mutable string type, cloning the data if necessary.
-    pub fn into_mut(self) -> StdString {
-        match self.inner {
-            SStringCowInner::Owned(s) => s,
-            SStringCowInner::Borrowed(s) => s.to_owned(),
-            SStringCowInner::Singleton(s) => s.to_owned(),
-        }
-    }
-}
-
-impl<'s> std::ops::Deref for SStringCow<'s> {
-    type Target = str;
-
-    #[inline]
-    fn deref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl<'s> Eq for SStringCow<'s> {}
-
-impl<'s> PartialEq<SStringCow<'s>> for SStringCow<'s> {
-    #[inline]
-    fn eq(&self, other: &SStringCow<'s>) -> bool {
-        PartialEq::eq(self.as_str(), other.as_str())
-    }
-}
-
-impl<'s> PartialEq<str> for SStringCow<'s> {
-    #[inline]
-    fn eq(&self, other: &str) -> bool {
-        PartialEq::eq(self.as_str(), other)
-    }
-}
-
-impl<'s> PartialEq<&'s str> for SStringCow<'s> {
-    #[inline]
-    fn eq(&self, other: &&str) -> bool {
-        PartialEq::eq(self.as_str(), *other)
-    }
-}
-
-impl<'s> PartialEq<String> for SStringCow<'s> {
-    #[inline]
-    fn eq(&self, other: &StdString) -> bool {
-        PartialEq::eq(self.as_str(), other.as_str())
-    }
-}
-
-impl<'s> Ord for SStringCow<'s> {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.as_str().cmp(other.as_str())
-    }
-}
-
-impl<'s> PartialOrd for SStringCow<'s> {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        self.as_str().partial_cmp(other.as_str())
-    }
-}
-
-impl<'s> std::hash::Hash for SStringCow<'s> {
-    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        self.as_str().hash(state);
-    }
-}
-
-impl<'s> fmt::Display for SStringCow<'s> {
-    #[inline]
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self.as_str(), f)
-    }
-}
-
-impl<'s> AsRef<str> for SStringCow<'s> {
-    #[inline]
-    fn as_ref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl<'s> AsRef<[u8]> for SStringCow<'s> {
-    #[inline]
-    fn as_ref(&self) -> &[u8] {
-        self.as_bytes()
-    }
-}
-
-impl<'s> AsRef<std::ffi::OsStr> for SStringCow<'s> {
-    fn as_ref(&self) -> &std::ffi::OsStr {
-        (&**self).as_ref()
-    }
-}
-
-impl<'s> AsRef<std::path::Path> for SStringCow<'s> {
-    fn as_ref(&self) -> &std::path::Path {
-        std::path::Path::new(self)
-    }
-}
-
-impl<'s> std::borrow::Borrow<str> for SStringCow<'s> {
-    #[inline]
-    fn borrow(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl<'s> Default for SStringCow<'s> {
-    fn default() -> Self {
-        Self::singleton("")
-    }
-}
-
-impl<'s> From<SString> for SStringCow<'s> {
-    fn from(other: SString) -> Self {
-        match other.inner {
-            SStringInner::Owned(s) => s.into(),
-            SStringInner::Singleton(s) => SStringCow::singleton(s),
-        }
-    }
-}
-
-impl<'s> From<SStringRef<'s>> for SStringCow<'s> {
-    fn from(other: SStringRef<'s>) -> Self {
-        match other.inner {
-            SStringRefInner::Borrowed(s) => s.into(),
-            SStringRefInner::Singleton(s) => SStringCow::singleton(s),
-        }
-    }
-}
-
-impl<'s> From<&'s SStringRef<'s>> for SStringCow<'s> {
-    fn from(other: &'s SStringRef<'s>) -> Self {
-        match other.inner {
-            SStringRefInner::Borrowed(s) => s.into(),
-            SStringRefInner::Singleton(s) => SStringCow::singleton(s),
-        }
-    }
-}
-
-impl<'s> From<StdString> for SStringCow<'s> {
-    fn from(other: StdString) -> Self {
-        SStringCow {
-            inner: SStringCowInner::Owned(other),
-        }
-    }
-}
-
-impl<'s> From<&'s StdString> for SStringCow<'s> {
-    fn from(other: &'s StdString) -> Self {
-        SStringCow {
-            inner: SStringCowInner::Borrowed(other.as_str()),
-        }
-    }
-}
-
-impl<'s> From<&'s str> for SStringCow<'s> {
-    fn from(other: &'s str) -> Self {
-        SStringCow {
-            inner: SStringCowInner::Borrowed(other),
-        }
-    }
-}
-
-mod serde_string_cow {
-    use super::*;
-    use serde::{self, Deserialize, Deserializer, Serializer};
-
-    pub(crate) fn serialize<S>(data: &SStringCowInner, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        let s = match data {
-            SStringCowInner::Owned(ref s) => s.as_str(),
-            SStringCowInner::Borrowed(ref s) => s,
-            SStringCowInner::Singleton(ref s) => s,
-        };
-        serializer.serialize_str(&s)
-    }
-
-    pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<SStringCowInner<'static>, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        let s = StdString::deserialize(deserializer)?;
-        Ok(SStringCowInner::Owned(s))
-    }
-}
diff --git a/crates/sstring/src/ref.rs b/crates/sstring/src/ref.rs
deleted file mode 100644
index 3f939fe..0000000
--- a/crates/sstring/src/ref.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-use std::fmt;
-
-use crate::SString;
-use crate::SStringCow;
-
-type StdString = std::string::String;
-
-/// A reference to a UTF-8 encoded, immutable string.
-#[derive(Copy, Clone, Debug)]
-#[repr(transparent)]
-pub struct SStringRef<'s> {
-    pub(crate) inner: SStringRefInner<'s>,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub(crate) enum SStringRefInner<'s> {
-    Borrowed(&'s str),
-    Singleton(&'static str),
-}
-
-impl<'s> SStringRef<'s> {
-    /// Create a new empty `SString`.
-    pub fn new() -> Self {
-        Default::default()
-    }
-
-    /// Create a reference to a borrowed data.
-    pub fn borrow(other: impl Into<SStringRef<'s>>) -> Self {
-        other.into()
-    }
-
-    /// Create a reference to a `'static` data.
-    pub fn singleton(other: &'static str) -> Self {
-        Self {
-            inner: SStringRefInner::Singleton(other),
-        }
-    }
-
-    /// Clone the data into an owned-type.
-    pub fn to_owned(&self) -> SString {
-        match self.inner {
-            SStringRefInner::Borrowed(s) => s.to_owned().into(),
-            SStringRefInner::Singleton(s) => s.into(),
-        }
-    }
-
-    /// Extracts a string slice containing the entire `SStringRef`.
-    pub fn as_str(&self) -> &str {
-        match self.inner {
-            SStringRefInner::Borrowed(ref s) => s,
-            SStringRefInner::Singleton(ref s) => s,
-        }
-    }
-
-    /// Convert to a mutable string type, cloning the data if necessary.
-    pub fn into_mut(self) -> StdString {
-        match self.inner {
-            SStringRefInner::Borrowed(s) => s.to_owned(),
-            SStringRefInner::Singleton(s) => s.to_owned(),
-        }
-    }
-}
-
-impl<'s> std::ops::Deref for SStringRef<'s> {
-    type Target = str;
-
-    #[inline]
-    fn deref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl<'s> Eq for SStringRef<'s> {}
-
-impl<'s> PartialEq<SStringRef<'s>> for SStringRef<'s> {
-    #[inline]
-    fn eq(&self, other: &SStringRef<'s>) -> bool {
-        PartialEq::eq(self.as_str(), other.as_str())
-    }
-}
-
-impl<'s> PartialEq<str> for SStringRef<'s> {
-    #[inline]
-    fn eq(&self, other: &str) -> bool {
-        PartialEq::eq(self.as_str(), other)
-    }
-}
-
-impl<'s> PartialEq<&'s str> for SStringRef<'s> {
-    #[inline]
-    fn eq(&self, other: &&str) -> bool {
-        PartialEq::eq(self.as_str(), *other)
-    }
-}
-
-impl<'s> PartialEq<String> for SStringRef<'s> {
-    #[inline]
-    fn eq(&self, other: &StdString) -> bool {
-        PartialEq::eq(self.as_str(), other.as_str())
-    }
-}
-
-impl<'s> Ord for SStringRef<'s> {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.as_str().cmp(other.as_str())
-    }
-}
-
-impl<'s> PartialOrd for SStringRef<'s> {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        self.as_str().partial_cmp(other.as_str())
-    }
-}
-
-impl<'s> std::hash::Hash for SStringRef<'s> {
-    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        self.as_str().hash(state);
-    }
-}
-
-impl<'s> fmt::Display for SStringRef<'s> {
-    #[inline]
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self.as_str(), f)
-    }
-}
-
-impl<'s> AsRef<str> for SStringRef<'s> {
-    #[inline]
-    fn as_ref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl<'s> AsRef<[u8]> for SStringRef<'s> {
-    #[inline]
-    fn as_ref(&self) -> &[u8] {
-        self.as_bytes()
-    }
-}
-
-impl<'s> AsRef<std::ffi::OsStr> for SStringRef<'s> {
-    fn as_ref(&self) -> &std::ffi::OsStr {
-        (&**self).as_ref()
-    }
-}
-
-impl<'s> AsRef<std::path::Path> for SStringRef<'s> {
-    fn as_ref(&self) -> &std::path::Path {
-        std::path::Path::new(self)
-    }
-}
-
-impl<'s> std::borrow::Borrow<str> for SStringRef<'s> {
-    #[inline]
-    fn borrow(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl<'s> Default for SStringRef<'s> {
-    fn default() -> Self {
-        Self::singleton("")
-    }
-}
-
-impl<'s> From<&'s SString> for SStringRef<'s> {
-    fn from(other: &'s SString) -> Self {
-        other.as_ref()
-    }
-}
-
-impl<'s> From<&'s SStringCow<'s>> for SStringRef<'s> {
-    fn from(other: &'s SStringCow<'s>) -> Self {
-        other.as_ref()
-    }
-}
-
-impl<'s> From<&'s StdString> for SStringRef<'s> {
-    fn from(other: &'s StdString) -> Self {
-        SStringRef {
-            inner: SStringRefInner::Borrowed(other.as_str()),
-        }
-    }
-}
-
-impl<'s> From<&'s str> for SStringRef<'s> {
-    fn from(other: &'s str) -> Self {
-        SStringRef {
-            inner: SStringRefInner::Borrowed(other),
-        }
-    }
-}
diff --git a/crates/sstring/src/string.rs b/crates/sstring/src/string.rs
deleted file mode 100644
index 71616fd..0000000
--- a/crates/sstring/src/string.rs
+++ /dev/null
@@ -1,233 +0,0 @@
-use std::fmt;
-
-use crate::SStringCow;
-use crate::SStringRef;
-
-type StdString = std::string::String;
-
-/// A UTF-8 encoded, immutable string.
-#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
-#[serde(transparent)]
-#[repr(transparent)]
-pub struct SString {
-    #[serde(with = "serde_string")]
-    pub(crate) inner: SStringInner,
-}
-
-#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub(crate) enum SStringInner {
-    Owned(StdString),
-    Singleton(&'static str),
-}
-
-impl SString {
-    /// Create a new empty `SString`.
-    pub fn new() -> Self {
-        Default::default()
-    }
-
-    /// Create an owned `SString`.
-    pub fn owned(other: impl Into<StdString>) -> Self {
-        Self {
-            inner: SStringInner::Owned(other.into()),
-        }
-    }
-
-    /// Create a reference to a `'static` data.
-    pub fn singleton(other: &'static str) -> Self {
-        Self {
-            inner: SStringInner::Singleton(other),
-        }
-    }
-
-    /// Get a reference to the `SString`.
-    pub fn as_ref(&self) -> SStringRef<'_> {
-        match self.inner {
-            SStringInner::Owned(ref s) => SStringRef::borrow(s),
-            SStringInner::Singleton(ref s) => SStringRef::singleton(s),
-        }
-    }
-
-    /// Extracts a string slice containing the entire `SString`.
-    pub fn as_str(&self) -> &str {
-        match self.inner {
-            SStringInner::Owned(ref s) => s.as_str(),
-            SStringInner::Singleton(ref s) => s,
-        }
-    }
-
-    /// Convert to a mutable string type, cloning the data if necessary.
-    pub fn into_mut(self) -> StdString {
-        match self.inner {
-            SStringInner::Owned(s) => s,
-            SStringInner::Singleton(s) => s.to_owned(),
-        }
-    }
-}
-
-impl std::ops::Deref for SString {
-    type Target = str;
-
-    #[inline]
-    fn deref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl Eq for SString {}
-
-impl<'s> PartialEq<SString> for SString {
-    #[inline]
-    fn eq(&self, other: &SString) -> bool {
-        PartialEq::eq(self.as_str(), other.as_str())
-    }
-}
-
-impl<'s> PartialEq<str> for SString {
-    #[inline]
-    fn eq(&self, other: &str) -> bool {
-        PartialEq::eq(self.as_str(), other)
-    }
-}
-
-impl<'s> PartialEq<&'s str> for SString {
-    #[inline]
-    fn eq(&self, other: &&str) -> bool {
-        PartialEq::eq(self.as_str(), *other)
-    }
-}
-
-impl<'s> PartialEq<String> for SString {
-    #[inline]
-    fn eq(&self, other: &StdString) -> bool {
-        PartialEq::eq(self.as_str(), other.as_str())
-    }
-}
-
-impl Ord for SString {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
-        self.as_str().cmp(other.as_str())
-    }
-}
-
-impl PartialOrd for SString {
-    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
-        self.as_str().partial_cmp(other.as_str())
-    }
-}
-
-impl std::hash::Hash for SString {
-    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
-        self.as_str().hash(state);
-    }
-}
-
-impl fmt::Display for SString {
-    #[inline]
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        fmt::Display::fmt(self.as_str(), f)
-    }
-}
-
-impl AsRef<str> for SString {
-    #[inline]
-    fn as_ref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl AsRef<[u8]> for SString {
-    #[inline]
-    fn as_ref(&self) -> &[u8] {
-        self.as_bytes()
-    }
-}
-
-impl AsRef<std::ffi::OsStr> for SString {
-    fn as_ref(&self) -> &std::ffi::OsStr {
-        (&**self).as_ref()
-    }
-}
-
-impl AsRef<std::path::Path> for SString {
-    fn as_ref(&self) -> &std::path::Path {
-        std::path::Path::new(self)
-    }
-}
-
-impl std::borrow::Borrow<str> for SString {
-    #[inline]
-    fn borrow(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl Default for SString {
-    fn default() -> Self {
-        "".into()
-    }
-}
-
-impl<'s> From<SStringRef<'s>> for SString {
-    fn from(other: SStringRef<'s>) -> Self {
-        other.to_owned()
-    }
-}
-
-impl<'s> From<&'s SStringRef<'s>> for SString {
-    fn from(other: &'s SStringRef<'s>) -> Self {
-        other.to_owned()
-    }
-}
-
-impl<'s> From<SStringCow<'s>> for SString {
-    fn from(other: SStringCow<'s>) -> Self {
-        other.into_owned()
-    }
-}
-
-impl<'s> From<&'s SStringCow<'s>> for SString {
-    fn from(other: &'s SStringCow<'s>) -> Self {
-        other.clone().into_owned()
-    }
-}
-
-impl From<StdString> for SString {
-    fn from(other: StdString) -> Self {
-        SString {
-            inner: SStringInner::Owned(other),
-        }
-    }
-}
-
-impl From<&'static str> for SString {
-    fn from(other: &'static str) -> Self {
-        SString {
-            inner: SStringInner::Singleton(other),
-        }
-    }
-}
-
-mod serde_string {
-    use super::*;
-    use serde::{self, Deserialize, Deserializer, Serializer};
-
-    pub(crate) fn serialize<S>(data: &SStringInner, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        let s = match data {
-            SStringInner::Owned(ref s) => s.as_str(),
-            SStringInner::Singleton(ref s) => s,
-        };
-        serializer.serialize_str(&s)
-    }
-
-    pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result<SStringInner, D::Error>
-    where
-        D: Deserializer<'de>,
-    {
-        let s = StdString::deserialize(deserializer)?;
-        Ok(SStringInner::Owned(s))
-    }
-}
diff --git a/crates/value/Cargo.toml b/crates/value/Cargo.toml
index 60940ec..0dfc4b8 100644
--- a/crates/value/Cargo.toml
+++ b/crates/value/Cargo.toml
@@ -20,7 +20,7 @@ num-traits = "0.2"
 # Exposed in API
 chrono = "0.4"
 serde = { version = "1.0", features = ["derive"] }
-sstring = { version = "0.1.0", path = "../sstring" }
+kstring = { version = "0.1.0", path = "../kstring" }
 liquid-error = { version = "^0.19", path = "../error" }
 
 [dev-dependencies]
diff --git a/crates/value/src/lib.rs b/crates/value/src/lib.rs
index 3bf231d..d15e7e7 100644
--- a/crates/value/src/lib.rs
+++ b/crates/value/src/lib.rs
@@ -24,8 +24,8 @@ pub mod error {
 }
 
 /// String-type optimized for `Value`
-pub mod sstring {
-    pub use sstring::*;
+pub mod kstring {
+    pub use kstring::*;
 }
 
 pub use crate::date::*;
diff --git a/crates/value/src/macros.rs b/crates/value/src/macros.rs
index a198b76..3326184 100644
--- a/crates/value/src/macros.rs
+++ b/crates/value/src/macros.rs
@@ -12,7 +12,7 @@
 ///     .to_integer().unwrap();
 /// liquid_value!("foo")
 ///     .as_scalar().unwrap()
-///     .to_sstr();
+///     .to_kstr();
 /// liquid_value!([1, 2, 3])
 ///     .as_array().unwrap();
 /// liquid_value!({"foo": 5})
diff --git a/crates/value/src/map.rs b/crates/value/src/map.rs
index 66ba898..7092ccb 100644
--- a/crates/value/src/map.rs
+++ b/crates/value/src/map.rs
@@ -19,7 +19,7 @@ pub struct Map {
     map: MapImpl<Key, Value>,
 }
 
-type Key = sstring::SString;
+type Key = kstring::KString;
 
 type MapImpl<K, V> = hash_map::HashMap<K, V>;
 type VacantEntryImpl<'a> = hash_map::VacantEntry<'a, Key, Value>;
@@ -212,9 +212,9 @@ impl Eq for Map {}
 /// # let val = &Value::scalar("");
 /// # let _ =
 /// match *val {
-///     Value::Scalar(ref s) => Some(s.to_sstr()),
-///     Value::Array(ref arr) => Some(arr[0].to_sstr()),
-///     Value::Object(ref map) => Some(map["type"].to_sstr()),
+///     Value::Scalar(ref s) => Some(s.to_kstr()),
+///     Value::Array(ref arr) => Some(arr[0].to_kstr()),
+///     Value::Object(ref map) => Some(map["type"].to_kstr()),
 ///     _ => None,
 /// }
 /// # ;
diff --git a/crates/value/src/scalar.rs b/crates/value/src/scalar.rs
index de0fe7d..d0d611c 100644
--- a/crates/value/src/scalar.rs
+++ b/crates/value/src/scalar.rs
@@ -1,9 +1,9 @@
 use std::cmp::Ordering;
 use std::fmt;
 
-use sstring::SString;
-use sstring::SStringCow;
-use sstring::SStringRef;
+use kstring::KString;
+use kstring::KStringCow;
+use kstring::KStringRef;
 
 use crate::{Date, DateTime};
 
@@ -25,7 +25,7 @@ enum ScalarCowEnum<'s> {
     Bool(bool),
     DateTime(DateTime),
     Date(Date),
-    Str(SStringCow<'s>),
+    Str(KStringCow<'s>),
 }
 
 impl<'s> ScalarCow<'s> {
@@ -65,7 +65,7 @@ impl<'s> ScalarCow<'s> {
     }
 
     /// Interpret as a string.
-    pub fn to_sstr(&self) -> SStringCow<'_> {
+    pub fn to_kstr(&self) -> KStringCow<'_> {
         match self.0 {
             ScalarCowEnum::Integer(_)
             | ScalarCowEnum::Float(_)
@@ -77,7 +77,7 @@ impl<'s> ScalarCow<'s> {
     }
 
     /// Convert to a string.
-    pub fn into_string(self) -> SString {
+    pub fn into_string(self) -> KString {
         match self.0 {
             ScalarCowEnum::Integer(x) => x.to_string().into(),
             ScalarCowEnum::Float(x) => x.to_string().into(),
@@ -234,48 +234,48 @@ impl<'s> From<Date> for ScalarCow<'s> {
     }
 }
 
-impl<'s> From<SString> for ScalarCow<'s> {
-    fn from(s: SString) -> Self {
+impl<'s> From<KString> for ScalarCow<'s> {
+    fn from(s: KString) -> Self {
         ScalarCow {
             0: ScalarCowEnum::Str(s.into()),
         }
     }
 }
 
-impl<'s> From<&'s SString> for ScalarCow<'s> {
-    fn from(s: &'s SString) -> Self {
+impl<'s> From<&'s KString> for ScalarCow<'s> {
+    fn from(s: &'s KString) -> Self {
         ScalarCow {
             0: ScalarCowEnum::Str(s.as_ref().into()),
         }
     }
 }
 
-impl<'s> From<SStringRef<'s>> for ScalarCow<'s> {
-    fn from(s: SStringRef<'s>) -> Self {
+impl<'s> From<KStringRef<'s>> for ScalarCow<'s> {
+    fn from(s: KStringRef<'s>) -> Self {
         ScalarCow {
             0: ScalarCowEnum::Str(s.into()),
         }
     }
 }
 
-impl<'s> From<&'s SStringRef<'s>> for ScalarCow<'s> {
-    fn from(s: &'s SStringRef<'s>) -> Self {
+impl<'s> From<&'s KStringRef<'s>> for ScalarCow<'s> {
+    fn from(s: &'s KStringRef<'s>) -> Self {
         ScalarCow {
             0: ScalarCowEnum::Str(s.into()),
         }
     }
 }
 
-impl<'s> From<SStringCow<'s>> for ScalarCow<'s> {
-    fn from(s: SStringCow<'s>) -> Self {
+impl<'s> From<KStringCow<'s>> for ScalarCow<'s> {
+    fn from(s: KStringCow<'s>) -> Self {
         ScalarCow {
             0: ScalarCowEnum::Str(s),
         }
     }
 }
 
-impl<'s> From<&'s SStringCow<'s>> for ScalarCow<'s> {
-    fn from(s: &'s SStringCow<'s>) -> Self {
+impl<'s> From<&'s KStringCow<'s>> for ScalarCow<'s> {
+    fn from(s: &'s KStringCow<'s>) -> Self {
         ScalarCow {
             0: ScalarCowEnum::Str(s.clone()),
         }
@@ -368,22 +368,22 @@ impl<'s> PartialEq<String> for ScalarCow<'s> {
     }
 }
 
-impl<'s> PartialEq<SString> for ScalarCow<'s> {
-    fn eq(&self, other: &SString) -> bool {
+impl<'s> PartialEq<KString> for ScalarCow<'s> {
+    fn eq(&self, other: &KString) -> bool {
         let other = other.into();
         scalar_eq(self, &other)
     }
 }
 
-impl<'s> PartialEq<SStringRef<'s>> for ScalarCow<'s> {
-    fn eq(&self, other: &SStringRef<'s>) -> bool {
+impl<'s> PartialEq<KStringRef<'s>> for ScalarCow<'s> {
+    fn eq(&self, other: &KStringRef<'s>) -> bool {
         let other = other.into();
         scalar_eq(self, &other)
     }
 }
 
-impl<'s> PartialEq<SStringCow<'s>> for ScalarCow<'s> {
-    fn eq(&self, other: &SStringCow<'s>) -> bool {
+impl<'s> PartialEq<KStringCow<'s>> for ScalarCow<'s> {
+    fn eq(&self, other: &KStringCow<'s>) -> bool {
         let other = other.into();
         scalar_eq(self, &other)
     }
@@ -517,28 +517,28 @@ mod test {
 
     #[test]
     fn test_to_str_bool() {
-        assert_eq!(TRUE.to_sstr(), "true");
+        assert_eq!(TRUE.to_kstr(), "true");
     }
 
     #[test]
     fn test_to_str_integer() {
         let val: ScalarCow<'_> = 42i32.into();
-        assert_eq!(val.to_sstr(), "42");
+        assert_eq!(val.to_kstr(), "42");
     }
 
     #[test]
     fn test_to_str_float() {
         let val: ScalarCow<'_> = 42f64.into();
-        assert_eq!(val.to_sstr(), "42");
+        assert_eq!(val.to_kstr(), "42");
 
         let val: ScalarCow<'_> = 42.34.into();
-        assert_eq!(val.to_sstr(), "42.34");
+        assert_eq!(val.to_kstr(), "42.34");
     }
 
     #[test]
     fn test_to_str_str() {
         let val: ScalarCow<'_> = "foobar".into();
-        assert_eq!(val.to_sstr(), "foobar");
+        assert_eq!(val.to_kstr(), "foobar");
     }
 
     #[test]
diff --git a/crates/value/src/values.rs b/crates/value/src/values.rs
index 4ae7a60..ba84616 100644
--- a/crates/value/src/values.rs
+++ b/crates/value/src/values.rs
@@ -1,7 +1,7 @@
 use std::cmp::Ordering;
 use std::fmt;
 
-use sstring::SStringCow;
+use kstring::KStringCow;
 
 use crate::map;
 use crate::Scalar;
@@ -57,11 +57,11 @@ impl<'v> ValueCow<'v> {
     }
 
     /// Interpret as a string.
-    pub fn to_sstr(&self) -> SStringCow<'_> {
+    pub fn to_kstr(&self) -> KStringCow<'_> {
         match self {
-            ValueCow::Scalar(x) => x.to_sstr(),
+            ValueCow::Scalar(x) => x.to_kstr(),
             ValueCow::Array(_) | ValueCow::Object(_) => self.render().to_string().into(),
-            ValueCow::Nil | ValueCow::Empty | ValueCow::Blank => SStringCow::default(),
+            ValueCow::Nil | ValueCow::Empty | ValueCow::Blank => KStringCow::default(),
         }
     }
 
@@ -206,13 +206,13 @@ impl<'v> ValueCow<'v> {
                     let index = convert_index(index, x.len());
                     index < x.len()
                 } else {
-                    match &*index.to_sstr() {
+                    match &*index.to_kstr() {
                         "first" | "last" => true,
                         _ => false,
                     }
                 }
             }
-            ValueCow::Object(ref x) => x.contains_key(index.to_sstr().as_str()),
+            ValueCow::Object(ref x) => x.contains_key(index.to_kstr().as_str()),
             _ => false,
         }
     }
@@ -240,14 +240,14 @@ impl<'v> ValueCow<'v> {
                     let index = convert_index(index, x.len());
                     x.get(index as usize)
                 } else {
-                    match &*index.to_sstr() {
+                    match &*index.to_kstr() {
                         "first" => x.get(0),
                         "last" => x.get(x.len() - 1),
                         _ => None,
                     }
                 }
             }
-            ValueCow::Object(ref x) => x.get(index.to_sstr().as_str()),
+            ValueCow::Object(ref x) => x.get(index.to_kstr().as_str()),
             _ => None,
         }
     }
@@ -350,20 +350,20 @@ impl<'s> PartialEq<String> for Value {
     }
 }
 
-impl PartialEq<sstring::SString> for Value {
-    fn eq(&self, other: &sstring::SString) -> bool {
+impl PartialEq<kstring::KString> for Value {
+    fn eq(&self, other: &kstring::KString) -> bool {
         value_eq(self, &ValueCow::scalar(other))
     }
 }
 
-impl<'s> PartialEq<sstring::SStringRef<'s>> for Value {
-    fn eq(&self, other: &sstring::SStringRef<'s>) -> bool {
+impl<'s> PartialEq<kstring::KStringRef<'s>> for Value {
+    fn eq(&self, other: &kstring::KStringRef<'s>) -> bool {
         value_eq(self, &ValueCow::scalar(other))
     }
 }
 
-impl<'s> PartialEq<sstring::SStringCow<'s>> for Value {
-    fn eq(&self, other: &sstring::SStringCow<'s>) -> bool {
+impl<'s> PartialEq<kstring::KStringCow<'s>> for Value {
+    fn eq(&self, other: &kstring::KStringCow<'s>) -> bool {
         value_eq(self, &ValueCow::scalar(other))
     }
 }
@@ -480,7 +480,7 @@ mod test {
     fn test_to_string_scalar() {
         let val = ValueCow::scalar(42f64);
         assert_eq!(&val.render().to_string(), "42");
-        assert_eq!(&val.to_sstr(), "42");
+        assert_eq!(&val.to_kstr(), "42");
     }
 
     #[test]
@@ -491,7 +491,7 @@ mod test {
             ValueCow::scalar(5.3),
         ]);
         assert_eq!(&val.render().to_string(), "3test5.3");
-        assert_eq!(&val.to_sstr(), "3test5.3");
+        assert_eq!(&val.to_kstr(), "3test5.3");
     }
 
     // TODO make a test for object, remember values are in arbitrary orders in HashMaps
@@ -499,7 +499,7 @@ mod test {
     #[test]
     fn test_to_string_nil() {
         assert_eq!(&ValueCow::Nil.render().to_string(), "");
-        assert_eq!(&ValueCow::Nil.to_sstr(), "");
+        assert_eq!(&ValueCow::Nil.to_kstr(), "");
     }
 
     #[test]
diff --git a/src/filters/jekyll/array.rs b/src/filters/jekyll/array.rs
index 41c9a1d..07a3c83 100644
--- a/src/filters/jekyll/array.rs
+++ b/src/filters/jekyll/array.rs
@@ -170,7 +170,7 @@ impl Filter for ArrayToSentenceStringFilter {
 
         let mut sentence = array
             .next()
-            .map(|v| v.to_sstr().into_mut())
+            .map(|v| v.to_kstr().into_string())
             .unwrap_or_else(|| "".to_string());
 
         let last = array.next_back();
diff --git a/src/filters/jekyll/slugify.rs b/src/filters/jekyll/slugify.rs
index c0849f0..4916aa3 100644
--- a/src/filters/jekyll/slugify.rs
+++ b/src/filters/jekyll/slugify.rs
@@ -69,7 +69,7 @@ impl Filter for SlugifyFilter {
     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let s = input.to_sstr();
+        let s = input.to_kstr();
         let mode = args
             .mode
             .map(|mode| SlugifyMode::new(mode.as_str()))
diff --git a/src/filters/mod.rs b/src/filters/mod.rs
index 43cc1b3..5573684 100644
--- a/src/filters/mod.rs
+++ b/src/filters/mod.rs
@@ -10,14 +10,14 @@ pub mod extra;
 
 pub fn invalid_input<S>(cause: S) -> Error
 where
-    S: Into<sstring::SString>,
+    S: Into<kstring::KString>,
 {
     Error::with_msg("Invalid input").context("cause", cause)
 }
 
 pub fn invalid_argument<S>(argument: S, cause: S) -> Error
 where
-    S: Into<sstring::SString>,
+    S: Into<kstring::KString>,
 {
     Error::with_msg("Invalid argument")
         .context("argument", argument)
diff --git a/src/filters/std/array.rs b/src/filters/std/array.rs
index 74b2650..4aab259 100644
--- a/src/filters/std/array.rs
+++ b/src/filters/std/array.rs
@@ -52,7 +52,7 @@ impl Filter for JoinFilter {
         let input = input
             .as_array()
             .ok_or_else(|| invalid_input("Array of strings expected"))?;
-        let input = input.iter().map(|x| x.to_sstr());
+        let input = input.iter().map(|x| x.to_kstr());
 
         Ok(Value::scalar(itertools::join(input, separator.as_str())))
     }
@@ -70,7 +70,7 @@ fn nil_safe_compare(a: &Value, b: &Value) -> Option<cmp::Ordering> {
 fn nil_safe_casecmp_key(value: &Value) -> Option<String> {
     match value {
         Value::Nil => None,
-        value => Some(value.to_sstr().to_lowercase()),
+        value => Some(value.to_kstr().to_lowercase()),
     }
 }
 
@@ -460,7 +460,7 @@ impl Filter for FirstFilter {
         match *input {
             Value::Scalar(ref x) => {
                 let c = x
-                    .to_sstr()
+                    .to_kstr()
                     .chars()
                     .next()
                     .map(|c| c.to_string())
@@ -490,7 +490,7 @@ impl Filter for LastFilter {
         match *input {
             Value::Scalar(ref x) => {
                 let c = x
-                    .to_sstr()
+                    .to_kstr()
                     .chars()
                     .last()
                     .map(|c| c.to_string())
diff --git a/src/filters/std/html.rs b/src/filters/std/html.rs
index 6484c74..8240ef4 100644
--- a/src/filters/std/html.rs
+++ b/src/filters/std/html.rs
@@ -22,7 +22,7 @@ fn escape(input: &Value, once_p: bool) -> Result<Value> {
     if input.is_nil() {
         return Ok(Value::Nil);
     }
-    let s = input.to_sstr();
+    let s = input.to_kstr();
     let mut result = String::new();
     let mut last = 0;
     let mut skip = 0;
@@ -122,7 +122,7 @@ static MATCHERS: once_cell::sync::Lazy<[Regex; 4]> = once_cell::sync::Lazy::new(
 
 impl Filter for StripHtmlFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-        let input = input.to_sstr().into_mut();
+        let input = input.to_kstr().into_string();
 
         let result = MATCHERS.iter().fold(input, |acc, matcher| {
             matcher.replace_all(&acc, "").into_owned()
@@ -146,7 +146,7 @@ struct NewlineToBrFilter;
 impl Filter for NewlineToBrFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
         // TODO handle windows line endings
-        let input = input.to_sstr();
+        let input = input.to_kstr();
         Ok(Value::scalar(input.replace("\n", "<br />\n")))
     }
 }
diff --git a/src/filters/std/mod.rs b/src/filters/std/mod.rs
index cafdd42..29e6ebb 100644
--- a/src/filters/std/mod.rs
+++ b/src/filters/std/mod.rs
@@ -44,7 +44,7 @@ struct SizeFilter;
 impl Filter for SizeFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
         match *input {
-            Value::Scalar(ref x) => Ok(Value::scalar(x.to_sstr().len() as i32)),
+            Value::Scalar(ref x) => Ok(Value::scalar(x.to_kstr().len() as i32)),
             Value::Array(ref x) => Ok(Value::scalar(x.len() as i32)),
             Value::Object(ref x) => Ok(Value::scalar(x.len() as i32)),
             _ => Ok(Value::scalar(0i32)),
diff --git a/src/filters/std/slice.rs b/src/filters/std/slice.rs
index 177b82d..1ff845b 100644
--- a/src/filters/std/slice.rs
+++ b/src/filters/std/slice.rs
@@ -75,7 +75,7 @@ impl Filter for SliceFilter {
                 input.iter().skip(offset).take(length).cloned(),
             ))
         } else {
-            let input = input.to_sstr();
+            let input = input.to_kstr();
             let (offset, length) = canonicalize_slice(offset, length, input.len());
             Ok(Value::scalar(
                 input.chars().skip(offset).take(length).collect::<String>(),
diff --git a/src/filters/std/string/case.rs b/src/filters/std/string/case.rs
index 88d69fd..51283ce 100644
--- a/src/filters/std/string/case.rs
+++ b/src/filters/std/string/case.rs
@@ -18,7 +18,7 @@ struct DowncaseFilter;
 
 impl Filter for DowncaseFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-        let s = input.to_sstr();
+        let s = input.to_kstr();
         Ok(Value::scalar(s.to_lowercase()))
     }
 }
@@ -37,7 +37,7 @@ struct UpcaseFilter;
 
 impl Filter for UpcaseFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-        let s = input.to_sstr();
+        let s = input.to_kstr();
         Ok(Value::scalar(s.to_uppercase()))
     }
 }
@@ -56,7 +56,7 @@ struct CapitalizeFilter;
 
 impl Filter for CapitalizeFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-        let s = input.to_sstr().to_owned();
+        let s = input.to_kstr().to_owned();
         let mut chars = s.chars();
         let capitalized = match chars.next() {
             Some(first_char) => first_char.to_uppercase().chain(chars).collect(),
diff --git a/src/filters/std/string/mod.rs b/src/filters/std/string/mod.rs
index 42c7b17..158e534 100644
--- a/src/filters/std/string/mod.rs
+++ b/src/filters/std/string/mod.rs
@@ -39,7 +39,7 @@ impl Filter for SplitFilter {
     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let input = input.to_sstr();
+        let input = input.to_kstr();
 
         // Split and construct resulting Array
         Ok(Value::Array(
diff --git a/src/filters/std/string/operate.rs b/src/filters/std/string/operate.rs
index 6c0895c..6f29f59 100644
--- a/src/filters/std/string/operate.rs
+++ b/src/filters/std/string/operate.rs
@@ -36,7 +36,7 @@ impl Filter for ReplaceFilter {
     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let input = input.to_sstr();
+        let input = input.to_kstr();
 
         let replace = args.replace.unwrap_or_else(|| "".into());
 
@@ -77,7 +77,7 @@ impl Filter for ReplaceFirstFilter {
     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let input = input.to_sstr();
+        let input = input.to_kstr();
 
         let search = args.search;
         let replace = args.replace.unwrap_or_else(|| "".into());
@@ -119,7 +119,7 @@ impl Filter for RemoveFilter {
     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let input = input.to_sstr();
+        let input = input.to_kstr();
 
         Ok(Value::scalar(input.replace(args.search.as_str(), "")))
     }
@@ -151,7 +151,7 @@ impl Filter for RemoveFirstFilter {
     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let input = input.to_sstr();
+        let input = input.to_kstr();
 
         Ok(Value::scalar(
             input.splitn(2, args.search.as_str()).collect::<String>(),
@@ -185,7 +185,7 @@ impl Filter for AppendFilter {
     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let mut input = input.to_sstr().into_mut();
+        let mut input = input.to_kstr().into_string();
         input.push_str(args.string.as_str());
 
         Ok(Value::scalar(input))
@@ -218,8 +218,8 @@ impl Filter for PrependFilter {
     fn evaluate(&self, input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let input = input.to_sstr();
-        let mut string = args.string.into_mut();
+        let input = input.to_kstr();
+        let mut string = args.string.into_string();
         string.push_str(input.as_str());
 
         Ok(Value::scalar(string))
diff --git a/src/filters/std/string/strip.rs b/src/filters/std/string/strip.rs
index b2e0b0f..331897e 100644
--- a/src/filters/std/string/strip.rs
+++ b/src/filters/std/string/strip.rs
@@ -25,7 +25,7 @@ struct StripFilter;
 
 impl Filter for StripFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-        let input = input.to_sstr();
+        let input = input.to_kstr();
         Ok(Value::scalar(input.trim().to_owned()))
     }
 }
@@ -50,7 +50,7 @@ struct LstripFilter;
 
 impl Filter for LstripFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-        let input = input.to_sstr();
+        let input = input.to_kstr();
         Ok(Value::scalar(input.trim_start().to_owned()))
     }
 }
@@ -75,7 +75,7 @@ struct RstripFilter;
 
 impl Filter for RstripFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-        let input = input.to_sstr();
+        let input = input.to_kstr();
         Ok(Value::scalar(input.trim_end().to_owned()))
     }
 }
@@ -94,7 +94,7 @@ struct StripNewlinesFilter;
 
 impl Filter for StripNewlinesFilter {
     fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-        let input = input.to_sstr();
+        let input = input.to_kstr();
         Ok(Value::scalar(
             input
                 .chars()
diff --git a/src/filters/std/string/truncate.rs b/src/filters/std/string/truncate.rs
index db13bf2..5950d7c 100644
--- a/src/filters/std/string/truncate.rs
+++ b/src/filters/std/string/truncate.rs
@@ -75,7 +75,7 @@ impl Filter for TruncateFilter {
 
         let l = cmp::max(lenght - truncate_string.len(), 0);
 
-        let input_string = input.to_sstr();
+        let input_string = input.to_kstr();
 
         let result = if lenght < input_string.len() {
             let result = UnicodeSegmentation::graphemes(input_string.as_str(), true)
@@ -133,7 +133,7 @@ impl Filter for TruncateWordsFilter {
 
         let l = cmp::max(words, 0);
 
-        let input_string = input.to_sstr();
+        let input_string = input.to_kstr();
 
         let word_list: Vec<&str> = input_string.split(' ').collect();
         let result = if words < word_list.len() {
diff --git a/src/filters/std/url.rs b/src/filters/std/url.rs
index f7f86c6..819c7c8 100644
--- a/src/filters/std/url.rs
+++ b/src/filters/std/url.rs
@@ -53,7 +53,7 @@ impl Filter for UrlEncodeFilter {
             return Ok(Value::Nil);
         }
 
-        let s = input.to_sstr();
+        let s = input.to_kstr();
 
         let result: String =
             percent_encoding::utf8_percent_encode(s.as_str(), URL_ENCODE_SET.clone()).collect();
@@ -79,7 +79,7 @@ impl Filter for UrlDecodeFilter {
             return Ok(Value::Nil);
         }
 
-        let s = input.to_sstr().replace("+", " ");
+        let s = input.to_kstr().replace("+", " ");
 
         let result = percent_encoding::percent_decode(s.as_bytes())
             .decode_utf8()
diff --git a/src/tags/case_block.rs b/src/tags/case_block.rs
index d05b6ab..ddcd3dc 100644
--- a/src/tags/case_block.rs
+++ b/src/tags/case_block.rs
@@ -66,7 +66,7 @@ impl Renderable for Case {
                     .trace_with(|| case.trace().into())
                     .trace_with(|| self.trace().into())
                     .context_key_with(|| self.target.to_string().into())
-                    .value_with(|| value.to_sstr().into_owned());
+                    .value_with(|| value.to_kstr().into_owned());
             }
         }
 
@@ -76,7 +76,7 @@ impl Renderable for Case {
                 .trace("{{% else %}}")
                 .trace_with(|| self.trace().into())
                 .context_key_with(|| self.target.to_string().into())
-                .value_with(|| value.to_sstr().into_owned());
+                .value_with(|| value.to_kstr().into_owned());
         }
 
         Ok(())
diff --git a/src/tags/cycle_tag.rs b/src/tags/cycle_tag.rs
index be1e92b..c51fcad 100644
--- a/src/tags/cycle_tag.rs
+++ b/src/tags/cycle_tag.rs
@@ -55,7 +55,7 @@ fn parse_cycle(mut arguments: TagTokenIter, _options: &Language) -> Result<Cycle
                 TryMatchToken::Fails(name) => match name.expect_literal() {
                     // This will allow non string literals such as 0 to be parsed as such.
                     // Is this ok or should more specific functions be created?
-                    TryMatchToken::Matches(name) => name.to_sstr().into_mut(),
+                    TryMatchToken::Matches(name) => name.to_kstr().into_string(),
                     TryMatchToken::Fails(name) => return name.raise_error().into_err(),
                 },
             };
diff --git a/src/tags/for_block.rs b/src/tags/for_block.rs
index 1752e40..9d74f70 100644
--- a/src/tags/for_block.rs
+++ b/src/tags/for_block.rs
@@ -145,7 +145,7 @@ fn int_argument(arg: &Expression, context: &Context, arg_name: &str) -> Result<i
         .and_then(Scalar::to_integer)
         .ok_or_else(|| unexpected_value_error("whole number", Some(value.type_name())))
         .context_key_with(|| arg_name.to_owned().into())
-        .value_with(|| value.to_sstr().into_owned())?;
+        .value_with(|| value.to_kstr().into_owned())?;
 
     Ok(value as isize)
 }
@@ -873,7 +873,7 @@ mod test {
 
     impl Filter for ShoutFilter {
         fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
-            Ok(Value::scalar(input.to_sstr().to_uppercase()))
+            Ok(Value::scalar(input.to_kstr().to_uppercase()))
         }
     }
 
diff --git a/src/tags/if_block.rs b/src/tags/if_block.rs
index ba43ec1..79641c9 100644
--- a/src/tags/if_block.rs
+++ b/src/tags/if_block.rs
@@ -153,8 +153,8 @@ struct Conditional {
 fn contains_check(a: &Value, b: &Value) -> Result<bool> {
     match *a {
         Value::Scalar(ref val) => {
-            let b = b.to_sstr();
-            Ok(val.to_sstr().contains(b.as_str()))
+            let b = b.to_kstr();
+            Ok(val.to_kstr().contains(b.as_str()))
         }
         Value::Object(_) => {
             let b = b.as_scalar();
diff --git a/src/tags/include_tag.rs b/src/tags/include_tag.rs
index 40d2b7b..f7824a0 100644
--- a/src/tags/include_tag.rs
+++ b/src/tags/include_tag.rs
@@ -65,8 +65,8 @@ impl ParseTag for IncludeTag {
         // This may accept strange inputs such as `{% include 0 %}` or `{% include filterchain | filter:0 %}`.
         // Those inputs would fail anyway by there being not a path with those names so they are not a big concern.
         let name = match name.expect_literal() {
-            // Using `to_sstr()` on literals ensures `Strings` will have their quotes trimmed.
-            TryMatchToken::Matches(name) => name.to_sstr().to_string(),
+            // Using `to_kstr()` on literals ensures `Strings` will have their quotes trimmed.
+            TryMatchToken::Matches(name) => name.to_kstr().to_string(),
             TryMatchToken::Fails(name) => name.as_str().to_string(),
         };
 
@@ -145,7 +145,7 @@ mod test {
     impl Filter for SizeFilter {
         fn evaluate(&self, input: &Value, _context: &Context) -> Result<Value> {
             match *input {
-                Value::Scalar(ref x) => Ok(Value::scalar(x.to_sstr().len() as i32)),
+                Value::Scalar(ref x) => Ok(Value::scalar(x.to_kstr().len() as i32)),
                 Value::Array(ref x) => Ok(Value::scalar(x.len() as i32)),
                 Value::Object(ref x) => Ok(Value::scalar(x.len() as i32)),
                 _ => Ok(Value::scalar(0i32)),


Test patch:
diff --git a/tests/conformance_ruby/tags/standard_tag_test.rs b/tests/conformance_ruby/tags/standard_tag_test.rs
index d39de63..1c864f7 100644
--- a/tests/conformance_ruby/tags/standard_tag_test.rs
+++ b/tests/conformance_ruby/tags/standard_tag_test.rs
@@ -384,8 +384,10 @@ fn test_cycle() {
         r#"{%cycle "one", "two"%} {%cycle "one", "two"%} {%cycle "one", "two"%}"#
     );
 
-    assert_template_result!("text-align: left text-align: right",
-      r#"{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}"#);
+    assert_template_result!(
+        "text-align: left text-align: right",
+        r#"{%cycle "text-align: left", "text-align: right" %} {%cycle "text-align: left", "text-align: right"%}"#
+    );
 }
 
 #[test]
@@ -396,15 +398,20 @@ fn test_multiple_cycles() {
 
 #[test]
 fn test_multiple_named_cycles() {
-    assert_template_result!("one one two two one one",
-      r#"{%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %}"#);
+    assert_template_result!(
+        "one one two two one one",
+        r#"{%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %} {%cycle 1: "one", "two" %} {%cycle 2: "one", "two" %}"#
+    );
 }
 
 #[test]
 fn test_multiple_named_cycles_with_names_from_context() {
     let assigns = v!({ "var1": 1, "var2": 2 });
-    assert_template_result!("one one two two one one",
-      r#"{%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %}"#, assigns);
+    assert_template_result!(
+        "one one two two one one",
+        r#"{%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %} {%cycle var1: "one", "two" %} {%cycle var2: "one", "two" %}"#,
+        assigns
+    );
 }
 
 #[test]
diff --git a/tests/derive_macros_test_filters/mixed.rs b/tests/derive_macros_test_filters/mixed.rs
index 26ce9ff..5732e00 100644
--- a/tests/derive_macros_test_filters/mixed.rs
+++ b/tests/derive_macros_test_filters/mixed.rs
@@ -61,7 +61,7 @@ impl Filter for TestMixedFilter {
         let d = args.d.to_string();
         let e = args.e.to_string();
         let f = args.f.map(|i| i.to_string()).unwrap_or("None".to_string());
-        let g = args.g.to_sstr();
+        let g = args.g.to_kstr();
 
         let result = format!(
             "<a: {}; b: {}; c: {}, d: {}, e: {}, f: {}, type: {}>",
diff --git a/tests/derive_macros_test_filters/positional.rs b/tests/derive_macros_test_filters/positional.rs
index d74239e..1e0374b 100644
--- a/tests/derive_macros_test_filters/positional.rs
+++ b/tests/derive_macros_test_filters/positional.rs
@@ -38,7 +38,7 @@ impl Filter for TestPositionalFilter {
     fn evaluate(&self, _input: &Value, context: &Context) -> Result<Value> {
         let args = self.args.evaluate(context)?;
 
-        let pos1 = args.pos1.to_sstr();
+        let pos1 = args.pos1.to_kstr();
         let result = if let Some(pos2) = args.pos2 {
             format!("<pos1: {}; pos2: {}>", pos1, pos2)
         } else {