Skip to content

Commit a88052f

Browse files
committed
simd: split cursor advancing from value matching
This refactors all SIMD modules in order to make the value-matching logic self-contained. Thus, all bytes-cursor manipulations are now grouped and performed once at the end, outside of SIMD logic.
1 parent 0beb74e commit a88052f

File tree

7 files changed

+229
-234
lines changed

7 files changed

+229
-234
lines changed

src/lib.rs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -953,18 +953,20 @@ fn parse_token<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
953953
#[allow(missing_docs)]
954954
// WARNING: Exported for internal benchmarks, not fit for public consumption
955955
pub fn parse_uri<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> {
956-
let start = bytes.pos();
957-
simd::match_uri_vectored(bytes);
958956
// URI must have at least one char
959-
if bytes.pos() == start {
957+
let uri_len = simd::match_uri_vectored(bytes.as_ref());
958+
if uri_len == 0 {
960959
return Err(Error::Token);
961960
}
961+
// SAFETY: these bytes have just been matched here above.
962+
unsafe { bytes.advance(uri_len) };
963+
let uri_slice = bytes.slice();
962964

963-
if next!(bytes) == b' ' {
964-
return Ok(Status::Complete(
965-
// SAFETY: all bytes up till `i` must have been `is_token` and therefore also utf-8.
966-
unsafe { str::from_utf8_unchecked(bytes.slice_skip(1)) },
967-
));
965+
let space_delim = next!(bytes);
966+
if space_delim == b' ' {
967+
// SAFETY: all bytes within `uri_slice` must have been `is_token` and therefore also utf-8.
968+
let uri = unsafe { str::from_utf8_unchecked(uri_slice) };
969+
Ok(Status::Complete(uri))
968970
} else {
969971
Err(Error::Token)
970972
}
@@ -1179,15 +1181,15 @@ fn parse_headers_iter_uninit<'a>(
11791181
#[allow(clippy::never_loop)]
11801182
// parse header name until colon
11811183
let header_name: &str = 'name: loop {
1182-
simd::match_header_name_vectored(bytes);
1183-
let mut b = next!(bytes);
1184-
1185-
// SAFETY: previously bumped by 1 with next! -> always safe.
1186-
let bslice = unsafe { bytes.slice_skip(1) };
1184+
let len = simd::match_header_name_vectored(bytes.as_ref());
1185+
// SAFETY: these bytes have just been matched here above.
1186+
unsafe { bytes.advance(len) };
1187+
let bslice = bytes.slice();
11871188
// SAFETY: previous call to match_header_name_vectored ensured all bytes are valid
11881189
// header name chars, and as such also valid utf-8.
11891190
let name = unsafe { str::from_utf8_unchecked(bslice) };
11901191

1192+
let mut b = next!(bytes);
11911193
if b == b':' {
11921194
break 'name name;
11931195
}
@@ -1213,6 +1215,7 @@ fn parse_headers_iter_uninit<'a>(
12131215
// eat white space between colon and value
12141216
'whitespace_after_colon: loop {
12151217
b = next!(bytes);
1218+
12161219
if b == b' ' || b == b'\t' {
12171220
bytes.slice();
12181221
continue 'whitespace_after_colon;
@@ -1239,7 +1242,9 @@ fn parse_headers_iter_uninit<'a>(
12391242
'value_lines: loop {
12401243
// parse value till EOL
12411244

1242-
simd::match_header_value_vectored(bytes);
1245+
let len = simd::match_header_value_vectored(bytes.as_ref());
1246+
// SAFETY: these bytes have just been matched here above.
1247+
unsafe { bytes.advance(len) };
12431248
let b = next!(bytes);
12441249

12451250
//found_ctl

src/simd/avx2.rs

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,27 @@
1-
use crate::iter::Bytes;
2-
31
#[cfg(target_arch = "x86")]
4-
pub unsafe fn match_uri_vectored(_: &mut Bytes) {
2+
pub(crate) unsafe fn match_uri_vectored(_: &[u8]) -> usize {
53
unreachable!("AVX2 detection should be disabled for x86");
64
}
75

86
#[inline]
97
#[cfg(target_arch = "x86_64")]
108
#[target_feature(enable = "avx2", enable = "sse4.2")]
11-
pub unsafe fn match_uri_vectored(bytes: &mut Bytes) {
12-
while bytes.as_ref().len() >= 32 {
13-
let advance = match_url_char_32_avx(bytes.as_ref());
14-
bytes.advance(advance);
9+
pub(crate) unsafe fn match_uri_vectored(bytes: &[u8]) -> usize {
10+
let mut len = 0usize;
11+
let mut remaining = bytes;
12+
while remaining.len() >= 32 {
13+
let advance = match_url_char_32_avx(remaining);
14+
len = len.saturating_add(advance);
15+
remaining = &bytes[len..];
1516

1617
if advance != 32 {
17-
return;
18+
return len;
1819
}
1920
}
2021
// do both, since avx2 only works when bytes.len() >= 32
21-
super::sse42::match_uri_vectored(bytes)
22+
let advance = super::sse42::match_uri_vectored(remaining);
23+
len = len.saturating_add(advance);
24+
len
2225
}
2326

2427
#[inline(always)]
@@ -64,23 +67,28 @@ unsafe fn match_url_char_32_avx(buf: &[u8]) -> usize {
6467
}
6568

6669
#[cfg(target_arch = "x86")]
67-
pub unsafe fn match_header_value_vectored(_: &mut Bytes) {
70+
pub(crate) unsafe fn match_header_value_vectored(_: &[u8]) -> usize {
6871
unreachable!("AVX2 detection should be disabled for x86");
6972
}
7073

7174
#[cfg(target_arch = "x86_64")]
7275
#[target_feature(enable = "avx2", enable = "sse4.2")]
73-
pub unsafe fn match_header_value_vectored(bytes: &mut Bytes) {
74-
while bytes.as_ref().len() >= 32 {
75-
let advance = match_header_value_char_32_avx(bytes.as_ref());
76-
bytes.advance(advance);
76+
pub(crate) unsafe fn match_header_value_vectored(bytes: &[u8]) -> usize {
77+
let mut len = 0usize;
78+
let mut remaining = bytes;
79+
while remaining.len() >= 32 {
80+
let advance = match_header_value_char_32_avx(remaining);
81+
len = len.saturating_add(advance);
82+
remaining = &bytes[len..];
7783

7884
if advance != 32 {
79-
return;
85+
return len;
8086
}
8187
}
8288
// do both, since avx2 only works when bytes.len() >= 32
83-
super::sse42::match_header_value_vectored(bytes)
89+
let advance = super::sse42::match_header_value_vectored(remaining);
90+
len = len.saturating_add(advance);
91+
len
8492
}
8593

8694
#[inline(always)]
@@ -152,7 +160,7 @@ fn avx2_code_matches_header_value_chars_table() {
152160
}
153161

154162
#[cfg(test)]
155-
unsafe fn byte_is_allowed(byte: u8, f: unsafe fn(bytes: &mut Bytes<'_>)) -> bool {
163+
unsafe fn byte_is_allowed(byte: u8, f: unsafe fn(bytes: &[u8]) -> usize) -> bool {
156164
let slice = [
157165
b'_', b'_', b'_', b'_',
158166
b'_', b'_', b'_', b'_',
@@ -163,11 +171,9 @@ unsafe fn byte_is_allowed(byte: u8, f: unsafe fn(bytes: &mut Bytes<'_>)) -> bool
163171
b'_', b'_', byte, b'_',
164172
b'_', b'_', b'_', b'_',
165173
];
166-
let mut bytes = Bytes::new(&slice);
167-
168-
f(&mut bytes);
169174

170-
match bytes.pos() {
175+
let pos = f(&slice);
176+
match pos {
171177
32 => true,
172178
26 => false,
173179
_ => unreachable!(),

src/simd/mod.rs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ mod swar;
1111
)
1212
),
1313
)))]
14-
pub use self::swar::*;
14+
pub(crate) use self::swar::*;
1515

1616
#[cfg(all(
1717
httparse_simd,
@@ -59,7 +59,7 @@ mod runtime;
5959
target_arch = "x86_64",
6060
),
6161
))]
62-
pub use self::runtime::*;
62+
pub(crate) use self::runtime::*;
6363

6464
#[cfg(all(
6565
httparse_simd,
@@ -72,18 +72,18 @@ pub use self::runtime::*;
7272
))]
7373
mod sse42_compile_time {
7474
#[inline(always)]
75-
pub fn match_header_name_vectored(b: &mut crate::iter::Bytes<'_>) {
76-
super::swar::match_header_name_vectored(b);
75+
pub(crate) fn match_header_name_vectored(b: &[u8]) -> usize {
76+
super::swar::match_header_name_vectored(b)
7777
}
7878

7979
#[inline(always)]
80-
pub fn match_uri_vectored(b: &mut crate::iter::Bytes<'_>) {
80+
pub(crate) fn match_uri_vectored(b: &[u8]) -> usize {
8181
// SAFETY: calls are guarded by a compile time feature check
8282
unsafe { crate::simd::sse42::match_uri_vectored(b) }
8383
}
84-
84+
8585
#[inline(always)]
86-
pub fn match_header_value_vectored(b: &mut crate::iter::Bytes<'_>) {
86+
pub(crate) fn match_header_value_vectored(b: &[u8]) -> usize {
8787
// SAFETY: calls are guarded by a compile time feature check
8888
unsafe { crate::simd::sse42::match_header_value_vectored(b) }
8989
}
@@ -98,7 +98,7 @@ mod sse42_compile_time {
9898
target_arch = "x86_64",
9999
),
100100
))]
101-
pub use self::sse42_compile_time::*;
101+
pub(crate) use self::sse42_compile_time::*;
102102

103103
#[cfg(all(
104104
httparse_simd,
@@ -110,18 +110,18 @@ pub use self::sse42_compile_time::*;
110110
))]
111111
mod avx2_compile_time {
112112
#[inline(always)]
113-
pub fn match_header_name_vectored(b: &mut crate::iter::Bytes<'_>) {
114-
super::swar::match_header_name_vectored(b);
113+
pub(crate) fn match_header_name_vectored(b: &[u8]) -> usize {
114+
super::swar::match_header_name_vectored(b)
115115
}
116116

117117
#[inline(always)]
118-
pub fn match_uri_vectored(b: &mut crate::iter::Bytes<'_>) {
118+
pub(crate) fn match_uri_vectored(b: &[u8]) -> usize {
119119
// SAFETY: calls are guarded by a compile time feature check
120120
unsafe { crate::simd::avx2::match_uri_vectored(b) }
121121
}
122-
122+
123123
#[inline(always)]
124-
pub fn match_header_value_vectored(b: &mut crate::iter::Bytes<'_>) {
124+
pub(crate) fn match_header_value_vectored(b: &[u8]) -> usize {
125125
// SAFETY: calls are guarded by a compile time feature check
126126
unsafe { crate::simd::avx2::match_header_value_vectored(b) }
127127
}
@@ -135,7 +135,7 @@ mod avx2_compile_time {
135135
target_arch = "x86_64",
136136
),
137137
))]
138-
pub use self::avx2_compile_time::*;
138+
pub(crate) use self::avx2_compile_time::*;
139139

140140
#[cfg(all(
141141
httparse_simd,
@@ -149,4 +149,4 @@ mod neon;
149149
target_arch = "aarch64",
150150
httparse_simd_neon_intrinsics,
151151
))]
152-
pub use self::neon::*;
152+
pub(crate) use self::neon::*;

0 commit comments

Comments
 (0)