From 114a7b6dba93a7caf8cbf952204d883abbe6a32a Mon Sep 17 00:00:00 2001 From: fakeshadow <24548779@qq.com> Date: Tue, 26 Apr 2022 09:00:21 +0800 Subject: [PATCH] use MaybeUninit type for header parsing. (#107) --- ntex/src/http/h1/decoder.rs | 97 ++++++++++++++++++++++++------------- 1 file changed, 63 insertions(+), 34 deletions(-) diff --git a/ntex/src/http/h1/decoder.rs b/ntex/src/http/h1/decoder.rs index 16c9ff1c..7f3d4fe0 100644 --- a/ntex/src/http/h1/decoder.rs +++ b/ntex/src/http/h1/decoder.rs @@ -212,17 +212,15 @@ impl MessageType for Request { #[allow(clippy::uninit_assumed_init)] fn decode(src: &mut BytesMut) -> Result, ParseError> { - // Unsafe: we read this data only after httparse parses headers into. - // performance bump for pipeline benchmarks. - let mut headers: [HeaderIndex; MAX_HEADERS] = - unsafe { MaybeUninit::uninit().assume_init() }; + let mut headers: [MaybeUninit; MAX_HEADERS] = uninit_array(); - let (len, method, uri, ver, h_len) = { - let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = - unsafe { MaybeUninit::uninit().assume_init() }; + let (len, method, uri, ver, headers) = { + let mut parsed: [MaybeUninit>; MAX_HEADERS] = + uninit_array(); - let mut req = httparse::Request::new(&mut parsed); - match req.parse(src)? { + let mut req = httparse::Request::new(&mut []); + + match req.parse_with_uninit_headers(src, &mut parsed)? { httparse::Status::Complete(len) => { let method = Method::from_bytes(req.method.unwrap().as_bytes()) .map_err(|_| ParseError::Method)?; @@ -232,9 +230,14 @@ impl MessageType for Request { } else { Version::HTTP_10 }; - HeaderIndex::record(src, req.headers, &mut headers); - (len, method, uri, version, req.headers.len()) + ( + len, + method, + uri, + version, + HeaderIndex::record(src, req.headers, &mut headers), + ) } httparse::Status::Partial => { if src.len() >= MAX_BUFFER_SIZE { @@ -249,7 +252,7 @@ impl MessageType for Request { let mut msg = Request::new(); // convert headers - let length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?; + let length = msg.set_headers(&src.split_to(len).freeze(), headers)?; // payload decoder let decoder = match length { @@ -293,17 +296,18 @@ impl MessageType for ResponseHead { #[allow(clippy::uninit_assumed_init)] fn decode(src: &mut BytesMut) -> Result, ParseError> { - // Unsafe: we read this data only after httparse parses headers into. - // performance bump for pipeline benchmarks. - let mut headers: [HeaderIndex; MAX_HEADERS] = - unsafe { MaybeUninit::uninit().assume_init() }; + let mut headers: [MaybeUninit; MAX_HEADERS] = uninit_array(); - let (len, ver, status, h_len) = { - let mut parsed: [httparse::Header<'_>; MAX_HEADERS] = - unsafe { MaybeUninit::uninit().assume_init() }; + let (len, ver, status, headers) = { + let mut parsed: [MaybeUninit>; MAX_HEADERS] = + uninit_array(); - let mut res = httparse::Response::new(&mut parsed); - match res.parse(src)? { + let mut res = httparse::Response::new(&mut []); + match httparse::ParserConfig::default().parse_response_with_uninit_headers( + &mut res, + src, + &mut parsed, + )? { httparse::Status::Complete(len) => { let version = if res.version.unwrap() == 1 { Version::HTTP_11 @@ -312,9 +316,13 @@ impl MessageType for ResponseHead { }; let status = StatusCode::from_u16(res.code.unwrap()) .map_err(|_| ParseError::Status)?; - HeaderIndex::record(src, res.headers, &mut headers); - (len, version, status, res.headers.len()) + ( + len, + version, + status, + HeaderIndex::record(src, res.headers, &mut headers), + ) } httparse::Status::Partial => { return if src.len() >= MAX_BUFFER_SIZE { @@ -331,7 +339,7 @@ impl MessageType for ResponseHead { msg.version = ver; // convert headers - let length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?; + let length = msg.set_headers(&src.split_to(len).freeze(), headers)?; // message payload let decoder = if let PayloadLength::Payload(pl) = length { @@ -360,19 +368,35 @@ pub(super) struct HeaderIndex { } impl HeaderIndex { - pub(super) fn record( + pub(super) fn record<'a>( bytes: &[u8], headers: &[httparse::Header<'_>], - indices: &mut [HeaderIndex], - ) { + indices: &'a mut [MaybeUninit], + ) -> &'a [HeaderIndex] { let bytes_ptr = bytes.as_ptr() as usize; - for (header, indices) in headers.iter().zip(indices.iter_mut()) { - let name_start = header.name.as_ptr() as usize - bytes_ptr; - let name_end = name_start + header.name.len(); - indices.name = (name_start, name_end); - let value_start = header.value.as_ptr() as usize - bytes_ptr; - let value_end = value_start + header.value.len(); - indices.value = (value_start, value_end); + + let init_len = headers + .iter() + .zip(indices.iter_mut()) + .map(|(header, indices)| { + let name_start = header.name.as_ptr() as usize - bytes_ptr; + let name_end = name_start + header.name.len(); + let value_start = header.value.as_ptr() as usize - bytes_ptr; + let value_end = value_start + header.value.len(); + + indices.write(HeaderIndex { + name: (name_start, name_end), + value: (value_start, value_end), + }) + }) + .count(); + + // SAFETY: + // + // The total initialized items are counted by iterator. + unsafe { + &*(&indices[..init_len] as *const [MaybeUninit] + as *const [HeaderIndex]) } } } @@ -671,6 +695,11 @@ impl ChunkedState { } } +fn uninit_array() -> [MaybeUninit; LEN] { + // SAFETY: An uninitialized `[MaybeUninit<_>; LEN]` is valid. + unsafe { MaybeUninit::uninit().assume_init() } +} + #[cfg(test)] mod tests { use super::*;