1 #![cfg(feature = "std")]
2 
3 #[macro_use]
4 extern crate criterion;
5 
6 #[macro_use]
7 extern crate combine;
8 
9 use std::{collections::HashMap, fs::File, io::Read, path::Path};
10 
11 use {
12     combine::{
13         error::{Commit, ParseError},
14         parser::{
15             char::{char, digit, spaces, string},
16             choice::{choice, optional},
17             function::parser,
18             repeat::{many, many1, sep_by},
19             sequence::between,
20             token::{any, satisfy, satisfy_map},
21         },
22         stream::{
23             buffered,
24             position::{self, SourcePosition},
25             IteratorStream,
26         },
27         EasyParser, Parser, Stream, StreamOnce,
28     },
29     criterion::{black_box, Bencher, Criterion},
30 };
31 
32 #[derive(PartialEq, Debug)]
33 enum Value {
34     Number(f64),
35     String(String),
36     Bool(bool),
37     Null,
38     Object(HashMap<String, Value>),
39     Array(Vec<Value>),
40 }
41 
lex<Input, P>(p: P) -> impl Parser<Input, Output = P::Output> where P: Parser<Input>, Input: Stream<Token = char>, <Input as StreamOnce>::Error: ParseError< <Input as StreamOnce>::Token, <Input as StreamOnce>::Range, <Input as StreamOnce>::Position, >,42 fn lex<Input, P>(p: P) -> impl Parser<Input, Output = P::Output>
43 where
44     P: Parser<Input>,
45     Input: Stream<Token = char>,
46     <Input as StreamOnce>::Error: ParseError<
47         <Input as StreamOnce>::Token,
48         <Input as StreamOnce>::Range,
49         <Input as StreamOnce>::Position,
50     >,
51 {
52     p.skip(spaces())
53 }
54 
integer<Input>() -> impl Parser<Input, Output = i64> where Input: Stream<Token = char>,55 fn integer<Input>() -> impl Parser<Input, Output = i64>
56 where
57     Input: Stream<Token = char>,
58 {
59     lex(many1(digit()))
60         .map(|s: String| {
61             let mut n = 0;
62             for c in s.chars() {
63                 n = n * 10 + (c as i64 - '0' as i64);
64             }
65             n
66         })
67         .expected("integer")
68 }
69 
number<Input>() -> impl Parser<Input, Output = f64> where Input: Stream<Token = char>,70 fn number<Input>() -> impl Parser<Input, Output = f64>
71 where
72     Input: Stream<Token = char>,
73 {
74     let i = char('0').map(|_| 0.0).or(integer().map(|x| x as f64));
75     let fractional = many(digit()).map(|digits: String| {
76         let mut magnitude = 1.0;
77         digits.chars().fold(0.0, |acc, d| {
78             magnitude /= 10.0;
79             match d.to_digit(10) {
80                 Some(d) => acc + (d as f64) * magnitude,
81                 None => panic!("Not a digit"),
82             }
83         })
84     });
85 
86     let exp = satisfy(|c| c == 'e' || c == 'E').with(optional(char('-')).and(integer()));
87     lex(optional(char('-'))
88         .and(i)
89         .map(|(sign, n)| if sign.is_some() { -n } else { n })
90         .and(optional(char('.')).with(fractional))
91         .map(|(x, y)| if x >= 0.0 { x + y } else { x - y })
92         .and(optional(exp))
93         .map(|(n, exp_option)| match exp_option {
94             Some((sign, e)) => {
95                 let e = if sign.is_some() { -e } else { e };
96                 n * 10.0f64.powi(e as i32)
97             }
98             None => n,
99         }))
100     .expected("number")
101 }
102 
json_char<Input>() -> impl Parser<Input, Output = char> where Input: Stream<Token = char>,103 fn json_char<Input>() -> impl Parser<Input, Output = char>
104 where
105     Input: Stream<Token = char>,
106 {
107     parser(|input: &mut Input| {
108         let (c, committed) = any().parse_lazy(input).into_result()?;
109         let mut back_slash_char = satisfy_map(|c| {
110             Some(match c {
111                 '"' => '"',
112                 '\\' => '\\',
113                 '/' => '/',
114                 'b' => '\u{0008}',
115                 'f' => '\u{000c}',
116                 'n' => '\n',
117                 'r' => '\r',
118                 't' => '\t',
119                 _ => return None,
120             })
121         });
122         match c {
123             '\\' => committed.combine(|_| back_slash_char.parse_stream(input).into_result()),
124             '"' => Err(Commit::Peek(Input::Error::empty(input.position()).into())),
125             _ => Ok((c, committed)),
126         }
127     })
128 }
129 
json_string<Input>() -> impl Parser<Input, Output = String> where Input: Stream<Token = char>,130 fn json_string<Input>() -> impl Parser<Input, Output = String>
131 where
132     Input: Stream<Token = char>,
133 {
134     between(char('"'), lex(char('"')), many(json_char())).expected("string")
135 }
136 
object<Input>() -> impl Parser<Input, Output = Value> where Input: Stream<Token = char>,137 fn object<Input>() -> impl Parser<Input, Output = Value>
138 where
139     Input: Stream<Token = char>,
140 {
141     let field = (json_string(), lex(char(':')), json_value()).map(|t| (t.0, t.2));
142     let fields = sep_by(field, lex(char(',')));
143     between(lex(char('{')), lex(char('}')), fields)
144         .map(Value::Object)
145         .expected("object")
146 }
147 
148 #[inline]
json_value<Input>() -> impl Parser<Input, Output = Value> where Input: Stream<Token = char>,149 fn json_value<Input>() -> impl Parser<Input, Output = Value>
150 where
151     Input: Stream<Token = char>,
152 {
153     json_value_()
154 }
155 
156 // We need to use `parser!` to break the recursive use of `value` to prevent the returned parser
157 // from containing itself
158 parser! {
159     #[inline]
160     fn json_value_[Input]()(Input) -> Value
161         where [ Input: Stream<Token = char> ]
162     {
163         let array = between(
164             lex(char('[')),
165             lex(char(']')),
166             sep_by(json_value(), lex(char(','))),
167         ).map(Value::Array);
168 
169         choice((
170             json_string().map(Value::String),
171             object(),
172             array,
173             number().map(Value::Number),
174             lex(string("false").map(|_| Value::Bool(false))),
175             lex(string("true").map(|_| Value::Bool(true))),
176             lex(string("null").map(|_| Value::Null)),
177         ))
178     }
179 }
180 
181 #[test]
json_test()182 fn json_test() {
183     use self::Value::*;
184 
185     let input = r#"{
186     "array": [1, ""],
187     "object": {},
188     "number": 3.14,
189     "small_number": 0.59,
190     "int": -100,
191     "exp": -1e2,
192     "exp_neg": 23e-2,
193     "true": true,
194     "false"  : false,
195     "null" : null
196 }"#;
197     let result = json_value().easy_parse(input);
198     let expected = Object(
199         vec![
200             ("array", Array(vec![Number(1.0), String("".to_string())])),
201             ("object", Object(HashMap::new())),
202             ("number", Number(3.14)),
203             ("small_number", Number(0.59)),
204             ("int", Number(-100.)),
205             ("exp", Number(-1e2)),
206             ("exp_neg", Number(23E-2)),
207             ("true", Bool(true)),
208             ("false", Bool(false)),
209             ("null", Null),
210         ]
211         .into_iter()
212         .map(|(k, v)| (k.to_string(), v))
213         .collect(),
214     );
215     match result {
216         Ok(result) => assert_eq!(result, (expected, "")),
217         Err(e) => {
218             println!("{}", e);
219             panic!();
220         }
221     }
222 }
223 
test_data() -> String224 fn test_data() -> String {
225     let mut data = String::new();
226     File::open(&Path::new(&"benches/data.json"))
227         .and_then(|mut file| file.read_to_string(&mut data))
228         .unwrap();
229     data
230 }
231 
bench_json(bencher: &mut Bencher<'_>)232 fn bench_json(bencher: &mut Bencher<'_>) {
233     let data = test_data();
234     let mut parser = json_value();
235     match parser.easy_parse(position::Stream::new(&data[..])) {
236         Ok((Value::Array(_), _)) => (),
237         Ok(_) => panic!(),
238         Err(err) => {
239             println!("{}", err);
240             panic!();
241         }
242     }
243     bencher.iter(|| {
244         let result = parser.easy_parse(position::Stream::new(&data[..]));
245         black_box(result)
246     });
247 }
248 
bench_json_core_error(bencher: &mut Bencher<'_>)249 fn bench_json_core_error(bencher: &mut Bencher<'_>) {
250     let data = test_data();
251     let mut parser = json_value();
252     match parser.parse(position::Stream::new(&data[..])) {
253         Ok((Value::Array(_), _)) => (),
254         Ok(_) => panic!(),
255         Err(err) => {
256             println!("{}", err);
257             panic!();
258         }
259     }
260     bencher.iter(|| {
261         let result = parser.parse(position::Stream::new(&data[..]));
262         black_box(result)
263     });
264 }
265 
bench_json_core_error_no_position(bencher: &mut Bencher<'_>)266 fn bench_json_core_error_no_position(bencher: &mut Bencher<'_>) {
267     let data = test_data();
268     let mut parser = json_value();
269     match parser.parse(&data[..]) {
270         Ok((Value::Array(_), _)) => (),
271         Ok(_) => panic!(),
272         Err(err) => {
273             println!("{}", err);
274             panic!();
275         }
276     }
277     bencher.iter(|| {
278         let result = parser.parse(&data[..]);
279         black_box(result)
280     });
281 }
282 
bench_buffered_json(bencher: &mut Bencher<'_>)283 fn bench_buffered_json(bencher: &mut Bencher<'_>) {
284     let data = test_data();
285     bencher.iter(|| {
286         let buffer =
287             buffered::Stream::new(position::Stream::new(IteratorStream::new(data.chars())), 1);
288         let mut parser = json_value();
289         match parser.easy_parse(position::Stream::with_positioner(
290             buffer,
291             SourcePosition::default(),
292         )) {
293             Ok((Value::Array(v), _)) => {
294                 black_box(v);
295             }
296             Ok(_) => panic!(),
297             Err(err) => {
298                 println!("{}", err);
299                 panic!();
300             }
301         }
302     });
303 }
304 
bench(c: &mut Criterion)305 fn bench(c: &mut Criterion) {
306     c.bench_function("json", bench_json);
307     c.bench_function("json_core_error", bench_json_core_error);
308     c.bench_function(
309         "json_core_error_no_position",
310         bench_json_core_error_no_position,
311     );
312     c.bench_function("buffered_json", bench_buffered_json);
313 }
314 
315 criterion_group!(json, bench);
316 criterion_main!(json);
317