Merge pull request #814 from hyperium/headers-vec-map

perf(headers): use a VecMap, and check name against literals
This commit is contained in:
Sean McArthur
2016-06-01 15:51:00 -07:00
19 changed files with 210 additions and 61 deletions

View File

@@ -42,7 +42,8 @@ const ACCESS_CONTROL_ALLOW_CREDENTIALS_TRUE: UniCase<&'static str> = UniCase("tr
impl Header for AccessControlAllowCredentials { impl Header for AccessControlAllowCredentials {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Access-Control-Allow-Credentials" static NAME: &'static str = "Access-Control-Allow-Credentials";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowCredentials> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<AccessControlAllowCredentials> {

View File

@@ -73,7 +73,8 @@ impl<S: Scheme> DerefMut for Authorization<S> {
impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'static { impl<S: Scheme + Any> Header for Authorization<S> where <S as FromStr>::Err: 'static {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Authorization" static NAME: &'static str = "Authorization";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<Authorization<S>> {

View File

@@ -51,7 +51,8 @@ __hyper__deref!(CacheControl => Vec<CacheDirective>);
impl Header for CacheControl { impl Header for CacheControl {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Cache-Control" static NAME: &'static str = "Cache-Control";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<CacheControl> {

View File

@@ -90,7 +90,8 @@ pub struct ContentDisposition {
impl Header for ContentDisposition { impl Header for ContentDisposition {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Content-Disposition" static NAME: &'static str = "Content-Disposition";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentDisposition> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentDisposition> {

View File

@@ -32,10 +32,13 @@ use header::{Header, parsing};
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct ContentLength(pub u64); pub struct ContentLength(pub u64);
//static NAME: &'static str = "Content-Length";
impl Header for ContentLength { impl Header for ContentLength {
#[inline] #[inline]
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Content-Length" static NAME: &'static str = "Content-Length";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentLength> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<ContentLength> {
// If multiple Content-Length headers were sent, everything can still // If multiple Content-Length headers were sent, everything can still

View File

@@ -39,7 +39,8 @@ __hyper__deref!(Cookie => Vec<CookiePair>);
impl Header for Cookie { impl Header for Cookie {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Cookie" static NAME: &'static str = "Cookie";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Cookie> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<Cookie> {

View File

@@ -30,7 +30,8 @@ const EXPECT_CONTINUE: UniCase<&'static str> = UniCase("100-continue");
impl Header for Expect { impl Header for Expect {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Expect" static NAME: &'static str = "Expect";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<Expect> {

View File

@@ -43,7 +43,8 @@ pub struct Host {
impl Header for Host { impl Header for Host {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Host" static NAME: &'static str = "Host";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<Host> {

View File

@@ -55,7 +55,8 @@ pub enum IfRange {
impl Header for IfRange { impl Header for IfRange {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"If-Range" static NAME: &'static str = "If-Range";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<IfRange> {
let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw); let etag: ::Result<EntityTag> = header::parsing::from_one_raw_str(raw);

View File

@@ -217,7 +217,8 @@ macro_rules! header {
__hyper__deref!($id => Vec<$item>); __hyper__deref!($id => Vec<$item>);
impl $crate::header::Header for $id { impl $crate::header::Header for $id {
fn header_name() -> &'static str { fn header_name() -> &'static str {
$n static NAME: &'static str = $n;
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
$crate::header::parsing::from_comma_delimited(raw).map($id) $crate::header::parsing::from_comma_delimited(raw).map($id)
@@ -243,7 +244,8 @@ macro_rules! header {
__hyper__deref!($id => Vec<$item>); __hyper__deref!($id => Vec<$item>);
impl $crate::header::Header for $id { impl $crate::header::Header for $id {
fn header_name() -> &'static str { fn header_name() -> &'static str {
$n static NAME: &'static str = $n;
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
$crate::header::parsing::from_comma_delimited(raw).map($id) $crate::header::parsing::from_comma_delimited(raw).map($id)
@@ -268,7 +270,8 @@ macro_rules! header {
__hyper__deref!($id => $value); __hyper__deref!($id => $value);
impl $crate::header::Header for $id { impl $crate::header::Header for $id {
fn header_name() -> &'static str { fn header_name() -> &'static str {
$n static NAME: &'static str = $n;
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
$crate::header::parsing::from_one_raw_str(raw).map($id) $crate::header::parsing::from_one_raw_str(raw).map($id)
@@ -296,7 +299,8 @@ macro_rules! header {
} }
impl $crate::header::Header for $id { impl $crate::header::Header for $id {
fn header_name() -> &'static str { fn header_name() -> &'static str {
$n static NAME: &'static str = $n;
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> { fn parse_header(raw: &[Vec<u8>]) -> $crate::Result<Self> {
// FIXME: Return None if no item is in $id::Only // FIXME: Return None if no item is in $id::Only

View File

@@ -40,7 +40,8 @@ pub enum Pragma {
impl Header for Pragma { impl Header for Pragma {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Pragma" static NAME: &'static str = "Pragma";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Pragma> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<Pragma> {

View File

@@ -52,7 +52,8 @@ __hyper__deref!(Prefer => Vec<Preference>);
impl Header for Prefer { impl Header for Prefer {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Prefer" static NAME: &'static str = "Prefer";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Prefer> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<Prefer> {

View File

@@ -50,7 +50,8 @@ __hyper__deref!(PreferenceApplied => Vec<Preference>);
impl Header for PreferenceApplied { impl Header for PreferenceApplied {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Preference-Applied" static NAME: &'static str = "Preference-Applied";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<PreferenceApplied> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<PreferenceApplied> {

View File

@@ -176,7 +176,8 @@ impl FromStr for ByteRangeSpec {
impl Header for Range { impl Header for Range {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Range" static NAME: &'static str = "Range";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<Range> {

View File

@@ -84,7 +84,8 @@ __hyper__deref!(SetCookie => Vec<CookiePair>);
impl Header for SetCookie { impl Header for SetCookie {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Set-Cookie" static NAME: &'static str = "Set-Cookie";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<SetCookie> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<SetCookie> {

View File

@@ -121,7 +121,8 @@ impl FromStr for StrictTransportSecurity {
impl Header for StrictTransportSecurity { impl Header for StrictTransportSecurity {
fn header_name() -> &'static str { fn header_name() -> &'static str {
"Strict-Transport-Security" static NAME: &'static str = "Strict-Transport-Security";
NAME
} }
fn parse_header(raw: &[Vec<u8>]) -> ::Result<StrictTransportSecurity> { fn parse_header(raw: &[Vec<u8>]) -> ::Result<StrictTransportSecurity> {

View File

@@ -1,4 +1,6 @@
pub use self::item::Item; pub use self::item::Item;
pub use self::vec_map::{VecMap, Entry};
mod cell; mod cell;
mod item; mod item;
mod vec_map;

View File

@@ -0,0 +1,89 @@
#[derive(Clone)]
pub struct VecMap<K, V> {
vec: Vec<(K, V)>,
}
impl<K: PartialEq, V> VecMap<K, V> {
pub fn new() -> VecMap<K, V> {
VecMap {
vec: Vec::new()
}
}
pub fn insert(&mut self, key: K, value: V) {
match self.find(&key) {
Some(pos) => self.vec[pos] = (key, value),
None => self.vec.push((key, value))
}
}
pub fn entry(&mut self, key: K) -> Entry<K, V> {
match self.find(&key) {
Some(pos) => Entry::Occupied(OccupiedEntry {
vec: self,
pos: pos,
}),
None => Entry::Vacant(VacantEntry {
vec: self,
key: key,
})
}
}
pub fn get(&self, key: &K) -> Option<&V> {
self.find(key).map(move |pos| &self.vec[pos].1)
}
pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
self.find(key).map(move |pos| &mut self.vec[pos].1)
}
pub fn contains_key(&self, key: &K) -> bool {
self.find(key).is_some()
}
pub fn len(&self) -> usize { self.vec.len() }
pub fn iter(&self) -> ::std::slice::Iter<(K, V)> {
self.vec.iter()
}
pub fn remove(&mut self, key: &K) -> Option<V> {
self.find(key).map(|pos| self.vec.remove(pos)).map(|(_, v)| v)
}
pub fn clear(&mut self) {
self.vec.clear();
}
fn find(&self, key: &K) -> Option<usize> {
self.vec.iter().position(|entry| key == &entry.0)
}
}
pub enum Entry<'a, K: 'a, V: 'a> {
Vacant(VacantEntry<'a, K, V>),
Occupied(OccupiedEntry<'a, K, V>)
}
pub struct VacantEntry<'a, K: 'a, V: 'a> {
vec: &'a mut VecMap<K, V>,
key: K,
}
impl<'a, K, V> VacantEntry<'a, K, V> {
pub fn insert(self, val: V) -> &'a mut V {
let mut vec = self.vec;
vec.vec.push((self.key, val));
let pos = vec.vec.len() - 1;
&mut vec.vec[pos].1
}
}
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
vec: &'a mut VecMap<K, V>,
pos: usize,
}
impl<'a, K, V> OccupiedEntry<'a, K, V> {
pub fn into_mut(self) -> &'a mut V {
&mut self.vec.vec[self.pos].1
}
}

View File

@@ -77,17 +77,16 @@
//! ``` //! ```
use std::any::Any; use std::any::Any;
use std::borrow::{Cow, ToOwned}; use std::borrow::{Cow, ToOwned};
use std::collections::HashMap; //use std::collections::HashMap;
use std::collections::hash_map::{Iter, Entry}; //use std::collections::hash_map::{Iter, Entry};
use std::iter::{FromIterator, IntoIterator}; use std::iter::{FromIterator, IntoIterator};
use std::ops::{Deref, DerefMut};
use std::{mem, fmt}; use std::{mem, fmt};
use {httparse, traitobject}; use {httparse, traitobject};
use typeable::Typeable; use typeable::Typeable;
use unicase::UniCase; use unicase::UniCase;
use self::internals::Item; use self::internals::{Item, VecMap, Entry};
#[cfg(feature = "serde-serialization")] #[cfg(feature = "serde-serialization")]
use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer};
@@ -104,7 +103,6 @@ mod internals;
mod shared; mod shared;
pub mod parsing; pub mod parsing;
type HeaderName = UniCase<CowStr>;
/// A trait for any object that will represent a header field and value. /// A trait for any object that will represent a header field and value.
/// ///
@@ -169,7 +167,8 @@ fn header_name<T: Header>() -> &'static str {
/// A map of header fields on requests and responses. /// A map of header fields on requests and responses.
#[derive(Clone)] #[derive(Clone)]
pub struct Headers { pub struct Headers {
data: HashMap<HeaderName, Item> //data: HashMap<HeaderName, Item>
data: VecMap<HeaderName, Item>,
} }
impl Default for Headers { impl Default for Headers {
@@ -178,12 +177,58 @@ impl Default for Headers {
} }
} }
macro_rules! literals {
($($len:expr => $($header:path),+;)+) => (
fn maybe_literal(s: &str) -> Cow<'static, str> {
match s.len() {
$($len => {
$(
if UniCase(<$header>::header_name()) == s {
return Cow::Borrowed(<$header>::header_name());
}
)+
})+
_ => ()
}
Cow::Owned(s.to_owned())
}
#[test]
fn test_literal_lens() {
$(
$({
let s = <$header>::header_name();
assert!(s.len() == $len, "{:?} has len of {}, listed as {}", s, s.len(), $len);
})+
)+
}
);
}
literals! {
4 => Host, Date, ETag;
5 => Allow, Range;
6 => Accept, Cookie, Server, Expect;
7 => Upgrade, Referer, Expires;
8 => Location, IfMatch, IfRange;
10 => UserAgent, Connection, SetCookie;
12 => ContentType;
13 => Authorization<String>, CacheControl, LastModified, IfNoneMatch, AcceptRanges, ContentRange;
14 => ContentLength, AcceptCharset;
15 => AcceptEncoding, AcceptLanguage;
17 => TransferEncoding;
25 => StrictTransportSecurity;
27 => AccessControlAllowOrigin;
}
impl Headers { impl Headers {
/// Creates a new, empty headers map. /// Creates a new, empty headers map.
pub fn new() -> Headers { pub fn new() -> Headers {
Headers { Headers {
data: HashMap::new() data: VecMap::new()
} }
} }
@@ -192,7 +237,7 @@ impl Headers {
let mut headers = Headers::new(); let mut headers = Headers::new();
for header in raw { for header in raw {
trace!("raw header: {:?}={:?}", header.name, &header.value[..]); trace!("raw header: {:?}={:?}", header.name, &header.value[..]);
let name = UniCase(CowStr(Cow::Owned(header.name.to_owned()))); let name = HeaderName(UniCase(maybe_literal(header.name)));
let mut item = match headers.data.entry(name) { let mut item = match headers.data.entry(name) {
Entry::Vacant(entry) => entry.insert(Item::new_raw(vec![])), Entry::Vacant(entry) => entry.insert(Item::new_raw(vec![])),
Entry::Occupied(entry) => entry.into_mut() Entry::Occupied(entry) => entry.into_mut()
@@ -209,7 +254,7 @@ impl Headers {
/// The field is determined by the type of the value being set. /// The field is determined by the type of the value being set.
pub fn set<H: Header>(&mut self, value: H) { pub fn set<H: Header>(&mut self, value: H) {
trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), HeaderFormatter(&value)); trace!("Headers.set( {:?}, {:?} )", header_name::<H>(), HeaderFormatter(&value));
self.data.insert(UniCase(CowStr(Cow::Borrowed(header_name::<H>()))), self.data.insert(HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))),
Item::new_typed(Box::new(value))); Item::new_typed(Box::new(value)));
} }
@@ -226,7 +271,7 @@ impl Headers {
/// ``` /// ```
pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> { pub fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
self.data self.data
.get(&UniCase(CowStr(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))) .get(&HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))))
.map(Item::raw) .map(Item::raw)
} }
@@ -242,26 +287,26 @@ impl Headers {
pub fn set_raw<K: Into<Cow<'static, str>> + fmt::Debug>(&mut self, name: K, pub fn set_raw<K: Into<Cow<'static, str>> + fmt::Debug>(&mut self, name: K,
value: Vec<Vec<u8>>) { value: Vec<Vec<u8>>) {
trace!("Headers.set_raw( {:?}, {:?} )", name, value); trace!("Headers.set_raw( {:?}, {:?} )", name, value);
self.data.insert(UniCase(CowStr(name.into())), Item::new_raw(value)); self.data.insert(HeaderName(UniCase(name.into())), Item::new_raw(value));
} }
/// Remove a header set by set_raw /// Remove a header set by set_raw
pub fn remove_raw(&mut self, name: &str) { pub fn remove_raw(&mut self, name: &str) {
trace!("Headers.remove_raw( {:?} )", name); trace!("Headers.remove_raw( {:?} )", name);
self.data.remove( self.data.remove(
&UniCase(CowStr(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) }))) &HeaderName(UniCase(Cow::Borrowed(unsafe { mem::transmute::<&str, &str>(name) })))
); );
} }
/// Get a reference to the header field's value, if it exists. /// Get a reference to the header field's value, if it exists.
pub fn get<H: Header>(&self) -> Option<&H> { pub fn get<H: Header>(&self) -> Option<&H> {
self.data.get(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) self.data.get(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))))
.and_then(Item::typed::<H>) .and_then(Item::typed::<H>)
} }
/// Get a mutable reference to the header field's value, if it exists. /// Get a mutable reference to the header field's value, if it exists.
pub fn get_mut<H: Header>(&mut self) -> Option<&mut H> { pub fn get_mut<H: Header>(&mut self) -> Option<&mut H> {
self.data.get_mut(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) self.data.get_mut(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))))
.and_then(Item::typed_mut::<H>) .and_then(Item::typed_mut::<H>)
} }
@@ -276,14 +321,14 @@ impl Headers {
/// let has_type = headers.has::<ContentType>(); /// let has_type = headers.has::<ContentType>();
/// ``` /// ```
pub fn has<H: Header>(&self) -> bool { pub fn has<H: Header>(&self) -> bool {
self.data.contains_key(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))) self.data.contains_key(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))))
} }
/// Removes a header from the map, if one existed. /// Removes a header from the map, if one existed.
/// Returns true if a header has been removed. /// Returns true if a header has been removed.
pub fn remove<H: Header>(&mut self) -> bool { pub fn remove<H: Header>(&mut self) -> bool {
trace!("Headers.remove( {:?} )", header_name::<H>()); trace!("Headers.remove( {:?} )", header_name::<H>());
self.data.remove(&UniCase(CowStr(Cow::Borrowed(header_name::<H>())))).is_some() self.data.remove(&HeaderName(UniCase(Cow::Borrowed(header_name::<H>())))).is_some()
} }
/// Returns an iterator over the header fields. /// Returns an iterator over the header fields.
@@ -377,14 +422,14 @@ impl Deserialize for Headers {
/// An `Iterator` over the fields in a `Headers` map. /// An `Iterator` over the fields in a `Headers` map.
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct HeadersItems<'a> { pub struct HeadersItems<'a> {
inner: Iter<'a, HeaderName, Item> inner: ::std::slice::Iter<'a, (HeaderName, Item)>
} }
impl<'a> Iterator for HeadersItems<'a> { impl<'a> Iterator for HeadersItems<'a> {
type Item = HeaderView<'a>; type Item = HeaderView<'a>;
fn next(&mut self) -> Option<HeaderView<'a>> { fn next(&mut self) -> Option<HeaderView<'a>> {
self.inner.next().map(|(k, v)| HeaderView(k, v)) self.inner.next().map(|&(ref k, ref v)| HeaderView(k, v))
} }
} }
@@ -395,7 +440,7 @@ impl<'a> HeaderView<'a> {
/// Check if a HeaderView is a certain Header. /// Check if a HeaderView is a certain Header.
#[inline] #[inline]
pub fn is<H: Header>(&self) -> bool { pub fn is<H: Header>(&self) -> bool {
UniCase(CowStr(Cow::Borrowed(header_name::<H>()))) == *self.0 HeaderName(UniCase(Cow::Borrowed(header_name::<H>()))) == *self.0
} }
/// Get the Header name as a slice. /// Get the Header name as a slice.
@@ -473,38 +518,30 @@ impl<'a, H: Header> fmt::Debug for HeaderFormatter<'a, H> {
} }
} }
#[derive(Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] #[derive(Clone, Debug)]
struct CowStr(Cow<'static, str>); struct HeaderName(UniCase<Cow<'static, str>>);
impl Deref for CowStr { impl fmt::Display for HeaderName {
type Target = Cow<'static, str>;
fn deref(&self) -> &Cow<'static, str> {
&self.0
}
}
impl fmt::Debug for CowStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for CowStr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f) fmt::Display::fmt(&self.0, f)
} }
} }
impl DerefMut for CowStr { impl AsRef<str> for HeaderName {
fn deref_mut(&mut self) -> &mut Cow<'static, str> { fn as_ref(&self) -> &str {
&mut self.0 ((self.0).0).as_ref()
} }
} }
impl AsRef<str> for CowStr { impl PartialEq for HeaderName {
fn as_ref(&self) -> &str { fn eq(&self, other: &HeaderName) -> bool {
self let s = self.as_ref();
let k = other.as_ref();
if s.len() == k.len() && s.as_ptr() == k.as_ptr() {
true
} else {
self.0 == other.0
}
} }
} }