reduce size of Response, async::Response, and async::Decoder
This commit is contained in:
		| @@ -1,7 +1,7 @@ | |||||||
| use std::fmt; | use std::fmt; | ||||||
|  |  | ||||||
| use futures::{Stream, Poll, Async}; | use futures::{Stream, Poll, Async}; | ||||||
| use bytes::Bytes; | use bytes::{Buf, Bytes}; | ||||||
| use hyper::body::Payload; | use hyper::body::Payload; | ||||||
|  |  | ||||||
| /// An asynchronous `Stream`. | /// An asynchronous `Stream`. | ||||||
| @@ -88,6 +88,20 @@ pub struct Chunk { | |||||||
|     inner: ::hyper::Chunk, |     inner: ::hyper::Chunk, | ||||||
| } | } | ||||||
|  |  | ||||||
|  | impl Buf for Chunk { | ||||||
|  |     fn bytes(&self) -> &[u8] { | ||||||
|  |         self.inner.bytes() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn remaining(&self) -> usize { | ||||||
|  |         self.inner.remaining() | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn advance(&mut self, n: usize) { | ||||||
|  |         self.inner.advance(n); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| impl AsRef<[u8]> for Chunk { | impl AsRef<[u8]> for Chunk { | ||||||
|     #[inline] |     #[inline] | ||||||
|     fn as_ref(&self) -> &[u8] { |     fn as_ref(&self) -> &[u8] { | ||||||
|   | |||||||
| @@ -26,7 +26,7 @@ use std::mem; | |||||||
| use std::cmp; | use std::cmp; | ||||||
| use std::io::{self, Read}; | use std::io::{self, Read}; | ||||||
|  |  | ||||||
| use bytes::{BufMut, BytesMut}; | use bytes::{Buf, BufMut, BytesMut}; | ||||||
| use libflate::non_blocking::gzip; | use libflate::non_blocking::gzip; | ||||||
| use futures::{Async, Future, Poll, Stream}; | use futures::{Async, Future, Poll, Stream}; | ||||||
| use hyper::{HeaderMap}; | use hyper::{HeaderMap}; | ||||||
| @@ -53,17 +53,15 @@ enum Inner { | |||||||
|     Pending(Pending) |     Pending(Pending) | ||||||
| } | } | ||||||
|  |  | ||||||
| enum Pending { | /// A future attempt to poll the response body for EOF so we know whether to use gzip or not. | ||||||
|     /// An unreachable internal state. | struct Pending { | ||||||
|     Empty, |     body: ReadableChunks<Body>, | ||||||
|     /// A future attempt to poll the response body for EOF so we know whether to use gzip or not. |  | ||||||
|     Gzip(ReadableChunks<Body>) |  | ||||||
| } | } | ||||||
|  |  | ||||||
| /// A gzip decoder that reads from a `libflate::gzip::Decoder` into a `BytesMut` and emits the results | /// A gzip decoder that reads from a `libflate::gzip::Decoder` into a `BytesMut` and emits the results | ||||||
| /// as a `Chunk`. | /// as a `Chunk`. | ||||||
| struct Gzip { | struct Gzip { | ||||||
|     inner: gzip::Decoder<Peeked<ReadableChunks<Body>>>, |     inner: Box<gzip::Decoder<Peeked<ReadableChunks<Body>>>>, | ||||||
|     buf: BytesMut, |     buf: BytesMut, | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -101,7 +99,7 @@ impl Decoder { | |||||||
|     #[inline] |     #[inline] | ||||||
|     fn gzip(body: Body) -> Decoder { |     fn gzip(body: Body) -> Decoder { | ||||||
|         Decoder { |         Decoder { | ||||||
|             inner: Inner::Pending(Pending::Gzip(ReadableChunks::new(body))) |             inner: Inner::Pending(Pending { body: ReadableChunks::new(body) }) | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -141,28 +139,19 @@ impl Future for Pending { | |||||||
|     type Error = error::Error; |     type Error = error::Error; | ||||||
|  |  | ||||||
|     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { |     fn poll(&mut self) -> Poll<Self::Item, Self::Error> { | ||||||
|         let body_state = match *self { |         let body_state = match self.body.poll_stream() { | ||||||
|             Pending::Gzip(ref mut body) => { |             Ok(Async::Ready(state)) => state, | ||||||
|                 match body.poll_stream() { |             Ok(Async::NotReady) => return Ok(Async::NotReady), | ||||||
|                     Ok(Async::Ready(state)) => state, |             Err(e) => return Err(e) | ||||||
|                     Ok(Async::NotReady) => return Ok(Async::NotReady), |  | ||||||
|                     Err(e) => return Err(e) |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|             Pending::Empty => panic!("poll for a decoder after it's done") |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         match mem::replace(self, Pending::Empty) { |         let body = mem::replace(&mut self.body, ReadableChunks::new(body::empty())); | ||||||
|             Pending::Gzip(body) => { |         // libflate does a read_exact([0; 2]), so its impossible to tell | ||||||
|                 // libflate does a read_exact([0; 2]), so its impossible to tell |         // if the stream was empty, or truly had an UnexpectedEof. | ||||||
|                 // if the stream was empty, or truly had an UnexpectedEof. |         // Therefore, we need to check for EOF first. | ||||||
|                 // Therefore, we need to check for EOF first. |         match body_state { | ||||||
|                 match body_state { |             StreamState::Eof => Ok(Async::Ready(Inner::PlainText(body::empty()))), | ||||||
|                     StreamState::Eof => Ok(Async::Ready(Inner::PlainText(body::empty()))), |             StreamState::HasMore => Ok(Async::Ready(Inner::Gzip(Gzip::new(body)))) | ||||||
|                     StreamState::HasMore => Ok(Async::Ready(Inner::Gzip(Gzip::new(body)))) |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|             Pending::Empty => panic!("invalid internal state") |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -171,7 +160,7 @@ impl Gzip { | |||||||
|     fn new(stream: ReadableChunks<Body>) -> Self { |     fn new(stream: ReadableChunks<Body>) -> Self { | ||||||
|         Gzip { |         Gzip { | ||||||
|             buf: BytesMut::with_capacity(INIT_BUFFER_SIZE), |             buf: BytesMut::with_capacity(INIT_BUFFER_SIZE), | ||||||
|             inner: gzip::Decoder::new(Peeked::new(stream)) |             inner: Box::new(gzip::Decoder::new(Peeked::new(stream))), | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @@ -221,7 +210,7 @@ pub struct ReadableChunks<S> { | |||||||
|  |  | ||||||
| enum ReadState { | enum ReadState { | ||||||
|     /// A chunk is ready to be read from. |     /// A chunk is ready to be read from. | ||||||
|     Ready(Chunk, usize), |     Ready(Chunk), | ||||||
|     /// The next chunk isn't ready yet. |     /// The next chunk isn't ready yet. | ||||||
|     NotReady, |     NotReady, | ||||||
|     /// The stream has finished. |     /// The stream has finished. | ||||||
| @@ -330,21 +319,20 @@ impl<S> fmt::Debug for ReadableChunks<S> { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl<S> Read for ReadableChunks<S>  | impl<S> Read for ReadableChunks<S> | ||||||
|     where S: Stream<Item = Chunk, Error = error::Error> | where | ||||||
|  |     S: Stream<Item = Chunk, Error = error::Error>, | ||||||
| { | { | ||||||
|     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { |     fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | ||||||
|         loop { |         loop { | ||||||
|             let ret; |             let ret; | ||||||
|             match self.state { |             match self.state { | ||||||
|                 ReadState::Ready(ref mut chunk, ref mut pos) => { |                 ReadState::Ready(ref mut chunk) => { | ||||||
|                     let chunk_start = *pos; |                     let len = cmp::min(buf.len(), chunk.remaining()); | ||||||
|                     let len = cmp::min(buf.len(), chunk.len() - chunk_start); |  | ||||||
|                     let chunk_end = chunk_start + len; |  | ||||||
|  |  | ||||||
|                     buf[..len].copy_from_slice(&chunk[chunk_start..chunk_end]); |                     buf[..len].copy_from_slice(&chunk[..len]); | ||||||
|                     *pos += len; |                     chunk.advance(len); | ||||||
|                     if *pos == chunk.len() { |                     if chunk.is_empty() { | ||||||
|                         ret = len; |                         ret = len; | ||||||
|                     } else { |                     } else { | ||||||
|                         return Ok(len); |                         return Ok(len); | ||||||
| @@ -382,7 +370,7 @@ impl<S> ReadableChunks<S> | |||||||
|     fn poll_stream(&mut self) -> Poll<StreamState, error::Error> { |     fn poll_stream(&mut self) -> Poll<StreamState, error::Error> { | ||||||
|         match self.stream.poll() { |         match self.stream.poll() { | ||||||
|             Ok(Async::Ready(Some(chunk))) => { |             Ok(Async::Ready(Some(chunk))) => { | ||||||
|                 self.state = ReadState::Ready(chunk, 0); |                 self.state = ReadState::Ready(chunk); | ||||||
|  |  | ||||||
|                 Ok(Async::Ready(StreamState::HasMore)) |                 Ok(Async::Ready(StreamState::HasMore)) | ||||||
|             }, |             }, | ||||||
| @@ -441,3 +429,8 @@ pub fn detect(headers: &mut HeaderMap, body: Body, check_gzip: bool) -> Decoder | |||||||
|         Decoder::plain_text(body) |         Decoder::plain_text(body) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn mem_size_of() { | ||||||
|  |     assert_eq!(::std::mem::size_of::<Decoder>(), 64); | ||||||
|  | } | ||||||
|   | |||||||
| @@ -16,7 +16,9 @@ use super::{decoder, body, Decoder}; | |||||||
| pub struct Response { | pub struct Response { | ||||||
|     status: StatusCode, |     status: StatusCode, | ||||||
|     headers: HeaderMap, |     headers: HeaderMap, | ||||||
|     url: Url, |     // Boxed to save space (11 words to 1 word), and it's not accessed | ||||||
|  |     // frequently internally. | ||||||
|  |     url: Box<Url>, | ||||||
|     body: Decoder, |     body: Decoder, | ||||||
|     version: Version, |     version: Version, | ||||||
| } | } | ||||||
| @@ -29,11 +31,11 @@ impl Response { | |||||||
|         let decoder = decoder::detect(&mut headers, body::wrap(res.into_body()), gzip); |         let decoder = decoder::detect(&mut headers, body::wrap(res.into_body()), gzip); | ||||||
|         debug!("Response: '{}' for {}", status, url); |         debug!("Response: '{}' for {}", status, url); | ||||||
|         Response { |         Response { | ||||||
|             status: status, |             status, | ||||||
|             headers: headers, |             headers, | ||||||
|             url: url, |             url: Box::new(url), | ||||||
|             body: decoder, |             body: decoder, | ||||||
|             version: version, |             version, | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -123,9 +125,9 @@ impl Response { | |||||||
|     #[inline] |     #[inline] | ||||||
|     pub fn error_for_status(self) -> ::Result<Self> { |     pub fn error_for_status(self) -> ::Result<Self> { | ||||||
|         if self.status.is_client_error() { |         if self.status.is_client_error() { | ||||||
|             Err(::error::client_error(self.url, self.status)) |             Err(::error::client_error(*self.url, self.status)) | ||||||
|         } else if self.status.is_server_error() { |         } else if self.status.is_server_error() { | ||||||
|             Err(::error::server_error(self.url, self.status)) |             Err(::error::server_error(*self.url, self.status)) | ||||||
|         } else { |         } else { | ||||||
|             Ok(self) |             Ok(self) | ||||||
|         } |         } | ||||||
| @@ -163,3 +165,4 @@ impl<T> fmt::Debug for Json<T> { | |||||||
|             .finish() |             .finish() | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -335,3 +335,30 @@ pub fn new(mut res: async_impl::Response, timeout: Option<Duration>, thread: Kee | |||||||
|         _thread_handle: thread, |         _thread_handle: thread, | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn mem_size_of() { | ||||||
|  |     assert_eq!(::std::mem::size_of::<Response>(), 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn mem_size_of_readable_async_res() { | ||||||
|  |     assert_eq!(::std::mem::size_of::<async_impl::Response>(), 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn mem_size_of_wait_body() { | ||||||
|  |     assert_eq!(::std::mem::size_of::<WaitBody>(), 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn mem_size_of_url() { | ||||||
|  |     assert_eq!(::std::mem::size_of::<::Url>(), 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[test] | ||||||
|  | fn mem_size_of_readable_chunks_wait_body() { | ||||||
|  |     assert_eq!(::std::mem::size_of::<async_impl::ReadableChunks<WaitBody>>(), 0); | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user