Skip to content

Commit cb67c17

Browse files
committed
Add from_maybe_shared constructors
These constructors don't expose the internal `Bytes` type, but instead will try to downcast the argument to prevent a copy. If the types don't match up (a user provides an older version of `Bytes`), the value will just be copied. Adds: - `HeaderValue::from_maybe_shared` - `HeaderValue::from_maybe_shared_unchecked` - `Uri::from_maybe_shared` - `Authority::from_maybe_shared` - `PathAndQuery::from_maybe_shared`
1 parent db9b1b9 commit cb67c17

File tree

7 files changed

+114
-117
lines changed

7 files changed

+114
-117
lines changed

benches/header_value.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fn from_shared_short(b: &mut Bencher) {
1414
b.bytes = SHORT.len() as u64;
1515
let bytes = Bytes::from_static(SHORT);
1616
b.iter(|| {
17-
HeaderValue::from_shared(bytes.clone()).unwrap();
17+
HeaderValue::from_maybe_shared(bytes.clone()).unwrap();
1818
});
1919
}
2020

@@ -23,7 +23,7 @@ fn from_shared_long(b: &mut Bencher) {
2323
b.bytes = LONG.len() as u64;
2424
let bytes = Bytes::from_static(LONG);
2525
b.iter(|| {
26-
HeaderValue::from_shared(bytes.clone()).unwrap();
26+
HeaderValue::from_maybe_shared(bytes.clone()).unwrap();
2727
});
2828
}
2929

@@ -32,7 +32,7 @@ fn from_shared_unchecked_short(b: &mut Bencher) {
3232
b.bytes = SHORT.len() as u64;
3333
let bytes = Bytes::from_static(SHORT);
3434
b.iter(|| unsafe {
35-
HeaderValue::from_shared_unchecked(bytes.clone());
35+
HeaderValue::from_maybe_shared_unchecked(bytes.clone());
3636
});
3737
}
3838

@@ -41,6 +41,6 @@ fn from_shared_unchecked_long(b: &mut Bencher) {
4141
b.bytes = LONG.len() as u64;
4242
let bytes = Bytes::from_static(LONG);
4343
b.iter(|| unsafe {
44-
HeaderValue::from_shared_unchecked(bytes.clone());
44+
HeaderValue::from_maybe_shared_unchecked(bytes.clone());
4545
});
4646
}

src/convert.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
macro_rules! if_downcast_into {
2+
($in_ty:ty, $out_ty:ty, $val:ident, $body:expr) => ({
3+
if std::any::TypeId::of::<$in_ty>() == std::any::TypeId::of::<$out_ty>() {
4+
// Store the value in an `Option` so we can `take`
5+
// it after casting to `&mut dyn Any`.
6+
let mut slot = Some($val);
7+
// Re-write the `$val` ident with the downcasted value.
8+
let $val = (&mut slot as &mut dyn std::any::Any)
9+
.downcast_mut::<Option<$out_ty>>()
10+
.unwrap()
11+
.take()
12+
.unwrap();
13+
// Run the $body in scope of the replaced val.
14+
$body
15+
}
16+
})
17+
}

src/header/value.rs

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -149,39 +149,54 @@ impl HeaderValue {
149149

150150
/// Attempt to convert a `Bytes` buffer to a `HeaderValue`.
151151
///
152-
/// If the argument contains invalid header value bytes, an error is
153-
/// returned. Only byte values between 32 and 255 (inclusive) are permitted,
154-
/// excluding byte 127 (DEL).
155-
///
156-
/// This function is intended to be replaced in the future by a `TryFrom`
157-
/// implementation once the trait is stabilized in std.
158-
#[inline]
159-
fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> {
160-
HeaderValue::try_from_generic(src, std::convert::identity)
152+
/// This will try to prevent a copy if the type passed is the type used
153+
/// internally, and will copy the data if it is not.
154+
pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
155+
where
156+
T: AsRef<[u8]> + 'static,
157+
{
158+
if_downcast_into!(T, Bytes, src, {
159+
return HeaderValue::from_shared(src);
160+
});
161+
162+
HeaderValue::from_bytes(src.as_ref())
161163
}
162164

163-
/*
164165
/// Convert a `Bytes` directly into a `HeaderValue` without validating.
165166
///
166167
/// This function does NOT validate that illegal bytes are not contained
167168
/// within the buffer.
168-
#[inline]
169-
pub unsafe fn from_shared_unchecked(src: Bytes) -> HeaderValue {
169+
pub unsafe fn from_maybe_shared_unchecked<T>(src: T) -> HeaderValue
170+
where
171+
T: AsRef<[u8]> + 'static,
172+
{
170173
if cfg!(debug_assertions) {
171-
match HeaderValue::from_shared(src) {
174+
match HeaderValue::from_maybe_shared(src) {
172175
Ok(val) => val,
173176
Err(_err) => {
174-
panic!("HeaderValue::from_shared_unchecked() with invalid bytes");
177+
panic!("HeaderValue::from_maybe_shared_unchecked() with invalid bytes");
175178
}
176179
}
177180
} else {
181+
182+
if_downcast_into!(T, Bytes, src, {
183+
return HeaderValue {
184+
inner: src,
185+
is_sensitive: false,
186+
};
187+
});
188+
189+
let src = Bytes::copy_from_slice(src.as_ref());
178190
HeaderValue {
179191
inner: src,
180192
is_sensitive: false,
181193
}
182194
}
183195
}
184-
*/
196+
197+
fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> {
198+
HeaderValue::try_from_generic(src, std::convert::identity)
199+
}
185200

186201
fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(src: T, into: F) -> Result<HeaderValue, InvalidHeaderValue> {
187202
for &b in src.as_ref() {

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ extern crate doc_comment;
167167
#[cfg(test)]
168168
doctest!("../README.md");
169169

170+
#[macro_use]
171+
mod convert;
172+
170173
pub mod header;
171174
pub mod method;
172175
pub mod request;

src/uri/authority.rs

Lines changed: 19 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,26 +21,7 @@ impl Authority {
2121
}
2222
}
2323

24-
/// Attempt to convert an `Authority` from `Bytes`.
25-
///
26-
/// This function has been replaced by `TryFrom` implementation.
27-
///
28-
/// # Examples
29-
///
30-
/// ```
31-
/// # extern crate http;
32-
/// # use http::uri::*;
33-
/// extern crate bytes;
34-
///
35-
/// use bytes::Bytes;
36-
///
37-
/// # pub fn main() {
38-
/// let bytes = Bytes::from("example.com");
39-
/// let authority = Authority::from_shared(bytes).unwrap();
40-
///
41-
/// assert_eq!(authority.host(), "example.com");
42-
/// # }
43-
/// ```
24+
// Not public while `bytes` is unstable.
4425
pub(super) fn from_shared(s: Bytes) -> Result<Self, InvalidUri> {
4526
let authority_end = Authority::parse_non_empty(&s[..])?;
4627

@@ -85,6 +66,22 @@ impl Authority {
8566
}
8667
}
8768

69+
70+
/// Attempt to convert a `Bytes` buffer to a `Authority`.
71+
///
72+
/// This will try to prevent a copy if the type passed is the type used
73+
/// internally, and will copy the data if it is not.
74+
pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
75+
where
76+
T: AsRef<[u8]> + 'static,
77+
{
78+
if_downcast_into!(T, Bytes, src, {
79+
return Authority::from_shared(src);
80+
});
81+
82+
Authority::try_from(src.as_ref())
83+
}
84+
8885
// Note: this may return an *empty* Authority. You might want `parse_non_empty`.
8986
pub(super) fn parse(s: &[u8]) -> Result<usize, InvalidUri> {
9087
let mut colon_cnt = 0;
@@ -267,41 +264,8 @@ impl Authority {
267264
}
268265
}
269266

270-
/*
271-
impl TryFrom<Bytes> for Authority {
272-
type Error = InvalidUri;
273-
/// Attempt to convert an `Authority` from `Bytes`.
274-
///
275-
/// # Examples
276-
///
277-
/// ```
278-
/// # extern crate http;
279-
/// # use http::uri::*;
280-
/// extern crate bytes;
281-
///
282-
/// use std::convert::TryFrom;
283-
/// use bytes::Bytes;
284-
///
285-
/// # pub fn main() {
286-
/// let bytes = Bytes::from("example.com");
287-
/// let authority = Authority::try_from(bytes).unwrap();
288-
///
289-
/// assert_eq!(authority.host(), "example.com");
290-
/// # }
291-
/// ```
292-
fn try_from(s: Bytes) -> Result<Self, Self::Error> {
293-
let authority_end = Authority::parse_non_empty(&s[..])?;
294-
295-
if authority_end != s.len() {
296-
return Err(ErrorKind::InvalidUriChar.into());
297-
}
298-
299-
Ok(Authority {
300-
data: unsafe { ByteStr::from_utf8_unchecked(s) },
301-
})
302-
}
303-
}
304-
*/
267+
// Purposefully not public while `bytes` is unstable.
268+
// impl TryFrom<Bytes> for Authority
305269

306270
impl AsRef<str> for Authority {
307271
fn as_ref(&self) -> &str {

src/uri/mod.rs

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -235,27 +235,22 @@ impl Uri {
235235
})
236236
}
237237

238-
/// Attempt to convert a `Uri` from `Bytes`
238+
/// Attempt to convert a `Bytes` buffer to a `Uri`.
239239
///
240-
/// This function has been replaced by `TryFrom` implementation.
241-
///
242-
/// # Examples
243-
///
244-
/// ```
245-
/// # extern crate http;
246-
/// # use http::uri::*;
247-
/// extern crate bytes;
248-
///
249-
/// use bytes::Bytes;
250-
///
251-
/// # pub fn main() {
252-
/// let bytes = Bytes::from("http://example.com/foo");
253-
/// let uri = Uri::from_shared(bytes).unwrap();
254-
///
255-
/// assert_eq!(uri.host().unwrap(), "example.com");
256-
/// assert_eq!(uri.path(), "/foo");
257-
/// # }
258-
/// ```
240+
/// This will try to prevent a copy if the type passed is the type used
241+
/// internally, and will copy the data if it is not.
242+
pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
243+
where
244+
T: AsRef<[u8]> + 'static,
245+
{
246+
if_downcast_into!(T, Bytes, src, {
247+
return Uri::from_shared(src);
248+
});
249+
250+
Uri::try_from(src.as_ref())
251+
}
252+
253+
// Not public while `bytes` is unstable.
259254
fn from_shared(s: Bytes) -> Result<Uri, InvalidUri> {
260255
use self::ErrorKind::*;
261256

@@ -674,6 +669,15 @@ impl Uri {
674669
}
675670
}
676671

672+
impl<'a> TryFrom<&'a [u8]> for Uri {
673+
type Error = InvalidUri;
674+
675+
#[inline]
676+
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
677+
Uri::from_shared(Bytes::copy_from_slice(t))
678+
}
679+
}
680+
677681
impl<'a> TryFrom<&'a str> for Uri {
678682
type Error = InvalidUri;
679683

@@ -846,7 +850,7 @@ impl FromStr for Uri {
846850

847851
#[inline]
848852
fn from_str(s: &str) -> Result<Uri, InvalidUri> {
849-
Uri::from_shared(Bytes::copy_from_slice(s.as_bytes()))
853+
Uri::try_from(s.as_bytes())
850854
}
851855
}
852856

src/uri/path.rs

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,28 +17,7 @@ pub struct PathAndQuery {
1717
const NONE: u16 = ::std::u16::MAX;
1818

1919
impl PathAndQuery {
20-
/// Attempt to convert a `PathAndQuery` from `Bytes`.
21-
///
22-
/// This function will be replaced by a `TryFrom` implementation once the
23-
/// trait lands in stable.
24-
///
25-
/// # Examples
26-
///
27-
/// ```
28-
/// # extern crate http;
29-
/// # use http::uri::*;
30-
/// extern crate bytes;
31-
///
32-
/// use bytes::Bytes;
33-
///
34-
/// # pub fn main() {
35-
/// let bytes = Bytes::from("/hello?world");
36-
/// let path_and_query = PathAndQuery::from_shared(bytes).unwrap();
37-
///
38-
/// assert_eq!(path_and_query.path(), "/hello");
39-
/// assert_eq!(path_and_query.query(), Some("world"));
40-
/// # }
41-
/// ```
20+
// Not public while `bytes` is unstable.
4221
pub(super) fn from_shared(mut src: Bytes) -> Result<Self, InvalidUri> {
4322
let mut query = NONE;
4423
let mut fragment = None;
@@ -144,6 +123,21 @@ impl PathAndQuery {
144123
PathAndQuery::from_shared(src).unwrap()
145124
}
146125

126+
/// Attempt to convert a `Bytes` buffer to a `PathAndQuery`.
127+
///
128+
/// This will try to prevent a copy if the type passed is the type used
129+
/// internally, and will copy the data if it is not.
130+
pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
131+
where
132+
T: AsRef<[u8]> + 'static,
133+
{
134+
if_downcast_into!(T, Bytes, src, {
135+
return PathAndQuery::from_shared(src);
136+
});
137+
138+
PathAndQuery::try_from(src.as_ref())
139+
}
140+
147141
pub(super) fn empty() -> Self {
148142
PathAndQuery {
149143
data: ByteStr::new(),

0 commit comments

Comments
 (0)