diff --git a/Cargo.toml b/Cargo.toml index c7a67c7..20d6b8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,9 +14,10 @@ version = "0.6.2" nom = "7.1.3" [features] -# default = ["custom-vecdeque"] +default = ["simd"] rc-alloc = [] custom-vecdeque = [] +simd = [] [profile.release] lto = true diff --git a/src/protocol/h1/parser/primitives.rs b/src/protocol/h1/parser/primitives.rs index 2fc4f81..54b1be0 100644 --- a/src/protocol/h1/parser/primitives.rs +++ b/src/protocol/h1/parser/primitives.rs @@ -205,9 +205,9 @@ fn http_status(i: &[u8]) -> IResult<&[u8], (&[u8], u16)> { #[inline] #[allow(clippy::type_complexity)] pub fn parse_request_line(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8], Version)> { - let (i, method) = tchar::take_while_simd(i)?; + let (i, method) = tchar::take_while_fast(i)?; let (i, _) = space(i)?; - let (i, uri) = vchar::take_while_simd(i)?; + let (i, uri) = vchar::take_while_fast(i)?; let (i, _) = space(i)?; let (i, version) = http_version(i)?; let (i, _) = crlf(i)?; @@ -224,7 +224,7 @@ pub fn parse_response_line(i: &[u8]) -> IResult<&[u8], (Version, &[u8], u16, &[u let (i, _) = space(i)?; let (i, (status, code)) = http_status(i)?; let (i, _) = space(i)?; - let (i, reason) = achar::take_while_simd(i)?; + let (i, reason) = achar::take_while_fast(i)?; let (i, _) = crlf(i)?; Ok((i, (version, status, code, reason))) } @@ -236,13 +236,13 @@ pub fn parse_response_line(i: &[u8]) -> IResult<&[u8], (Version, &[u8], u16, &[u #[inline] #[allow(clippy::type_complexity)] pub fn parse_header_or_cookie(i: &[u8]) -> IResult<&[u8], Option<(&[u8], &[u8])>> { - let (i, key) = tchar::take_while_simd(i)?; + let (i, key) = tchar::take_while_fast(i)?; let (i, _) = tag(b":")(i)?; let (i, _) = take_while(is_space)(i)?; if compare_no_case(key, b"cookie") { return Ok((i, None)); } - let (i, val) = achar::take_while_simd(i)?; + let (i, val) = achar::take_while_fast(i)?; let (i, _) = crlf(i)?; Ok((i, Some((key, val)))) } @@ -253,10 +253,10 @@ pub fn parse_header_or_cookie(i: &[u8]) -> IResult<&[u8], Option<(&[u8], &[u8])> /// example: `Content-Length: 42\r\n` #[inline] pub fn parse_header(i: &[u8]) -> IResult<&[u8], (&[u8], &[u8])> { - let (i, key) = tchar::take_while_simd(i)?; + let (i, key) = tchar::take_while_fast(i)?; let (i, _) = tag(b":")(i)?; let (i, _) = take_while(is_space)(i)?; - let (i, val) = achar::take_while_simd(i)?; + let (i, val) = achar::take_while_fast(i)?; let (i, _) = crlf(i)?; Ok((i, (key, val))) } @@ -277,8 +277,8 @@ pub fn parse_single_crumb(i: &[u8], first: bool) -> IResult<&[u8], (&[u8], &[u8] } else { i }; - let (i, key) = ck_char::take_while_simd(i)?; - let (i, val) = opt(tuple((tag(b"="), cv_char::take_while_simd)))(i)?; + let (i, key) = ck_char::take_while_fast(i)?; + let (i, val) = opt(tuple((tag(b"="), cv_char::take_while_fast)))(i)?; match val { Some((_, val)) => Ok((i, (key, val))), diff --git a/src/protocol/utils.rs b/src/protocol/utils.rs index 24acca7..6034e76 100644 --- a/src/protocol/utils.rs +++ b/src/protocol/utils.rs @@ -103,8 +103,10 @@ macro_rules! compile_lookup { mod $name { use $crate::h1::parser::primitives::{CharLookup, CharRanges, CharTable}; pub const LOOKUP: CharLookup = $crate::make_char_lookup!($($t)*); - pub const RANGES: CharRanges = LOOKUP.ranges; pub const TABLE: CharTable = LOOKUP.table; + #[allow(dead_code)] + pub const RANGES: CharRanges = LOOKUP.ranges; + #[allow(dead_code)] pub const LENGTH: i32 = LOOKUP.len; #[inline] @@ -115,11 +117,11 @@ macro_rules! compile_lookup { } #[inline] - #[allow(dead_code)] + #[cfg(feature="simd")] /// Returns the longest string that fits the rule (simd optimized) /// /// *Streaming version* will return a Err::Incomplete(Needed::Unknown) if the pattern reaches the end of the input. - pub fn take_while_simd(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { + fn take_while_simd(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { use std::arch::x86_64::{ _mm_cmpestri, _mm_lddqu_si128, _mm_loadu_si128, _SIDD_CMP_RANGES, _SIDD_LEAST_SIGNIFICANT, _SIDD_UBYTE_OPS, @@ -173,9 +175,9 @@ macro_rules! compile_lookup { } #[inline] - #[allow(dead_code)] + #[cfg(feature="simd")] /// Returns the longest string that fits the rule (simd optimized) - pub fn take_while_complete_simd(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { + fn take_while_complete_simd(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { use std::arch::x86_64::{ _mm_cmpestri, _mm_lddqu_si128, _mm_loadu_si128, _SIDD_CMP_RANGES, _SIDD_LEAST_SIGNIFICANT, _SIDD_UBYTE_OPS, @@ -223,6 +225,74 @@ macro_rules! compile_lookup { )) } } + + #[inline] + #[allow(dead_code)] + /// Returns the longest string that fits the rule (not simd optimized) + /// + /// *Streaming version* will return a Err::Incomplete(Needed::Unknown) if the pattern reaches the end of the input. + pub fn take_while(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { + let mut i = 0; + while i < input.len() { + if unsafe { !TABLE.get_unchecked(*input.get_unchecked(i) as usize) } { + break; + } + i += 1; + } + if i == input.len() { + return Err(nom::Err::Incomplete(nom::Needed::Unknown)); + } else { + unsafe { + Ok(( + input.get_unchecked(i..), + input.get_unchecked(..i), + )) + } + } + } + + #[inline] + #[allow(dead_code)] + /// Returns the longest string that fits the rule (not simd optimized) + pub fn take_while_complete(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { + let mut i = 0; + while i < input.len() { + if unsafe { !TABLE.get_unchecked(*input.get_unchecked(i) as usize) } { + break; + } + i += 1; + } + unsafe { + Ok(( + input.get_unchecked(i..), + input.get_unchecked(..i), + )) + } + } + + #[inline] + #[allow(dead_code)] + /// Returns the longest string that fits the rule (using simd if enabled) + /// + /// *Streaming version* will return a Err::Incomplete(Needed::Unknown) if the pattern reaches the end of the input. + pub fn take_while_fast(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { + #[cfg(feature="simd")] + let result = take_while_simd(input); + #[cfg(not(feature="simd"))] + let result = take_while(input); + result + } + + #[inline] + #[allow(dead_code)] + /// Returns the longest string that fits the rule (using simd if enabled) + pub fn take_while_complete_fast(input: &[u8]) -> nom::IResult<&[u8], &[u8]> { + #[cfg(feature="simd")] + let result = take_while_complete_simd(input); + #[cfg(not(feature="simd"))] + let result = take_while_complete(input); + result + } } } }