1 #![cfg(feature = "std")]
2
3 #[macro_use]
4 extern crate criterion;
5 #[macro_use]
6 extern crate combine;
7
8 use std::fmt;
9
10 use {
11 combine::{
12 many, many1,
13 parser::range::{range, take_while1},
14 stream::easy,
15 token, ParseError, Parser, RangeStream,
16 },
17 criterion::{black_box, Bencher, Criterion},
18 };
19
20 #[allow(dead_code)]
21 #[derive(Debug)]
22 struct Request<'a> {
23 method: &'a [u8],
24 uri: &'a [u8],
25 version: &'a [u8],
26 }
27
28 #[allow(dead_code)]
29 #[derive(Debug)]
30 struct Header<'a> {
31 name: &'a [u8],
32 value: Vec<&'a [u8]>,
33 }
34
is_token(c: u8) -> bool35 fn is_token(c: u8) -> bool {
36 !matches!(
37 c,
38 128..=255
39 | 0..=31
40 | b'('
41 | b')'
42 | b'<'
43 | b'>'
44 | b'@'
45 | b','
46 | b';'
47 | b':'
48 | b'\\'
49 | b'"'
50 | b'/'
51 | b'['
52 | b']'
53 | b'?'
54 | b'='
55 | b'{'
56 | b'}'
57 | b' '
58 )
59 }
60
is_horizontal_space(c: u8) -> bool61 fn is_horizontal_space(c: u8) -> bool {
62 c == b' ' || c == b'\t'
63 }
is_space(c: u8) -> bool64 fn is_space(c: u8) -> bool {
65 c == b' '
66 }
is_not_space(c: u8) -> bool67 fn is_not_space(c: u8) -> bool {
68 c != b' '
69 }
is_http_version(c: u8) -> bool70 fn is_http_version(c: u8) -> bool {
71 (b'0'..=b'9').contains(&c) || c == b'.'
72 }
73
end_of_line<'a, Input>() -> impl Parser<Input, Output = u8> where Input: RangeStream<Token = u8, Range = &'a [u8]>,74 fn end_of_line<'a, Input>() -> impl Parser<Input, Output = u8>
75 where
76 Input: RangeStream<Token = u8, Range = &'a [u8]>,
77 {
78 (token(b'\r'), token(b'\n')).map(|_| b'\r').or(token(b'\n'))
79 }
80
message_header<'a, Input>() -> impl Parser<Input, Output = Header<'a>> where Input: RangeStream<Token = u8, Range = &'a [u8]>,81 fn message_header<'a, Input>() -> impl Parser<Input, Output = Header<'a>>
82 where
83 Input: RangeStream<Token = u8, Range = &'a [u8]>,
84 {
85 let message_header_line = (
86 take_while1(is_horizontal_space),
87 take_while1(|c| c != b'\r' && c != b'\n'),
88 end_of_line(),
89 )
90 .map(|(_, line, _)| line);
91
92 struct_parser!(Header {
93 name: take_while1(is_token),
94 _: token(b':'),
95 value: many1(message_header_line),
96 })
97 }
98
99 type HttpRequest<'a> = (Request<'a>, Vec<Header<'a>>);
100
parse_http_request<'a, Input>(input: Input) -> Result<(HttpRequest<'a>, Input), Input::Error> where Input: RangeStream<Token = u8, Range = &'a [u8]>,101 fn parse_http_request<'a, Input>(input: Input) -> Result<(HttpRequest<'a>, Input), Input::Error>
102 where
103 Input: RangeStream<Token = u8, Range = &'a [u8]>,
104 {
105 let http_version = range(&b"HTTP/"[..]).with(take_while1(is_http_version));
106
107 let request_line = struct_parser!(Request {
108 method: take_while1(is_token),
109 _: take_while1(is_space),
110 uri: take_while1(is_not_space),
111 _: take_while1(is_space),
112 version: http_version,
113 });
114
115 let mut request = (
116 request_line,
117 end_of_line(),
118 many(message_header()),
119 end_of_line(),
120 )
121 .map(|(request, _, headers, _)| (request, headers));
122
123 request.parse(input)
124 }
125
126 static REQUESTS: &[u8] = include_bytes!("http-requests.txt");
127
http_requests_small(b: &mut Bencher<'_>)128 fn http_requests_small(b: &mut Bencher<'_>) {
129 http_requests_bench(b, easy::Stream(REQUESTS))
130 }
131
http_requests_large(b: &mut Bencher<'_>)132 fn http_requests_large(b: &mut Bencher<'_>) {
133 use std::iter;
134
135 let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
136 for buf in iter::repeat(REQUESTS).take(5) {
137 buffer.extend_from_slice(buf);
138 }
139 http_requests_bench(b, easy::Stream(&buffer[..]))
140 }
141
http_requests_large_cheap_error(b: &mut Bencher<'_>)142 fn http_requests_large_cheap_error(b: &mut Bencher<'_>) {
143 use std::iter;
144
145 let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
146 for buf in iter::repeat(REQUESTS).take(5) {
147 buffer.extend_from_slice(buf);
148 }
149 http_requests_bench(b, &buffer[..])
150 }
151
http_requests_bench<'a, Input>(b: &mut Bencher<'_>, buffer: Input) where Input: RangeStream<Token = u8, Range = &'a [u8]> + Clone, Input::Error: fmt::Debug,152 fn http_requests_bench<'a, Input>(b: &mut Bencher<'_>, buffer: Input)
153 where
154 Input: RangeStream<Token = u8, Range = &'a [u8]> + Clone,
155 Input::Error: fmt::Debug,
156 {
157 b.iter(|| {
158 let mut buf = black_box(buffer.clone());
159
160 while buf.clone().uncons().is_ok() {
161 match parse_http_request(buf) {
162 Ok(((_, _), b)) => {
163 buf = b;
164 }
165 Err(err) => panic!("{:?}", err),
166 }
167 }
168 });
169 }
170
http_requests(c: &mut Criterion)171 fn http_requests(c: &mut Criterion) {
172 c.bench_function("http_requests_small", http_requests_small);
173 c.bench_function("http_requests_large", http_requests_large);
174 c.bench_function(
175 "http_requests_large_cheap_error",
176 http_requests_large_cheap_error,
177 );
178 }
179
180 criterion_group!(http, http_requests,);
181 criterion_main!(http);
182