• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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]>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,74 fn end_of_line<'a, Input>() -> impl Parser<Input, Output = u8>
75 where
76     Input: RangeStream<Token = u8, Range = &'a [u8]>,
77     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
78 {
79     (token(b'\r'), token(b'\n')).map(|_| b'\r').or(token(b'\n'))
80 }
81 
message_header<'a, Input>() -> impl Parser<Input, Output = Header<'a>> where Input: RangeStream<Token = u8, Range = &'a [u8]>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,82 fn message_header<'a, Input>() -> impl Parser<Input, Output = Header<'a>>
83 where
84     Input: RangeStream<Token = u8, Range = &'a [u8]>,
85     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
86 {
87     let message_header_line = (
88         take_while1(is_horizontal_space),
89         take_while1(|c| c != b'\r' && c != b'\n'),
90         end_of_line(),
91     )
92         .map(|(_, line, _)| line);
93 
94     struct_parser!(Header {
95         name: take_while1(is_token),
96         _: token(b':'),
97         value: many1(message_header_line),
98     })
99 }
100 
101 type HttpRequest<'a> = (Request<'a>, Vec<Header<'a>>);
102 
parse_http_request<'a, Input>(input: Input) -> Result<(HttpRequest<'a>, Input), Input::Error> where Input: RangeStream<Token = u8, Range = &'a [u8]>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,103 fn parse_http_request<'a, Input>(input: Input) -> Result<(HttpRequest<'a>, Input), Input::Error>
104 where
105     Input: RangeStream<Token = u8, Range = &'a [u8]>,
106     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
107 {
108     let http_version = range(&b"HTTP/"[..]).with(take_while1(is_http_version));
109 
110     let request_line = struct_parser!(Request {
111         method: take_while1(is_token),
112         _: take_while1(is_space),
113         uri: take_while1(is_not_space),
114         _: take_while1(is_space),
115         version: http_version,
116     });
117 
118     let mut request = (
119         request_line,
120         end_of_line(),
121         many(message_header()),
122         end_of_line(),
123     )
124         .map(|(request, _, headers, _)| (request, headers));
125 
126     request.parse(input)
127 }
128 
129 static REQUESTS: &[u8] = include_bytes!("http-requests.txt");
130 
http_requests_small(b: &mut Bencher<'_>)131 fn http_requests_small(b: &mut Bencher<'_>) {
132     http_requests_bench(b, easy::Stream(REQUESTS))
133 }
134 
http_requests_large(b: &mut Bencher<'_>)135 fn http_requests_large(b: &mut Bencher<'_>) {
136     use std::iter;
137 
138     let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
139     for buf in iter::repeat(REQUESTS).take(5) {
140         buffer.extend_from_slice(buf);
141     }
142     http_requests_bench(b, easy::Stream(&buffer[..]))
143 }
144 
http_requests_large_cheap_error(b: &mut Bencher<'_>)145 fn http_requests_large_cheap_error(b: &mut Bencher<'_>) {
146     use std::iter;
147 
148     let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
149     for buf in iter::repeat(REQUESTS).take(5) {
150         buffer.extend_from_slice(buf);
151     }
152     http_requests_bench(b, &buffer[..])
153 }
154 
http_requests_bench<'a, Input>(b: &mut Bencher<'_>, buffer: Input) where Input: RangeStream<Token = u8, Range = &'a [u8]> + Clone, Input::Error: ParseError<Input::Token, Input::Range, Input::Position> + fmt::Debug,155 fn http_requests_bench<'a, Input>(b: &mut Bencher<'_>, buffer: Input)
156 where
157     Input: RangeStream<Token = u8, Range = &'a [u8]> + Clone,
158     Input::Error: ParseError<Input::Token, Input::Range, Input::Position> + fmt::Debug,
159 {
160     b.iter(|| {
161         let mut buf = black_box(buffer.clone());
162 
163         while buf.clone().uncons().is_ok() {
164             match parse_http_request(buf) {
165                 Ok(((_, _), b)) => {
166                     buf = b;
167                 }
168                 Err(err) => panic!("{:?}", err),
169             }
170         }
171     });
172 }
173 
http_requests(c: &mut Criterion)174 fn http_requests(c: &mut Criterion) {
175     c.bench_function("http_requests_small", http_requests_small);
176     c.bench_function("http_requests_large", http_requests_large);
177     c.bench_function(
178         "http_requests_large_cheap_error",
179         http_requests_large_cheap_error,
180     );
181 }
182 
183 criterion_group!(http, http_requests,);
184 criterion_main!(http);
185