582 lines
17 KiB
Rust
582 lines
17 KiB
Rust
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
use jsonschema::JSONSchema;
|
|
use jsonschema_valid::schemas;
|
|
use serde_json::{from_str, json, Value};
|
|
use std::{fs::File, io::Read, path::Path};
|
|
use valico::json_schema;
|
|
|
|
fn read_json(filepath: &str) -> Value {
|
|
let path = Path::new(filepath);
|
|
let mut file = File::open(&path).unwrap();
|
|
let mut content = String::new();
|
|
file.read_to_string(&mut content).ok().unwrap();
|
|
let data: Value = from_str(&content).unwrap();
|
|
data
|
|
}
|
|
|
|
fn strip_characters(original: &str) -> String {
|
|
original
|
|
.chars()
|
|
.filter(|&c| !"{}:\" ,[]".contains(c))
|
|
.collect()
|
|
}
|
|
|
|
macro_rules! bench {
|
|
(
|
|
name = $name:tt;
|
|
schema = $schema:tt;
|
|
valid = $( $valid:tt ),* $(,)*;
|
|
invalid = $( $invalid:tt ),* $(,)*;
|
|
) => {
|
|
paste::item! {
|
|
#[allow(dead_code)]
|
|
fn [<bench_ $name>](c: &mut Criterion) {
|
|
let schema = json!($schema);
|
|
let validator = JSONSchema::compile(&schema).unwrap();
|
|
let suffix = strip_characters(stringify!($schema));
|
|
c.bench_function(format!("jsonschema-rs {} compile {}", $name, suffix).as_str(), |b| b.iter(|| JSONSchema::compile(&schema).unwrap()));
|
|
$(
|
|
let instance = black_box(json!($valid));
|
|
assert!(validator.is_valid(&instance));
|
|
let suffix = strip_characters(stringify!($valid));
|
|
c.bench_function(format!("jsonschema-rs {} is_valid valid {}", $name, suffix).as_str(), |b| b.iter(|| validator.is_valid(&instance)));
|
|
c.bench_function(format!("jsonschema-rs {} validate valid {}", $name, suffix).as_str(), |b| b.iter(|| validator.validate(&instance).ok()));
|
|
)*
|
|
$(
|
|
let instance = black_box(json!($invalid));
|
|
assert!(!validator.is_valid(&instance));
|
|
let suffix = strip_characters(stringify!($invalid));
|
|
c.bench_function(format!("jsonschema-rs {} is_valid invalid {}", $name, suffix).as_str(), |b| b.iter(|| validator.is_valid(&instance)));
|
|
c.bench_function(format!("jsonschema-rs {} validate invalid {}", $name, suffix).as_str(), |b| b.iter(|| {
|
|
let _: Vec<_> = validator.validate(&instance).unwrap_err().collect();
|
|
}));
|
|
)*
|
|
}
|
|
}
|
|
};
|
|
(
|
|
name = $name:tt;
|
|
schema = $schema:tt;
|
|
invalid = $( $invalid:tt ),* $(,)*;
|
|
) => {
|
|
paste::item! {
|
|
fn [<bench_ $name>](c: &mut Criterion) {
|
|
let schema = json!($schema);
|
|
let validator = JSONSchema::compile(&schema).unwrap();
|
|
let suffix = strip_characters(stringify!($schema));
|
|
c.bench_function(format!("jsonschema-rs {} compile {}", $name, suffix).as_str(), |b| b.iter(|| JSONSchema::compile(&schema).unwrap()));
|
|
$(
|
|
let instance = black_box(json!($invalid));
|
|
assert!(!validator.is_valid(&instance));
|
|
let suffix = strip_characters(stringify!($invalid));
|
|
c.bench_function(format!("jsonschema-rs {} is_valid invalid {}", $name, suffix).as_str(), |b| b.iter(|| validator.is_valid(&instance)));
|
|
c.bench_function(format!("jsonschema-rs {} validate invalid {}", $name, suffix).as_str(), |b| b.iter(|| {
|
|
let _: Vec<_> = validator.validate(&instance).unwrap_err().collect();
|
|
}));
|
|
)*
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn big_schema(c: &mut Criterion) {
|
|
let schema = black_box(read_json("benches/canada_schema.json"));
|
|
let instance = black_box(read_json("benches/canada.json"));
|
|
|
|
// jsonschema
|
|
let validator = JSONSchema::compile(&schema).unwrap();
|
|
c.bench_function("compare jsonschema-rs big schema compile", |b| {
|
|
b.iter(|| JSONSchema::compile(&schema).unwrap())
|
|
});
|
|
c.bench_function("compare jsonschema-rs big schema is_valid", |b| {
|
|
b.iter(|| validator.is_valid(&instance))
|
|
});
|
|
c.bench_function("compare jsonschema-rs big schema validate", |b| {
|
|
b.iter(|| validator.validate(&instance).ok())
|
|
});
|
|
|
|
// jsonschema_valid
|
|
let cfg = jsonschema_valid::Config::from_schema(&schema, Some(schemas::Draft::Draft7)).unwrap();
|
|
c.bench_function("compare jsonschema_valid big schema compile", |b| {
|
|
b.iter(|| {
|
|
jsonschema_valid::Config::from_schema(&schema, Some(schemas::Draft::Draft7)).unwrap()
|
|
})
|
|
});
|
|
c.bench_function("compare jsonschema_valid big schema validate", |b| {
|
|
b.iter(|| jsonschema_valid::validate(&cfg, &instance))
|
|
});
|
|
|
|
// valico
|
|
let mut scope = json_schema::Scope::new();
|
|
let compiled = scope.compile_and_return(schema.clone(), false).unwrap();
|
|
c.bench_function("compare valico big schema compile", |b| {
|
|
b.iter(|| {
|
|
let mut scope = json_schema::Scope::new();
|
|
scope.compile_and_return(schema.clone(), false).unwrap();
|
|
})
|
|
});
|
|
c.bench_function("compare valico big schema validate", |b| {
|
|
b.iter(|| compiled.validate(&instance).is_valid())
|
|
});
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
fn small_schema(c: &mut Criterion) {
|
|
let schema = read_json("benches/small_schema.json");
|
|
let valid =
|
|
black_box(json!([9, "hello", [1, "a", true], {"a": "a", "b": "b", "d": "d"}, 42, 3]));
|
|
let invalid =
|
|
black_box(json!([10, "world", [1, "a", true], {"a": "a", "b": "b", "c": "xy"}, "str", 5]));
|
|
|
|
// jsonschema
|
|
let validator = JSONSchema::compile(&schema).unwrap();
|
|
c.bench_function("compare jsonschema-rs small schema compile", |b| {
|
|
b.iter(|| JSONSchema::compile(&schema).unwrap())
|
|
});
|
|
c.bench_function("compare jsonschema-rs small schema is_valid valid", |b| {
|
|
b.iter(|| validator.is_valid(&valid))
|
|
});
|
|
c.bench_function("compare jsonschema-rs small schema validate valid", |b| {
|
|
b.iter(|| validator.validate(&valid).ok())
|
|
});
|
|
c.bench_function("compare jsonschema-rs small schema is_valid invalid", |b| {
|
|
b.iter(|| validator.is_valid(&invalid))
|
|
});
|
|
c.bench_function("compare jsonschema-rs small schema validate invalid", |b| {
|
|
b.iter(|| {
|
|
let _: Vec<_> = validator.validate(&invalid).unwrap_err().collect();
|
|
})
|
|
});
|
|
|
|
// jsonschema_valid
|
|
let cfg = jsonschema_valid::Config::from_schema(&schema, Some(schemas::Draft::Draft7)).unwrap();
|
|
c.bench_function("compare jsonschema_valid small schema compile", |b| {
|
|
b.iter(|| {
|
|
jsonschema_valid::Config::from_schema(&schema, Some(schemas::Draft::Draft7)).unwrap()
|
|
})
|
|
});
|
|
c.bench_function(
|
|
"compare jsonschema_valid small schema validate valid",
|
|
|b| b.iter(|| jsonschema_valid::validate(&cfg, &valid)),
|
|
);
|
|
c.bench_function(
|
|
"compare jsonschema_valid small schema validate invalid",
|
|
|b| b.iter(|| jsonschema_valid::validate(&cfg, &invalid).ok()),
|
|
);
|
|
|
|
// valico
|
|
let mut scope = json_schema::Scope::new();
|
|
let compiled = scope.compile_and_return(schema.clone(), false).unwrap();
|
|
c.bench_function("compare valico small schema compile", |b| {
|
|
b.iter(|| {
|
|
let mut scope = json_schema::Scope::new();
|
|
scope.compile_and_return(schema.clone(), false).unwrap();
|
|
})
|
|
});
|
|
c.bench_function("compare valico small schema validate valid", |b| {
|
|
b.iter(|| compiled.validate(&valid).is_valid())
|
|
});
|
|
c.bench_function("compare valico small schema validate invalid", |b| {
|
|
b.iter(|| compiled.validate(&invalid).is_valid())
|
|
});
|
|
}
|
|
|
|
bench!(
|
|
name = "additional_items_boolean";
|
|
schema = {"items": [{}, {}, {}], "additionalItems": false};
|
|
valid = [1, 2, 3];
|
|
invalid = [1, 2, 3, 4];
|
|
);
|
|
bench!(
|
|
name = "additional_items_object";
|
|
schema = {"items": [{}, {}, {}], "additionalItems": {"type": "string"}};
|
|
valid = [1, 2, 3, "foo"];
|
|
invalid = [1, 2, 3, 4];
|
|
);
|
|
bench!(
|
|
name = "additional_properties_single";
|
|
schema = {"additionalProperties": {"type": "string"}};
|
|
valid = {"foo": "bar"};
|
|
invalid = {"foo": 1};
|
|
);
|
|
bench!(
|
|
name = "additional_properties_and_properties";
|
|
schema = {"additionalProperties": {"type": "string"}, "properties": {"foo": {}}};
|
|
valid = {"foo": 1};
|
|
invalid = {"foo": 1, "bar": true};
|
|
);
|
|
bench!(
|
|
name = "additional_properties_and_pattern_properties";
|
|
schema = {"additionalProperties": {"type": "string"}, "patternProperties": {"f.*o": {"type": "integer"}}};
|
|
valid = {"foo": 1};
|
|
invalid = {"foo": 1, "bar": true};
|
|
);
|
|
bench!(
|
|
name = "additional_properties_and_properties_and_pattern_properties";
|
|
schema = {"additionalProperties": {"type": "string"}, "properties": {"foo": {}}, "patternProperties": {"f.*a": {"type": "integer"}}};
|
|
valid = {"foo": null, "fza": 2};
|
|
invalid = {"foo": null, "fzo": 2, "bar": true};
|
|
);
|
|
bench!(
|
|
name = "additional_properties_false";
|
|
schema = {"additionalProperties": false};
|
|
valid = {};
|
|
invalid = {"foo": 1};
|
|
);
|
|
bench!(
|
|
name = "additional_properties_false_and_properties";
|
|
schema = {"additionalProperties": false, "properties": {"foo": {}}};
|
|
valid = {"foo": 1};
|
|
invalid = {"foo": 1, "bar": 2};
|
|
);
|
|
bench!(
|
|
name = "additional_properties_false_and_pattern_properties";
|
|
schema = {"additionalProperties": false, "patternProperties": {"f.*o": {"type": "integer"}}};
|
|
valid = {"foo": 1};
|
|
invalid = {"foo": 1, "bar": 2};
|
|
);
|
|
bench!(
|
|
name = "additional_properties_false_and_properties_and_pattern_properties";
|
|
schema = {"additionalProperties": false, "properties": {"foo": {}}, "patternProperties": {"f.*o": {"type": "integer"}}};
|
|
valid = {"foo": 1};
|
|
invalid = {"foo": 1, "fz0": 2, "bar": 2};
|
|
);
|
|
bench!(
|
|
name = "all_of";
|
|
schema = {"allOf": [{"type": "integer"}, {"minimum": 2}]};
|
|
valid = 4;
|
|
invalid = 1;
|
|
);
|
|
bench!(
|
|
name = "any_of";
|
|
schema = {"anyOf": [{"type": "integer"}, {"minimum": 2}]};
|
|
valid = 1;
|
|
invalid = 1.5;
|
|
);
|
|
bench!(
|
|
name = "any_of_multiple_types";
|
|
schema = {"anyOf": [{"type": "integer"}, {"type": "string"}]};
|
|
valid = "foo";
|
|
invalid = null;
|
|
);
|
|
bench!(
|
|
name = "boolean_false";
|
|
schema = false;
|
|
invalid = 1;
|
|
);
|
|
bench!(
|
|
name = "const";
|
|
schema = {"const": 1};
|
|
valid = 1;
|
|
invalid = "foo";
|
|
);
|
|
bench!(
|
|
name = "contains";
|
|
schema = {"contains": {"minimum": 5}};
|
|
valid = [5];
|
|
invalid = [1];
|
|
);
|
|
bench!(
|
|
name = "enum";
|
|
schema = {"enum": [1, 2, 3, 4]};
|
|
valid = 4;
|
|
invalid = 5, "6";
|
|
);
|
|
bench!(
|
|
name = "exclusive_maximum";
|
|
schema = {"exclusiveMaximum": 3};
|
|
valid = 2;
|
|
invalid = 3;
|
|
);
|
|
bench!(
|
|
name = "exclusive_minimum";
|
|
schema = {"exclusiveMinimum": 3};
|
|
valid = 4;
|
|
invalid = 3;
|
|
);
|
|
bench!(
|
|
name = "format_date";
|
|
schema = {"format": "date"};
|
|
valid = "1963-06-19";
|
|
invalid = "06/19/1963";
|
|
);
|
|
bench!(
|
|
name = "format_datetime";
|
|
schema = {"format": "date-time"};
|
|
valid = "1963-06-19T08:30:06.283185Z";
|
|
invalid = "1990-02-31T15:59:60.123-08:00";
|
|
);
|
|
bench!(
|
|
name = "format_email";
|
|
schema = {"format": "email"};
|
|
valid = "test@test.com";
|
|
invalid = "foo";
|
|
);
|
|
bench!(
|
|
name = "format_hostname";
|
|
schema = {"format": "hostname"};
|
|
valid = "www.example.com";
|
|
invalid = "not_a_valid_host_name";
|
|
);
|
|
bench!(
|
|
name = "format_ipv4";
|
|
schema = {"format": "ipv4"};
|
|
valid = "127.0.0.1";
|
|
invalid = "127.0.0.999", "foobar", "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
);
|
|
bench!(
|
|
name = "format_ipv6";
|
|
schema = {"format": "ipv6"};
|
|
valid = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
|
|
invalid = "127.0.0.1", "foobar";
|
|
);
|
|
bench!(
|
|
name = "format_iri";
|
|
schema = {"format": "iri"};
|
|
valid = "http://ƒøø.ßår/?∂éœ=πîx#πîüx";
|
|
invalid = "/abc";
|
|
);
|
|
bench!(
|
|
name = "format_iri_reference";
|
|
schema = {"format": "iri-reference"};
|
|
valid = "http://ƒøø.ßår/?∂éœ=πîx#πîüx";
|
|
invalid = "#ƒräg\\mênt";
|
|
);
|
|
bench!(
|
|
name = "format_json_pointer";
|
|
schema = {"format": "json-pointer"};
|
|
valid = "/foo/bar~0/baz~1/%a";
|
|
invalid = "/foo/bar~";
|
|
);
|
|
bench!(
|
|
name = "format_regex";
|
|
schema = {"format": "regex"};
|
|
valid = r#"([abc])+\s+$"#;
|
|
invalid = "^(abc]";
|
|
);
|
|
bench!(
|
|
name = "format_relative_json_pointer";
|
|
schema = {"format": "relative-json-pointer"};
|
|
valid = "1";
|
|
invalid = "/foo/bar";
|
|
);
|
|
bench!(
|
|
name = "format_time";
|
|
schema = {"format": "time"};
|
|
valid = "08:30:06.283185Z";
|
|
invalid = "01:01:01,1111";
|
|
);
|
|
bench!(
|
|
name = "format_uri_reference";
|
|
schema = {"format": "uri-reference"};
|
|
valid = "http://foo.bar/?baz=qux#quux";
|
|
invalid = "#frag\\ment";
|
|
);
|
|
bench!(
|
|
name = "format_uri_template";
|
|
schema = {"format": "uri-template"};
|
|
valid = "http://example.com/dictionary/{term:1}/{term}";
|
|
invalid = "http://example.com/dictionary/{term:1}/{term";
|
|
);
|
|
bench!(
|
|
name = "items";
|
|
schema = {"items": {"type": "integer"}};
|
|
valid = [1, 2, 3];
|
|
invalid = [1, 2, "x"];
|
|
);
|
|
bench!(
|
|
name = "maximum";
|
|
schema = {"maximum": 3};
|
|
valid = 3;
|
|
invalid = 5;
|
|
);
|
|
bench!(
|
|
name = "max_items";
|
|
schema = {"maxItems": 1};
|
|
valid = [1];
|
|
invalid = [1, 2];
|
|
);
|
|
bench!(
|
|
name = "max_length";
|
|
schema = {"maxLength": 3};
|
|
valid = "foo";
|
|
invalid = "foob";
|
|
);
|
|
bench!(
|
|
name = "max_properties";
|
|
schema = {"maxProperties": 1};
|
|
valid = {"a": 1};
|
|
invalid = {"a": 1, "b": 1};
|
|
);
|
|
bench!(
|
|
name = "minimum";
|
|
schema = {"minimum": 3};
|
|
valid = 5;
|
|
invalid = 1;
|
|
);
|
|
bench!(
|
|
name = "min_items";
|
|
schema = {"minItems": 2};
|
|
valid = [1, 2];
|
|
invalid = [1];
|
|
);
|
|
bench!(
|
|
name = "min_length";
|
|
schema = {"minLength": 3};
|
|
valid = "123";
|
|
invalid = "12";
|
|
);
|
|
bench!(
|
|
name = "min_properties";
|
|
schema = {"minProperties": 2};
|
|
valid = {"a": 1, "b": 2};
|
|
invalid = {"a": 1};
|
|
);
|
|
bench!(
|
|
name = "multiple_of_integer";
|
|
schema = {"multipleOf": 5};
|
|
valid = 125;
|
|
invalid = 212, 212.4;
|
|
);
|
|
bench!(
|
|
name = "multiple_of_number";
|
|
schema = {"multipleOf": 2.5};
|
|
valid = 127.5;
|
|
invalid = 112.2;
|
|
);
|
|
bench!(
|
|
name = "not";
|
|
schema = {"not": {"type": "null"}};
|
|
valid = 1;
|
|
invalid = null;
|
|
);
|
|
bench!(
|
|
name = "one_of";
|
|
schema = {"oneOf": [{"type": "integer"}, {"minimum": 2}]};
|
|
valid = 1;
|
|
invalid = 3;
|
|
);
|
|
bench!(
|
|
name = "pattern";
|
|
schema = {"pattern": "A[0-9]{2}Z"};
|
|
valid = "A11Z";
|
|
invalid = "A119";
|
|
);
|
|
bench!(
|
|
name = "pattern_properties";
|
|
schema = {"patternProperties": {"f.*o": {"type": "integer"}}};
|
|
valid = {"foo": 1};
|
|
invalid = {"foo": "bar", "fooooo": 2};
|
|
);
|
|
bench!(
|
|
name = "properties";
|
|
schema = {"properties": {"foo": {"type": "string"}}};
|
|
valid = {"foo": "bar"};
|
|
invalid = {"foo": 1};
|
|
);
|
|
bench!(
|
|
name = "property_names";
|
|
schema = {"propertyNames": {"maxLength": 3}};
|
|
valid = {"ABC": 1};
|
|
invalid = {"ABCD": 1};
|
|
);
|
|
bench!(
|
|
name = "ref";
|
|
schema = {"items": [{"type": "integer"},{"$ref": "#/items/0"}]};
|
|
valid = [1, 2];
|
|
invalid = [1, "b"];
|
|
);
|
|
bench!(
|
|
name = "required";
|
|
schema = {"required": ["a"]};
|
|
valid = {"a": 1};
|
|
invalid = {};
|
|
);
|
|
bench!(
|
|
name = "type_integer";
|
|
schema = {"type": "integer"};
|
|
valid = 1, 1.0;
|
|
invalid = 1.4, "foo";
|
|
);
|
|
bench!(
|
|
name = "type_string";
|
|
schema = {"type": "string"};
|
|
valid = "foo";
|
|
invalid = 1;
|
|
);
|
|
bench!(
|
|
name = "type_multiple";
|
|
schema = {"type": ["integer", "string"]};
|
|
valid = "foo";
|
|
invalid = [];
|
|
);
|
|
bench!(
|
|
name = "unique_items";
|
|
schema = {"uniqueItems": true};
|
|
valid = [1, 2, 3, 4, 5];
|
|
invalid = [1, 2, 3, 4, 5, 1];
|
|
);
|
|
|
|
criterion_group!(
|
|
keywords,
|
|
bench_additional_items_boolean,
|
|
bench_additional_items_object,
|
|
bench_additional_properties_single,
|
|
bench_additional_properties_and_properties,
|
|
bench_additional_properties_and_pattern_properties,
|
|
bench_additional_properties_and_properties_and_pattern_properties,
|
|
bench_additional_properties_false,
|
|
bench_additional_properties_false_and_properties,
|
|
bench_additional_properties_false_and_pattern_properties,
|
|
bench_additional_properties_false_and_properties_and_pattern_properties,
|
|
bench_all_of,
|
|
bench_any_of,
|
|
bench_any_of_multiple_types,
|
|
bench_boolean_false,
|
|
bench_const,
|
|
bench_contains,
|
|
bench_enum,
|
|
bench_exclusive_maximum,
|
|
bench_exclusive_minimum,
|
|
bench_format_date,
|
|
bench_format_datetime,
|
|
bench_format_email,
|
|
bench_format_hostname,
|
|
bench_format_ipv4,
|
|
bench_format_ipv6,
|
|
bench_format_iri,
|
|
bench_format_iri_reference,
|
|
bench_format_json_pointer,
|
|
bench_format_regex,
|
|
bench_format_relative_json_pointer,
|
|
bench_format_time,
|
|
bench_format_uri_reference,
|
|
bench_format_uri_template,
|
|
bench_items,
|
|
bench_maximum,
|
|
bench_max_items,
|
|
bench_max_length,
|
|
bench_max_properties,
|
|
bench_minimum,
|
|
bench_min_items,
|
|
bench_min_length,
|
|
bench_min_properties,
|
|
bench_multiple_of_integer,
|
|
bench_multiple_of_number,
|
|
bench_not,
|
|
bench_one_of,
|
|
bench_pattern,
|
|
bench_pattern_properties,
|
|
bench_properties,
|
|
bench_property_names,
|
|
bench_ref,
|
|
bench_required,
|
|
bench_type_integer,
|
|
bench_type_string,
|
|
bench_type_multiple,
|
|
bench_unique_items,
|
|
);
|
|
criterion_group!(arbitrary, big_schema, small_schema);
|
|
criterion_main!(arbitrary, keywords);
|