Skip to content

Commit 7a9d15a

Browse files
committed
refactor: cleaner header parsing
Builds off seanmonstar#134 (swar), seanmonstar#138 (Bytes cursor) Cleaner, faster and less macros ! - Broke down header-parsing into clean conceptual steps (whilst being faster !) - Added InnerResult allowing for idiomatic `?` early-exits, removing need for parsing-helper macros - Removed macros.rs, leaving only `byte_map!` (response header-parsing macros should become functions) - convert response header-parser, supporting its quirks
1 parent 6e7ba52 commit 7a9d15a

File tree

10 files changed

+966
-504
lines changed

10 files changed

+966
-504
lines changed

benches/parse.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,11 @@ fn version(c: &mut Criterion) {
152152
.bench_function(name, |b| b.iter(|| {
153153
black_box({
154154
let mut b = httparse::_benchable::Bytes::new(input);
155-
httparse::_benchable::parse_version(&mut b).unwrap()
155+
match httparse::_benchable::parse_version(&mut b) {
156+
// Somewhat awkward, but this is internal code so it's ok.
157+
Ok(_) | Err(None) => (),
158+
Err(Some(e)) => panic!("parse_version failed: {}", e),
159+
}
156160
});
157161
}));
158162
}

src/iter.rs

Lines changed: 90 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1,157 @@
1-
use core::slice;
2-
use core::convert::TryInto;
3-
use core::convert::TryFrom;
4-
51
#[allow(missing_docs)]
62
pub struct Bytes<'a> {
7-
slice: &'a [u8],
8-
pos: usize
3+
start: *const u8,
4+
end: *const u8,
5+
cursor: *const u8,
6+
phantom: core::marker::PhantomData<&'a ()>,
97
}
108

119
#[allow(missing_docs)]
1210
impl<'a> Bytes<'a> {
1311
#[inline]
1412
pub fn new(slice: &'a [u8]) -> Bytes<'a> {
13+
let start = slice.as_ptr();
14+
let end = unsafe { start.add(slice.len()) };
15+
let cursor = start;
1516
Bytes {
16-
slice,
17-
pos: 0
17+
start,
18+
end,
19+
cursor,
20+
phantom: core::marker::PhantomData,
1821
}
1922
}
2023

2124
#[inline]
2225
pub fn pos(&self) -> usize {
23-
self.pos
26+
self.cursor as usize - self.start as usize
2427
}
2528

2629
#[inline]
2730
pub fn peek(&self) -> Option<u8> {
28-
self.peek_ahead(0)
31+
if self.cursor < self.end {
32+
// SAFETY: bounds checked
33+
Some(unsafe { *self.cursor })
34+
} else {
35+
None
36+
}
2937
}
3038

3139
#[inline]
3240
pub fn peek_ahead(&self, n: usize) -> Option<u8> {
33-
self.slice.get(self.pos + n).copied()
41+
let ptr = unsafe { self.cursor.add(n) };
42+
if ptr < self.end {
43+
// SAFETY: bounds checked
44+
Some(unsafe { *ptr })
45+
} else {
46+
None
47+
}
3448
}
35-
49+
3650
#[inline]
37-
pub fn peek_n<U: TryFrom<&'a[u8]>>(&self, n: usize) -> Option<U> {
38-
self.slice.get(self.pos..self.pos + n)?.try_into().ok()
51+
pub fn peek_n<U>(&self) -> Option<U> {
52+
let n = core::mem::size_of::<U>();
53+
// Boundary check then read array from ptr
54+
if self.len() >= n {
55+
let ptr = self.cursor as *const U;
56+
let x = unsafe { core::ptr::read_unaligned(ptr) };
57+
Some(x)
58+
} else {
59+
None
60+
}
3961
}
4062

4163
#[inline]
4264
pub unsafe fn bump(&mut self) {
43-
debug_assert!(self.pos < self.slice.len(), "overflow");
44-
self.pos += 1;
65+
self.advance(1)
4566
}
4667

47-
#[allow(unused)]
4868
#[inline]
4969
pub unsafe fn advance(&mut self, n: usize) {
50-
debug_assert!(self.pos + n <= self.slice.len(), "overflow");
51-
self.pos += n;
70+
self.cursor = self.cursor.add(n);
71+
debug_assert!(self.cursor <= self.end, "overflow");
5272
}
5373

5474
#[inline]
5575
pub fn len(&self) -> usize {
56-
self.slice.len()
76+
self.end as usize - self.cursor as usize
5777
}
5878

5979
#[inline]
6080
pub fn slice(&mut self) -> &'a [u8] {
6181
// not moving position at all, so it's safe
62-
unsafe {
63-
self.slice_skip(0)
64-
}
82+
let slice = unsafe { slice_from_ptr_range(self.start, self.cursor) };
83+
self.commit();
84+
slice
6585
}
6686

87+
// TODO: this is an anti-pattern, should be removed
6788
#[inline]
6889
pub unsafe fn slice_skip(&mut self, skip: usize) -> &'a [u8] {
69-
debug_assert!(self.pos >= skip);
70-
let head_pos = self.pos - skip;
71-
let ptr = self.slice.as_ptr();
72-
let head = slice::from_raw_parts(ptr, head_pos);
73-
let tail = slice::from_raw_parts(ptr.add(self.pos), self.slice.len() - self.pos);
74-
self.pos = 0;
75-
self.slice = tail;
90+
debug_assert!(self.cursor.sub(skip) >= self.start);
91+
let head = slice_from_ptr_range(self.start, self.cursor.sub(skip));
92+
self.commit();
7693
head
7794
}
95+
96+
#[inline]
97+
pub fn commit(&mut self) {
98+
self.start = self.cursor
99+
}
78100

79101
#[inline]
80102
pub unsafe fn advance_and_commit(&mut self, n: usize) {
81-
debug_assert!(self.pos + n <= self.slice.len(), "overflow");
82-
self.pos += n;
83-
let ptr = self.slice.as_ptr();
84-
let tail = slice::from_raw_parts(ptr.add(n), self.slice.len() - n);
85-
self.pos = 0;
86-
self.slice = tail;
103+
self.advance(n);
104+
self.commit();
105+
}
106+
107+
#[inline]
108+
pub fn as_ptr(&self) -> *const u8 {
109+
self.cursor
110+
}
111+
112+
#[inline]
113+
pub fn start(&self) -> *const u8 {
114+
self.start
115+
}
116+
117+
#[inline]
118+
pub fn end(&self) -> *const u8 {
119+
self.end
120+
}
121+
122+
#[inline]
123+
pub unsafe fn set_cursor(&mut self, ptr: *const u8) {
124+
debug_assert!(ptr >= self.start);
125+
debug_assert!(ptr <= self.end);
126+
self.cursor = ptr;
87127
}
88128
}
89129

90130
impl<'a> AsRef<[u8]> for Bytes<'a> {
91131
#[inline]
92132
fn as_ref(&self) -> &[u8] {
93-
&self.slice[self.pos..]
133+
unsafe { slice_from_ptr_range(self.cursor, self.end) }
94134
}
95135
}
96136

137+
#[inline]
138+
unsafe fn slice_from_ptr_range<'a>(start: *const u8, end: *const u8) -> &'a [u8] {
139+
debug_assert!(start <= end);
140+
core::slice::from_raw_parts(start, end as usize - start as usize)
141+
}
142+
97143
impl<'a> Iterator for Bytes<'a> {
98144
type Item = u8;
99145

100146
#[inline]
101147
fn next(&mut self) -> Option<u8> {
102-
if self.slice.len() > self.pos {
103-
let b = unsafe { *self.slice.get_unchecked(self.pos) };
104-
self.pos += 1;
105-
Some(b)
148+
if self.cursor < self.end {
149+
// SAFETY: bounds checked
150+
unsafe {
151+
let b = *self.cursor;
152+
self.bump();
153+
Some(b)
154+
}
106155
} else {
107156
None
108157
}

0 commit comments

Comments
 (0)