change internal rep of Value

This commit is contained in:
Rob Ede 2021-02-09 06:07:37 +00:00
parent b5b99d3909
commit 0c601c7f1b
No known key found for this signature in database
GPG Key ID: C2A3B36E841A91E6
6 changed files with 305 additions and 256 deletions

View File

@ -830,8 +830,8 @@ mod tests {
.get_all(SET_COOKIE) .get_all(SET_COOKIE)
.map(|v| v.to_str().unwrap().to_owned()) .map(|v| v.to_str().unwrap().to_owned())
.collect(); .collect();
assert_eq!(val[1], "c1=cookie1"); assert_eq!(val[0], "c1=cookie1");
assert_eq!(val[0], "c2=cookie2"); assert_eq!(val[1], "c2=cookie2");
} }
#[test] #[test]

View File

@ -144,104 +144,50 @@ pub(crate) trait MessageType: Sized {
let k = key.as_str().as_bytes(); let k = key.as_str().as_bytes();
let k_len = k.len(); let k_len = k.len();
match value { // TODO: drain?
Value::One(ref val) => { for val in value.iter() {
let v = val.as_ref(); let v = val.as_ref();
let v_len = v.len(); let v_len = v.len();
let len = k_len + v_len + 4;
// key length + value length + colon + space + \r\n if len > remaining {
let len = k_len + v_len + 4; // SAFETY: all the bytes written up to position "pos" are initialized
// the written byte count and pointer advancement are kept in sync
if len > remaining {
// not enough room in buffer for this header; reserve more space
// SAFETY: all the bytes written up to position "pos" are initialized
// the written byte count and pointer advancement are kept in sync
unsafe {
dst.advance_mut(pos);
}
pos = 0;
dst.reserve(len * 2);
remaining = dst.capacity() - dst.len();
// re-assign buf raw pointer since it's possible that the buffer was
// reallocated and/or resized
buf = dst.chunk_mut().as_mut_ptr();
}
// SAFETY: on each write, it is enough to ensure that the advancement of the
// cursor matches the number of bytes written
unsafe { unsafe {
// use upper Camel-Case dst.advance_mut(pos);
if camel_case {
write_camel_case(k, from_raw_parts_mut(buf, k_len))
} else {
write_data(k, buf, k_len)
}
buf = buf.add(k_len);
write_data(b": ", buf, 2);
buf = buf.add(2);
write_data(v, buf, v_len);
buf = buf.add(v_len);
write_data(b"\r\n", buf, 2);
buf = buf.add(2);
} }
pos = 0;
dst.reserve(len * 2);
remaining = dst.capacity() - dst.len();
pos += len; // re-assign buf raw pointer since it's possible that the buffer was
remaining -= len; // reallocated and/or resized
buf = dst.chunk_mut().as_mut_ptr();
} }
Value::Multi(ref vec) => { // SAFETY: on each write, it is enough to ensure that the advancement of
for val in vec { // the cursor matches the number of bytes written
let v = val.as_ref(); unsafe {
let v_len = v.len(); if camel_case {
let len = k_len + v_len + 4; write_camel_case(k, from_raw_parts_mut(buf, k_len));
} else {
if len > remaining { write_data(k, buf, k_len);
// SAFETY: all the bytes written up to position "pos" are initialized
// the written byte count and pointer advancement are kept in sync
unsafe {
dst.advance_mut(pos);
}
pos = 0;
dst.reserve(len * 2);
remaining = dst.capacity() - dst.len();
// re-assign buf raw pointer since it's possible that the buffer was
// reallocated and/or resized
buf = dst.chunk_mut().as_mut_ptr();
}
// SAFETY: on each write, it is enough to ensure that the advancement of
// the cursor matches the number of bytes written
unsafe {
if camel_case {
write_camel_case(k, from_raw_parts_mut(buf, k_len));
} else {
write_data(k, buf, k_len);
}
buf = buf.add(k_len);
write_data(b": ", buf, 2);
buf = buf.add(2);
write_data(v, buf, v_len);
buf = buf.add(v_len);
write_data(b"\r\n", buf, 2);
buf = buf.add(2);
};
pos += len;
remaining -= len;
} }
}
buf = buf.add(k_len);
write_data(b": ", buf, 2);
buf = buf.add(2);
write_data(v, buf, v_len);
buf = buf.add(v_len);
write_data(b"\r\n", buf, 2);
buf = buf.add(2);
};
pos += len;
remaining -= len;
} }
}); });

View File

@ -0,0 +1,44 @@
use std::{borrow::Cow, str::FromStr};
use http::header::{HeaderName, InvalidHeaderName};
pub trait AsHeaderName: Sealed {}
pub trait Sealed {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName>;
}
impl Sealed for HeaderName {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
Ok(Cow::Borrowed(self))
}
}
impl AsHeaderName for HeaderName {}
impl Sealed for &HeaderName {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
Ok(Cow::Borrowed(*self))
}
}
impl AsHeaderName for &HeaderName {}
impl Sealed for &str {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
HeaderName::from_str(self).map(Cow::Owned)
}
}
impl AsHeaderName for &str {}
impl Sealed for String {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
HeaderName::from_str(self).map(Cow::Owned)
}
}
impl AsHeaderName for String {}
impl Sealed for &String {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
HeaderName::from_str(self).map(Cow::Owned)
}
}
impl AsHeaderName for &String {}

View File

@ -1,58 +1,71 @@
//! A multi-value [`HeaderMap`], its iterators, and a helper trait for types that can be effectively //! A multi-value [`HeaderMap`], its iterators, and a helper trait for types that can be effectively
//! borrowed as, or converted to, a [HeaderValue]. //! borrowed as, or converted to, a [HeaderValue].
use std::{borrow::Cow, collections::hash_map, mem, str::FromStr}; use std::{borrow::Cow, collections::hash_map, ops};
use ahash::AHashMap; use ahash::AHashMap;
use http::header::{HeaderName, HeaderValue, InvalidHeaderName}; use http::header::{HeaderName, HeaderValue};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
pub use as_header_name::AsHeaderName; use crate::header::AsHeaderName;
/// A multi-map of HTTP headers. /// A multi-map of HTTP headers.
/// ///
/// `HeaderMap` is a "multi-map" of [`HeaderName`] to one or more [`HeaderValue`]s. /// `HeaderMap` is a "multi-map" of [`HeaderName`] to one or more [`HeaderValue`]s.
///
/// # Examples
/// ```
/// use actix_http::http::{header, HeaderMap, HeaderValue};
///
/// let mut map = HeaderMap::new();
///
/// map.insert(header::CONTENT_TYPE, HeaderValue::from_static("text/plain"));
/// map.insert(header::ORIGIN, HeaderValue::from_static("example.com"));
///
/// assert!(map.contains_key(header::CONTENT_TYPE));
/// assert!(map.contains_key(header::ORIGIN));
///
/// let mut removed = map.remove(header::ORIGIN);
/// assert_eq!(removed.next().unwrap(), "example.com");
///
/// assert!(!map.contains_key(header::ORIGIN));
/// ```
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct HeaderMap { pub struct HeaderMap {
pub(crate) inner: AHashMap<HeaderName, Value>, pub(crate) inner: AHashMap<HeaderName, Value>,
} }
/// A bespoke non-empty list for HeaderMap values.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum Value { pub(crate) struct Value {
One(HeaderValue), inner: SmallVec<[HeaderValue; 4]>,
Multi(SmallVec<[HeaderValue; 4]>),
} }
impl Value { impl Value {
fn len(&self) -> usize { fn one(val: HeaderValue) -> Self {
match self { Self {
Value::One(_) => 1, inner: smallvec![val],
Value::Multi(vals) => vals.len(),
} }
} }
fn first(&self) -> &HeaderValue { fn first(&self) -> &HeaderValue {
match self { &self.inner[0]
Value::One(ref val) => val,
Value::Multi(ref val) => &val[0],
}
} }
fn first_mut(&mut self) -> &mut HeaderValue { fn first_mut(&mut self) -> &mut HeaderValue {
match self { &mut self.inner[0]
Value::One(ref mut val) => val,
Value::Multi(ref mut val) => &mut val[0],
}
} }
fn append(&mut self, val: HeaderValue) { fn append(&mut self, new_val: HeaderValue) {
match self { self.inner.push(new_val)
Value::One(_) => match mem::replace(self, Value::Multi(smallvec![val])) { }
Value::One(val) => self.append(val), }
Value::Multi(_) => unreachable!(),
}, impl ops::Deref for Value {
Value::Multi(ref mut vals) => vals.push(val), type Target = SmallVec<[HeaderValue; 4]>;
}
fn deref(&self) -> &Self::Target {
&self.inner
} }
} }
@ -60,6 +73,15 @@ impl HeaderMap {
/// Create an empty `HeaderMap`. /// Create an empty `HeaderMap`.
/// ///
/// The map will be created without any capacity; this function will not allocate. /// The map will be created without any capacity; this function will not allocate.
///
/// # Examples
/// ```
/// # use actix_http::http::HeaderMap;
/// let map = HeaderMap::new();
///
/// assert!(map.is_empty());
/// assert_eq!(0, map.capacity());
/// ```
pub fn new() -> Self { pub fn new() -> Self {
HeaderMap::default() HeaderMap::default()
} }
@ -68,6 +90,15 @@ impl HeaderMap {
/// ///
/// The map will be able to hold at least `capacity` elements without needing to reallocate. /// The map will be able to hold at least `capacity` elements without needing to reallocate.
/// If `capacity` is 0, the map will be created without allocating. /// If `capacity` is 0, the map will be created without allocating.
///
/// # Examples
/// ```
/// # use actix_http::http::HeaderMap;
/// let map = HeaderMap::with_capacity(16);
///
/// assert!(map.is_empty());
/// assert!(map.capacity() >= 16);
/// ```
pub fn with_capacity(capacity: usize) -> Self { pub fn with_capacity(capacity: usize) -> Self {
HeaderMap { HeaderMap {
inner: AHashMap::with_capacity(capacity), inner: AHashMap::with_capacity(capacity),
@ -105,7 +136,21 @@ impl HeaderMap {
/// Returns the number of values stored in the map. /// Returns the number of values stored in the map.
/// ///
/// Also see [`len_keys`](Self::len_keys). /// See also: [`len_keys`](Self::len_keys).
///
/// # Examples
/// ```
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
/// let mut map = HeaderMap::new();
/// assert_eq!(map.len(), 0);
///
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
/// assert_eq!(map.len(), 2);
///
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
/// assert_eq!(map.len(), 3);
/// ```
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.inner self.inner
.iter() .iter()
@ -114,12 +159,36 @@ impl HeaderMap {
/// Returns the number of _keys_ stored in the map. /// Returns the number of _keys_ stored in the map.
/// ///
/// The number of _values_ stored will be at least this number. Also see [`Self::len`]. /// The number of values stored will be at least this number. See also: [`Self::len`].
///
/// # Examples
/// ```
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
/// let mut map = HeaderMap::new();
/// assert_eq!(map.len_keys(), 0);
///
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
/// assert_eq!(map.len_keys(), 2);
///
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
/// assert_eq!(map.len_keys(), 2);
/// ```
pub fn len_keys(&self) -> usize { pub fn len_keys(&self) -> usize {
self.inner.len() self.inner.len()
} }
/// Returns true if the map contains no elements. /// Returns true if the map contains no elements.
///
/// # Examples
/// ```
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
/// let mut map = HeaderMap::new();
/// assert!(map.is_empty());
///
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
/// assert!(!map.is_empty());
/// ```
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.inner.len() == 0 self.inner.len() == 0
} }
@ -127,6 +196,19 @@ impl HeaderMap {
/// Clears the map, removing all name-value pairs. /// Clears the map, removing all name-value pairs.
/// ///
/// Keeps the allocated memory for reuse. /// Keeps the allocated memory for reuse.
///
/// # Examples
/// ```
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
/// let mut map = HeaderMap::new();
///
/// map.insert(header::ACCEPT, HeaderValue::from_static("text/plain"));
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
/// assert_eq!(map.len(), 2);
///
/// map.clear();
/// assert!(map.is_empty());
/// ```
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.inner.clear(); self.inner.clear();
} }
@ -140,18 +222,63 @@ impl HeaderMap {
/// Returns a reference to the _first_ value associated with a header name. /// Returns a reference to the _first_ value associated with a header name.
/// ///
/// Even when multiple values associated with the key, the "first" one is returned but is not /// Returns `None` if there is no value associated with the key.
/// guaranteed to be chosen in with particular order. Use `get_all` to get all values associated ///
/// with a given key. Returns `None` if there are no values associated with the key. /// Even when multiple values are associated with the key, the "first" one is returned but is
/// not guaranteed to be chosen with any particular order; though, the returned item will be
/// consistent for each call to `get` if the map has not changed.
///
/// See also: [`get_all`](Self::get_all).
///
/// # Examples
/// ```
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
/// let mut map = HeaderMap::new();
///
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
///
/// let cookie = map.get(header::SET_COOKIE).unwrap();
/// assert_eq!(cookie, "one=1");
///
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
/// assert_eq!(map.get(header::SET_COOKIE).unwrap(), "one=1");
///
/// assert_eq!(map.get(header::SET_COOKIE), map.get("set-cookie"));
/// assert_eq!(map.get(header::SET_COOKIE), map.get("Set-Cookie"));
///
/// assert!(map.get(header::HOST).is_none());
/// assert!(map.get("INVALID HEADER NAME").is_none());
/// ```
pub fn get(&self, key: impl AsHeaderName) -> Option<&HeaderValue> { pub fn get(&self, key: impl AsHeaderName) -> Option<&HeaderValue> {
self.get_value(key).map(|val| val.first()) self.get_value(key).map(|val| val.first())
} }
/// Returns a mutable reference to the _first_ value associated a header name. /// Returns a mutable reference to the _first_ value associated a header name.
/// ///
/// Even when multiple values associated with the key, the "first" one is returned but is not /// Returns `None` if there is no value associated with the key.
/// guaranteed to be chosen in with particular order. Use `get_all` to get all values associated ///
/// with a given key. Returns `None` if there are no values associated with the key. /// Even when multiple values are associated with the key, the "first" one is returned but is
/// not guaranteed to be chosen with any particular order; though, the returned item will be
/// consistent for each call to `get_mut` if the map has not changed.
///
/// See also: [`get_all`](Self::get_all).
///
/// # Examples
/// ```
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
/// let mut map = HeaderMap::new();
///
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
///
/// let mut cookie = map.get_mut(header::SET_COOKIE).unwrap();
/// assert_eq!(cookie, "one=1");
///
/// *cookie = HeaderValue::from_static("three=3");
/// assert_eq!(map.get(header::SET_COOKIE).unwrap(), "three=3");
///
/// assert!(map.get(header::HOST).is_none());
/// assert!(map.get("INVALID HEADER NAME").is_none());
/// ```
pub fn get_mut(&mut self, key: impl AsHeaderName) -> Option<&mut HeaderValue> { pub fn get_mut(&mut self, key: impl AsHeaderName) -> Option<&mut HeaderValue> {
match key.try_as_name().ok()? { match key.try_as_name().ok()? {
Cow::Borrowed(name) => self.inner.get_mut(name).map(|v| v.first_mut()), Cow::Borrowed(name) => self.inner.get_mut(name).map(|v| v.first_mut()),
@ -164,6 +291,23 @@ impl HeaderMap {
/// The returned iterator does not incur any allocations and will yield no items if there are no /// The returned iterator does not incur any allocations and will yield no items if there are no
/// values associated with the key. Iteration order is **not** guaranteed to be the same as /// values associated with the key. Iteration order is **not** guaranteed to be the same as
/// insertion order. /// insertion order.
///
/// # Examples
/// ```
/// # use actix_http::http::{header, HeaderMap, HeaderValue};
/// let mut map = HeaderMap::new();
///
/// let mut none_iter = map.get_all(header::ORIGIN);
/// assert!(none_iter.next().is_none());
///
/// map.insert(header::SET_COOKIE, HeaderValue::from_static("one=1"));
/// map.append(header::SET_COOKIE, HeaderValue::from_static("two=2"));
///
/// let mut set_cookies_iter = map.get_all(header::SET_COOKIE);
/// assert_eq!(set_cookies_iter.next().unwrap(), "one=1");
/// assert_eq!(set_cookies_iter.next().unwrap(), "two=2");
/// assert!(set_cookies_iter.next().is_none());
/// ```
pub fn get_all(&self, key: impl AsHeaderName) -> GetAll<'_> { pub fn get_all(&self, key: impl AsHeaderName) -> GetAll<'_> {
GetAll::new(self.get_value(key)) GetAll::new(self.get_value(key))
} }
@ -185,7 +329,8 @@ impl HeaderMap {
/// previous values are removed and returned as a `Removed` iterator. The key is not updated; /// previous values are removed and returned as a `Removed` iterator. The key is not updated;
/// this matters for types that can be `==` without being identical. /// this matters for types that can be `==` without being identical.
pub fn insert(&mut self, key: HeaderName, val: HeaderValue) -> Removed { pub fn insert(&mut self, key: HeaderName, val: HeaderValue) -> Removed {
Removed::new(self.inner.insert(key, Value::One(val))) let value = self.inner.insert(key, Value::one(val));
Removed::new(value)
} }
/// Inserts a name-value pair into the map. /// Inserts a name-value pair into the map.
@ -199,7 +344,7 @@ impl HeaderMap {
entry.get_mut().append(value); entry.get_mut().append(value);
} }
hash_map::Entry::Vacant(entry) => { hash_map::Entry::Vacant(entry) => {
entry.insert(Value::One(value)); entry.insert(Value::one(value));
} }
}; };
} }
@ -282,51 +427,6 @@ impl<'a> IntoIterator for &'a HeaderMap {
} }
} }
mod as_header_name {
use super::*;
pub trait AsHeaderName: Sealed {}
pub trait Sealed {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName>;
}
impl Sealed for HeaderName {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
Ok(Cow::Borrowed(self))
}
}
impl AsHeaderName for HeaderName {}
impl Sealed for &HeaderName {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
Ok(Cow::Borrowed(*self))
}
}
impl AsHeaderName for &HeaderName {}
impl Sealed for &str {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
HeaderName::from_str(self).map(Cow::Owned)
}
}
impl AsHeaderName for &str {}
impl Sealed for String {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
HeaderName::from_str(self).map(Cow::Owned)
}
}
impl AsHeaderName for String {}
impl Sealed for &String {
fn try_as_name(&self) -> Result<Cow<'_, HeaderName>, InvalidHeaderName> {
HeaderName::from_str(self).map(Cow::Owned)
}
}
impl AsHeaderName for &String {}
}
/// Iterator for all values with the same header name. /// Iterator for all values with the same header name.
#[derive(Debug)] #[derive(Debug)]
pub struct GetAll<'a> { pub struct GetAll<'a> {
@ -346,23 +446,16 @@ impl<'a> Iterator for GetAll<'a> {
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
let val = self.value?; let val = self.value?;
match val { match val.get(self.idx) {
Value::One(ref val) => { Some(val) => {
// remove value to fast-path future next calls self.idx += 1;
self.value = None;
Some(val) Some(val)
} }
Value::Multi(ref vals) => match vals.get(self.idx) { None => {
Some(val) => { // current index is none; remove value to fast-path future next calls
self.idx += 1; self.value = None;
Some(val) None
} }
None => {
// current index is none; remove value to fast-path future next calls
self.value = None;
None
}
},
} }
} }
@ -378,17 +471,12 @@ impl<'a> Iterator for GetAll<'a> {
/// on [`HeaderMap`] that remove or replace items. /// on [`HeaderMap`] that remove or replace items.
#[derive(Debug)] #[derive(Debug)]
pub struct Removed { pub struct Removed {
inner: smallvec::IntoIter<[HeaderValue; 4]>, inner: Option<smallvec::IntoIter<[HeaderValue; 4]>>,
} }
impl<'a> Removed { impl<'a> Removed {
fn new(value: Option<Value>) -> Self { fn new(value: Option<Value>) -> Self {
let inner = match value { let inner = value.map(|value| value.inner.into_iter());
Some(Value::One(val)) => smallvec![val].into_iter(),
Some(Value::Multi(vals)) => vals.into_iter(),
None => smallvec![].into_iter(),
};
Self { inner } Self { inner }
} }
} }
@ -398,12 +486,15 @@ impl Iterator for Removed {
#[inline] #[inline]
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
self.inner.next() self.inner.as_mut()?.next()
} }
#[inline] #[inline]
fn size_hint(&self) -> (usize, Option<usize>) { fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint() match self.inner {
Some(ref iter) => iter.size_hint(),
None => (0, None),
}
} }
} }
@ -463,14 +554,9 @@ impl<'a> Iterator for Iter<'a> {
let (name, value) = self.inner.next()?; let (name, value) = self.inner.next()?;
match value { // set up new inner iter and recurse into it
Value::One(ref value) => Some((name, value)), self.multi_inner = Some((name, &value.inner));
Value::Multi(ref vals) => { self.next()
// set up new multi value inner iter and recurse into it
self.multi_inner = Some((name, vals));
self.next()
}
}
} }
#[inline] #[inline]
@ -488,7 +574,7 @@ impl<'a> Iterator for Iter<'a> {
pub struct Drain<'a> { pub struct Drain<'a> {
inner: hash_map::Drain<'a, HeaderName, Value>, inner: hash_map::Drain<'a, HeaderName, Value>,
multi_inner: Option<(Option<HeaderName>, SmallVec<[HeaderValue; 4]>)>, multi_inner: Option<(Option<HeaderName>, SmallVec<[HeaderValue; 4]>)>,
multi_inner_idx: usize, multi_idx: usize,
} }
impl<'a> Drain<'a> { impl<'a> Drain<'a> {
@ -496,7 +582,7 @@ impl<'a> Drain<'a> {
Self { Self {
inner: iter, inner: iter,
multi_inner: None, multi_inner: None,
multi_inner_idx: 0, multi_idx: 0,
} }
} }
} }
@ -508,25 +594,20 @@ impl<'a> Iterator for Drain<'a> {
// handle in-progress multi value iterators first // handle in-progress multi value iterators first
if let Some((ref mut name, ref mut vals)) = self.multi_inner { if let Some((ref mut name, ref mut vals)) = self.multi_inner {
if vals.len() > 0 { if vals.len() > 0 {
// OPTIMISE: array removals // OPTIMIZE: array removals
return Some((name.take(), vals.remove(0))); return Some((name.take(), vals.remove(0)));
} else { } else {
// no more items in value iterator; reset state // no more items in value iterator; reset state
self.multi_inner = None; self.multi_inner = None;
self.multi_inner_idx = 0; self.multi_idx = 0;
} }
} }
let (name, mut value) = self.inner.next()?; let (name, value) = self.inner.next()?;
match value { // set up new inner iter and recurse into it
Value::One(value) => Some((Some(name), value)), self.multi_inner = Some((Some(name), value.inner));
Value::Multi(ref mut vals) => { self.next()
// set up new multi value inner iter and recurse into it
self.multi_inner = Some((Some(name), mem::take(vals)));
self.next()
}
}
} }
#[inline] #[inline]
@ -574,14 +655,9 @@ impl Iterator for IntoIter {
let (name, value) = self.inner.next()?; let (name, value) = self.inner.next()?;
match value { // set up new inner iter and recurse into it
Value::One(value) => Some((name, value)), self.multi_inner = Some((name, value.inner.into_iter()));
Value::Multi(vals) => { self.next()
// set up new multi value inner iter and recurse into it
self.multi_inner = Some((name, vals.into_iter()));
self.next()
}
}
} }
#[inline] #[inline]

View File

@ -11,6 +11,7 @@ pub use http::header::*;
use crate::error::ParseError; use crate::error::ParseError;
use crate::httpmessage::HttpMessage; use crate::httpmessage::HttpMessage;
mod as_name;
mod into_pair; mod into_pair;
mod into_value; mod into_value;
mod utils; mod utils;
@ -23,6 +24,7 @@ pub use self::common::*;
#[doc(hidden)] #[doc(hidden)]
pub use self::shared::*; pub use self::shared::*;
pub use self::as_name::AsHeaderName;
pub use self::into_pair::IntoHeaderPair; pub use self::into_pair::IntoHeaderPair;
pub use self::into_value::IntoHeaderValue; pub use self::into_value::IntoHeaderValue;
#[doc(hidden)] #[doc(hidden)]
@ -97,26 +99,3 @@ pub(crate) const HTTP_VALUE: &AsciiSet = &CONTROLS
.add(b']') .add(b']')
.add(b'{') .add(b'{')
.add(b'}'); .add(b'}');
#[cfg(test)]
mod tests {
use super::*;
use crate::header;
#[test]
fn test_http_header_map_to_ours() {
let mut http_map = http::HeaderMap::new();
let map = HeaderMap::from_drain(http_map.drain());
assert!(map.is_empty());
let mut http_map = http::HeaderMap::new();
http_map.append(header::HOST, HeaderValue::from_static("duck.com"));
http_map.append(header::COOKIE, HeaderValue::from_static("one=1"));
http_map.append(header::COOKIE, HeaderValue::from_static("two=2"));
let map = HeaderMap::from_drain(http_map.drain());
assert_eq!(map.len(), 3);
assert!(map.contains_key(header::HOST));
assert!(map.contains_key(header::COOKIE));
}
}

View File

@ -897,16 +897,20 @@ mod tests {
.max_age(time::Duration::days(1)) .max_age(time::Duration::days(1))
.finish(), .finish(),
) )
.del_cookie(&cookies[1]) .del_cookie(&cookies[0])
.finish(); .finish();
let mut val: Vec<_> = resp let mut val = resp
.headers() .headers()
.get_all(SET_COOKIE) .get_all(SET_COOKIE)
.map(|v| v.to_str().unwrap().to_owned()) .map(|v| v.to_str().unwrap())
.collect(); .collect::<Vec<_>>();
val.sort(); val.sort();
// the .del_cookie call
assert!(val[0].starts_with("cookie1=; Max-Age=0;")); assert!(val[0].starts_with("cookie1=; Max-Age=0;"));
// the .cookie call
assert_eq!( assert_eq!(
val[1], val[1],
"name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400" "name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400"
@ -931,9 +935,9 @@ mod tests {
let mut iter = r.cookies(); let mut iter = r.cookies();
let v = iter.next().unwrap(); let v = iter.next().unwrap();
assert_eq!((v.name(), v.value()), ("cookie3", "val300"));
let v = iter.next().unwrap();
assert_eq!((v.name(), v.value()), ("original", "val100")); assert_eq!((v.name(), v.value()), ("original", "val100"));
let v = iter.next().unwrap();
assert_eq!((v.name(), v.value()), ("cookie3", "val300"));
} }
#[test] #[test]