use std::hint::black_box;
use criterion::{Criterion, criterion_group, criterion_main};
#[derive(Debug, Clone, PartialEq)]
pub enum Json {
Null,
Bool(bool),
Str(String),
Num(f64),
Array(Vec<Json>),
Object(Vec<(String, Json)>),
}
#[derive(Debug, Clone, PartialEq)]
pub enum JsonZero<'a> {
Null,
Bool(bool),
Str(&'a [u8]),
Num(f64),
Array(Vec<JsonZero<'a>>),
Object(Vec<(&'a [u8], JsonZero<'a>)>),
}
static JSON: &[u8] = include_bytes!("../examples/samples/sample.json");
fn bench_json(c: &mut Criterion) {
c.bench_function("json neure", {
move |b| b.iter(|| black_box(neure_json::JsonParser::parse(black_box(JSON)).unwrap()))
});
c.bench_function("json neure zero", {
move |b| b.iter(|| black_box(neure_json_zero::JsonParser::parse(black_box(JSON)).unwrap()))
});
}
criterion_group!(
name = benches;
config = Criterion::default().configure_from_args();
targets = bench_json
);
criterion_main!(benches);
mod neure_json {
use neure::err::Error;
use neure::prelude::*;
use super::Json;
#[derive(Debug, Default)]
pub struct JsonParser;
pub fn to_str(val: &[u8]) -> Result<&str, Error> {
std::str::from_utf8(val).map_err(|_| Error::Utf8Error)
}
impl JsonParser {
pub fn parse(pat: &[u8]) -> Result<Json, Error> {
let parser = regex::rec_parser(|ctor| {
let ws = u8::is_ascii_whitespace.many0();
let sign = b'+'.or(b'-').opt();
let digit = neu::range(b'0'..=b'9').many1();
let dec = b".".then(digit).pat();
let num = sign.then(digit).then(dec.or(regex::empty()));
let num = ctor::Adapter::dyn_box(
num.pat()
.try_map(to_str)
.try_map(map::from_str::<f64>())
.map(Json::Num),
);
let escape = b'\r'.or(b'\t').or(b'\n').or(b'\\').or(b'\"');
let escape = b'\\'.then(escape);
let cond = neu::regex_cond(regex::not(escape));
let str_val = b'\"'
.not()
.many1()
.set_cond(cond)
.or(escape)
.repeat(0..)
.pat();
let str = ctor::Adapter::dyn_box(
str_val
.try_map(to_str)
.try_map(map::from_str::<String>())
.try_map(|v| Ok(Json::Str(v)))
.enclose(b"\"", b"\""),
);
let bool_t = regex::literal(b"true").try_map(|_| Ok(Json::Bool(true)));
let bool_f = regex::literal(b"false").try_map(|_| Ok(Json::Bool(false)));
let empty = regex::literal(b"empty").try_map(|_| Ok(Json::Null));
let ele = num.or(str.or(bool_t.or(bool_f.or(empty.or(ctor.clone())))));
let ele = ctor::Adapter::rc(ele.suffix(ws).prefix(ws));
let key = regex!((u8::is_ascii_alphabetic.or(u8::is_ascii_digit), b'_')+)
.try_map(to_str)
.try_map(map::from_str::<String>())
.enclose(b"\"", b"\"");
let key = key.suffix(ws).prefix(ws);
let obj = key.sep_once(b":", ele.clone());
let obj = obj.sep(b",").enclose(b"{", b"}").map(Json::Object);
let obj = ctor::Adapter::dyn_box(obj);
let array = ele.sep(b",").enclose(b"[", b"]");
let array = array.try_map(|v| Ok(Json::Array(v)));
obj.or(array)
});
let mut ctx = BytesCtx::new(pat);
ctx.ctor(&parser)
}
}
}
mod neure_json_zero {
use neure::err::Error;
use neure::prelude::*;
use super::JsonZero;
#[derive(Debug, Default)]
pub struct JsonParser;
impl JsonParser {
pub fn parse(pat: &[u8]) -> Result<JsonZero<'_>, Error> {
let parser = regex::rec_parser(|ctor| {
let ws = u8::is_ascii_whitespace.many0();
let sign = regex!((b'+', b'-'){0,1});
let digit = neu::range(b'0'..=b'9').many1();
let dec = b".".then(digit).pat();
let num = sign.then(digit).then(dec.or(regex::empty()));
let num = ctor::Adapter::dyn_box(num.pat()).try_map(&Self::to_digit);
let escape = neu!((b'\r', b'\t', b'\n', b'\\', b'\"'));
let escape = b'\\'.then(escape);
let cond = neu::regex_cond(regex::not(escape));
let str_val = b'\"'
.not()
.many1()
.set_cond(cond)
.or(escape)
.repeat(0..)
.pat();
let str = ctor::Adapter::dyn_box(str_val.enclose(b"\"", b"\""));
let str = str.try_map(|v| Ok(JsonZero::Str(v)));
let bool_t = regex::literal(b"true").try_map(|_| Ok(JsonZero::Bool(true)));
let bool_f = regex::literal(b"false").try_map(|_| Ok(JsonZero::Bool(false)));
let empty = regex::literal(b"empty").try_map(|_| Ok(JsonZero::Null));
let ele = num.or(str.or(bool_t.or(bool_f.or(empty.or(ctor.clone())))));
let ele = ctor::Adapter::rc(ele.suffix(ws).prefix(ws));
let key = regex!((u8::is_ascii_alphabetic.or(u8::is_ascii_digit), b'_')+);
let key = key.enclose(b"\"", b"\"");
let key = key.suffix(ws).prefix(ws);
let obj = key.sep_once(b":", ele.clone());
let obj = ctor::Adapter::dyn_box(obj.sep(b",").enclose(b"{", b"}"))
.try_map(|v| Ok(JsonZero::Object(v)));
let array = ctor::Adapter::dyn_box(ele.sep(b",").enclose(b"[", b"]"));
let array = array.try_map(|v| Ok(JsonZero::Array(v)));
obj.or(array)
});
let mut ctx = BytesCtx::new(pat);
ctx.ctor(&parser)
}
pub fn to_digit<'a>(val: &[u8]) -> Result<JsonZero<'a>, Error> {
std::str::from_utf8(val)
.map_err(|_| Error::Utf8Error)?
.parse::<f64>()
.map_err(|_| Error::FromStr)
.map(JsonZero::Num)
}
}
}