1 #[global_allocator]
2 static ALLOC: jemallocator::Jemalloc = jemallocator::Jemalloc;
3
4 use criterion::*;
5
6 use nom::{
7 bytes::complete::{is_a, tag, take_till, take_while},
8 character::complete::{alphanumeric1 as alphanumeric, char, not_line_ending, space0 as space},
9 combinator::opt,
10 multi::many0,
11 sequence::{delimited, pair, terminated, tuple},
12 IResult,
13 };
14
15 use std::collections::HashMap;
16
is_line_ending_or_comment(chr: char) -> bool17 fn is_line_ending_or_comment(chr: char) -> bool {
18 chr == ';' || chr == '\n'
19 }
20
space_or_line_ending(i: &str) -> IResult<&str, &str>21 fn space_or_line_ending(i: &str) -> IResult<&str, &str> {
22 is_a(" \r\n")(i)
23 }
24
category(i: &str) -> IResult<&str, &str>25 fn category(i: &str) -> IResult<&str, &str> {
26 terminated(
27 delimited(char('['), take_while(|c| c != ']'), char(']')),
28 opt(is_a(" \r\n")),
29 )(i)
30 }
31
key_value(i: &str) -> IResult<&str, (&str, &str)>32 fn key_value(i: &str) -> IResult<&str, (&str, &str)> {
33 let (i, key) = alphanumeric(i)?;
34 let (i, _) = tuple((opt(space), tag("="), opt(space)))(i)?;
35 let (i, val) = take_till(is_line_ending_or_comment)(i)?;
36 let (i, _) = opt(space)(i)?;
37 let (i, _) = opt(pair(tag(";"), not_line_ending))(i)?;
38 let (i, _) = opt(space_or_line_ending)(i)?;
39 Ok((i, (key, val)))
40 }
41
keys_and_values_aggregator(i: &str) -> IResult<&str, Vec<(&str, &str)>>42 fn keys_and_values_aggregator(i: &str) -> IResult<&str, Vec<(&str, &str)>> {
43 many0(key_value)(i)
44 }
45
keys_and_values(input: &str) -> IResult<&str, HashMap<&str, &str>>46 fn keys_and_values(input: &str) -> IResult<&str, HashMap<&str, &str>> {
47 match keys_and_values_aggregator(input) {
48 Ok((i, tuple_vec)) => Ok((i, tuple_vec.into_iter().collect())),
49 Err(e) => Err(e),
50 }
51 }
52
category_and_keys(i: &str) -> IResult<&str, (&str, HashMap<&str, &str>)>53 fn category_and_keys(i: &str) -> IResult<&str, (&str, HashMap<&str, &str>)> {
54 pair(category, keys_and_values)(i)
55 }
56
categories_aggregator(i: &str) -> IResult<&str, Vec<(&str, HashMap<&str, &str>)>>57 fn categories_aggregator(i: &str) -> IResult<&str, Vec<(&str, HashMap<&str, &str>)>> {
58 many0(category_and_keys)(i)
59 }
60
categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str>>>61 fn categories(input: &str) -> IResult<&str, HashMap<&str, HashMap<&str, &str>>> {
62 match categories_aggregator(input) {
63 Ok((i, tuple_vec)) => Ok((i, tuple_vec.into_iter().collect())),
64 Err(e) => Err(e),
65 }
66 }
67
bench_ini_str(c: &mut Criterion)68 fn bench_ini_str(c: &mut Criterion) {
69 let s = "[owner]
70 name=John Doe
71 organization=Acme Widgets Inc.
72
73 [database]
74 server=192.0.2.62
75 port=143
76 file=payroll.dat
77 ";
78
79 let mut group = c.benchmark_group("ini str");
80 group.throughput(Throughput::Bytes(s.len() as u64));
81 group.bench_function(BenchmarkId::new("parse", s.len()), |b| {
82 b.iter(|| categories(s).unwrap())
83 });
84 }
85
86 criterion_group!(benches, bench_ini_str);
87 criterion_main!(benches);
88