• 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 #[derive(Debug)]
21 struct Request<'a> {
22     method: &'a [u8],
23     uri: &'a [u8],
24     version: &'a [u8],
25 }
26 
27 #[derive(Debug)]
28 struct Header<'a> {
29     name: &'a [u8],
30     value: Vec<&'a [u8]>,
31 }
32 
is_token(c: u8) -> bool33 fn is_token(c: u8) -> bool {
34     !matches!(
35         c,
36         128..=255
37         | 0..=31
38         | b'('
39         | b')'
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     )
57 }
58 
is_horizontal_space(c: u8) -> bool59 fn is_horizontal_space(c: u8) -> bool {
60     c == b' ' || c == b'\t'
61 }
is_space(c: u8) -> bool62 fn is_space(c: u8) -> bool {
63     c == b' '
64 }
is_not_space(c: u8) -> bool65 fn is_not_space(c: u8) -> bool {
66     c != b' '
67 }
is_http_version(c: u8) -> bool68 fn is_http_version(c: u8) -> bool {
69     (b'0'..=b'9').contains(&c) || c == b'.'
70 }
71 
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>,72 fn end_of_line<'a, Input>() -> impl Parser<Input, Output = u8>
73 where
74     Input: RangeStream<Token = u8, Range = &'a [u8]>,
75     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
76 {
77     (token(b'\r'), token(b'\n')).map(|_| b'\r').or(token(b'\n'))
78 }
79 
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>,80 fn message_header<'a, Input>() -> impl Parser<Input, Output = Header<'a>>
81 where
82     Input: RangeStream<Token = u8, Range = &'a [u8]>,
83     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
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]>, Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,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     Input::Error: ParseError<Input::Token, Input::Range, Input::Position>,
105 {
106     let http_version = range(&b"HTTP/"[..]).with(take_while1(is_http_version));
107 
108     let request_line = struct_parser!(Request {
109         method: take_while1(is_token),
110         _: take_while1(is_space),
111         uri: take_while1(is_not_space),
112         _: take_while1(is_space),
113         version: http_version,
114     });
115 
116     let mut request = (
117         request_line,
118         end_of_line(),
119         many(message_header()),
120         end_of_line(),
121     )
122         .map(|(request, _, headers, _)| (request, headers));
123 
124     request.parse(input)
125 }
126 
127 static REQUESTS: &[u8] = include_bytes!("http-requests.txt");
128 
http_requests_small(b: &mut Bencher<'_>)129 fn http_requests_small(b: &mut Bencher<'_>) {
130     http_requests_bench(b, easy::Stream(REQUESTS))
131 }
132 
http_requests_large(b: &mut Bencher<'_>)133 fn http_requests_large(b: &mut Bencher<'_>) {
134     use std::iter;
135 
136     let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
137     for buf in iter::repeat(REQUESTS).take(5) {
138         buffer.extend_from_slice(buf);
139     }
140     http_requests_bench(b, easy::Stream(&buffer[..]))
141 }
142 
http_requests_large_cheap_error(b: &mut Bencher<'_>)143 fn http_requests_large_cheap_error(b: &mut Bencher<'_>) {
144     use std::iter;
145 
146     let mut buffer = Vec::with_capacity(REQUESTS.len() * 5);
147     for buf in iter::repeat(REQUESTS).take(5) {
148         buffer.extend_from_slice(buf);
149     }
150     http_requests_bench(b, &buffer[..])
151 }
152 
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,153 fn http_requests_bench<'a, Input>(b: &mut Bencher<'_>, buffer: Input)
154 where
155     Input: RangeStream<Token = u8, Range = &'a [u8]> + Clone,
156     Input::Error: ParseError<Input::Token, Input::Range, Input::Position> + fmt::Debug,
157 {
158     b.iter(|| {
159         let mut buf = black_box(buffer.clone());
160 
161         while buf.clone().uncons().is_ok() {
162             match parse_http_request(buf) {
163                 Ok(((_, _), b)) => {
164                     buf = b;
165                 }
166                 Err(err) => panic!("{:?}", err),
167             }
168         }
169     });
170 }
171 
http_requests(c: &mut Criterion)172 fn http_requests(c: &mut Criterion) {
173     c.bench_function("http_requests_small", http_requests_small);
174     c.bench_function("http_requests_large", http_requests_large);
175     c.bench_function(
176         "http_requests_large_cheap_error",
177         http_requests_large_cheap_error,
178     );
179 }
180 
181 criterion_group!(http, http_requests,);
182 criterion_main!(http);
183