Skip to content

Commit 19c7134

Browse files
committed
perf: use type guards to avoid descending into nodes
Signed-off-by: Dmitry Dygalo <dmitry@dygalo.dev>
1 parent 5f2e617 commit 19c7134

File tree

8 files changed

+192
-14
lines changed

8 files changed

+192
-14
lines changed

crates/jsonschema/src/keywords/boolean.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use crate::paths::{LazyLocation, Location};
22

3-
use crate::{error::ValidationError, keywords::CompilationResult, validator::Validate};
3+
use crate::{
4+
error::ValidationError, keywords::CompilationResult, types::JsonTypeSet, validator::Validate,
5+
};
46
use serde_json::Value;
57

68
pub(crate) struct FalseValidator {
@@ -28,6 +30,10 @@ impl Validate for FalseValidator {
2830
instance,
2931
))
3032
}
33+
34+
fn applicable_types(&self) -> JsonTypeSet {
35+
JsonTypeSet::empty()
36+
}
3137
}
3238

3339
#[cfg(test)]

crates/jsonschema/src/keywords/const_.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
use crate::{
2-
compiler, error::ValidationError, ext::cmp, keywords::CompilationResult, paths::Location,
2+
compiler,
3+
error::ValidationError,
4+
ext::cmp,
5+
keywords::CompilationResult,
6+
paths::Location,
7+
types::{JsonType, JsonTypeSet},
38
validator::Validate,
49
};
510
use serde_json::{Map, Number, Value};
@@ -45,6 +50,10 @@ impl Validate for ConstArrayValidator {
4550
false
4651
}
4752
}
53+
54+
fn applicable_types(&self) -> JsonTypeSet {
55+
JsonTypeSet::empty().insert(JsonType::Array)
56+
}
4857
}
4958

5059
struct ConstBooleanValidator {
@@ -83,6 +92,10 @@ impl Validate for ConstBooleanValidator {
8392
false
8493
}
8594
}
95+
96+
fn applicable_types(&self) -> JsonTypeSet {
97+
JsonTypeSet::empty().insert(JsonType::Boolean)
98+
}
8699
}
87100

88101
struct ConstNullValidator {
@@ -114,6 +127,10 @@ impl Validate for ConstNullValidator {
114127
fn is_valid(&self, instance: &Value) -> bool {
115128
instance.is_null()
116129
}
130+
131+
fn applicable_types(&self) -> JsonTypeSet {
132+
JsonTypeSet::empty().insert(JsonType::Null)
133+
}
117134
}
118135

119136
struct ConstNumberValidator {
@@ -157,6 +174,14 @@ impl Validate for ConstNumberValidator {
157174
false
158175
}
159176
}
177+
178+
fn applicable_types(&self) -> JsonTypeSet {
179+
let mut set = JsonTypeSet::empty().insert(JsonType::Number);
180+
if self.original_value.is_i64() || self.original_value.is_u64() {
181+
set = set.insert(JsonType::Integer);
182+
}
183+
set
184+
}
160185
}
161186

162187
pub(crate) struct ConstObjectValidator {
@@ -198,6 +223,10 @@ impl Validate for ConstObjectValidator {
198223
false
199224
}
200225
}
226+
227+
fn applicable_types(&self) -> JsonTypeSet {
228+
JsonTypeSet::empty().insert(JsonType::Object)
229+
}
201230
}
202231

203232
pub(crate) struct ConstStringValidator {
@@ -239,6 +268,10 @@ impl Validate for ConstStringValidator {
239268
false
240269
}
241270
}
271+
272+
fn applicable_types(&self) -> JsonTypeSet {
273+
JsonTypeSet::empty().insert(JsonType::String)
274+
}
242275
}
243276

244277
#[inline]

crates/jsonschema/src/keywords/enum_.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ impl EnumValidator {
2727
) -> CompilationResult<'a> {
2828
let mut types = JsonTypeSet::empty();
2929
for item in items {
30-
types = types.insert(JsonType::from(item));
30+
types = types.union(JsonTypeSet::from_value(item));
3131
}
3232
Ok(Box::new(EnumValidator {
3333
options: schema.clone(),
@@ -66,13 +66,18 @@ impl Validate for EnumValidator {
6666
false
6767
}
6868
}
69+
70+
fn applicable_types(&self) -> JsonTypeSet {
71+
self.types
72+
}
6973
}
7074

7175
#[derive(Debug)]
7276
pub(crate) struct SingleValueEnumValidator {
7377
value: Value,
7478
options: Value,
7579
location: Location,
80+
types: JsonTypeSet,
7681
}
7782

7883
impl SingleValueEnumValidator {
@@ -86,6 +91,7 @@ impl SingleValueEnumValidator {
8691
options: schema.clone(),
8792
value: value.clone(),
8893
location,
94+
types: JsonTypeSet::from_value(value),
8995
}))
9096
}
9197
}
@@ -111,6 +117,10 @@ impl Validate for SingleValueEnumValidator {
111117
fn is_valid(&self, instance: &Value) -> bool {
112118
cmp::equal(&self.value, instance)
113119
}
120+
121+
fn applicable_types(&self) -> JsonTypeSet {
122+
self.types
123+
}
114124
}
115125

116126
#[inline]

crates/jsonschema/src/keywords/legacy/type_draft_4.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ impl Validate for MultipleTypesValidator {
6868
))
6969
}
7070
}
71+
72+
fn applicable_types(&self) -> JsonTypeSet {
73+
self.types
74+
}
7175
}
7276

7377
pub(crate) struct IntegerTypeValidator {
@@ -105,6 +109,10 @@ impl Validate for IntegerTypeValidator {
105109
))
106110
}
107111
}
112+
113+
fn applicable_types(&self) -> JsonTypeSet {
114+
JsonTypeSet::empty().insert(JsonType::Integer)
115+
}
108116
}
109117

110118
fn is_integer(num: &Number) -> bool {

crates/jsonschema/src/keywords/type_.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ impl Validate for MultipleTypesValidator {
7070
))
7171
}
7272
}
73+
74+
fn applicable_types(&self) -> JsonTypeSet {
75+
self.types
76+
}
7377
}
7478

7579
pub(crate) struct NullTypeValidator {
@@ -103,6 +107,10 @@ impl Validate for NullTypeValidator {
103107
))
104108
}
105109
}
110+
111+
fn applicable_types(&self) -> JsonTypeSet {
112+
JsonTypeSet::empty().insert(JsonType::Null)
113+
}
106114
}
107115

108116
pub(crate) struct BooleanTypeValidator {
@@ -136,6 +144,10 @@ impl Validate for BooleanTypeValidator {
136144
))
137145
}
138146
}
147+
148+
fn applicable_types(&self) -> JsonTypeSet {
149+
JsonTypeSet::empty().insert(JsonType::Boolean)
150+
}
139151
}
140152

141153
pub(crate) struct StringTypeValidator {
@@ -170,6 +182,10 @@ impl Validate for StringTypeValidator {
170182
))
171183
}
172184
}
185+
186+
fn applicable_types(&self) -> JsonTypeSet {
187+
JsonTypeSet::empty().insert(JsonType::String)
188+
}
173189
}
174190

175191
pub(crate) struct ArrayTypeValidator {
@@ -204,6 +220,10 @@ impl Validate for ArrayTypeValidator {
204220
))
205221
}
206222
}
223+
224+
fn applicable_types(&self) -> JsonTypeSet {
225+
JsonTypeSet::empty().insert(JsonType::Array)
226+
}
207227
}
208228

209229
pub(crate) struct ObjectTypeValidator {
@@ -237,6 +257,10 @@ impl Validate for ObjectTypeValidator {
237257
))
238258
}
239259
}
260+
261+
fn applicable_types(&self) -> JsonTypeSet {
262+
JsonTypeSet::empty().insert(JsonType::Object)
263+
}
240264
}
241265

242266
pub(crate) struct NumberTypeValidator {
@@ -270,6 +294,10 @@ impl Validate for NumberTypeValidator {
270294
))
271295
}
272296
}
297+
298+
fn applicable_types(&self) -> JsonTypeSet {
299+
JsonTypeSet::empty().insert(JsonType::Number)
300+
}
273301
}
274302

275303
pub(crate) struct IntegerTypeValidator {
@@ -307,6 +335,10 @@ impl Validate for IntegerTypeValidator {
307335
))
308336
}
309337
}
338+
339+
fn applicable_types(&self) -> JsonTypeSet {
340+
JsonTypeSet::empty().insert(JsonType::Integer)
341+
}
310342
}
311343

312344
fn is_integer(num: &Number) -> bool {

crates/jsonschema/src/node.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use crate::{
55
output::{Annotations, BasicOutput, ErrorDescription, OutputUnit},
66
paths::{LazyLocation, Location},
77
thread::{Shared, SharedWeak},
8+
types::JsonTypeSet,
89
validator::{PartialApplication, Validate},
910
ValidationError,
1011
};
@@ -22,6 +23,7 @@ pub(crate) struct SchemaNode {
2223
validators: Shared<NodeValidators>,
2324
location: Location,
2425
absolute_path: Option<Arc<Uri<String>>>,
26+
applicable_types: JsonTypeSet,
2527
}
2628

2729
// Separate type used only during compilation for handling recursive references
@@ -35,6 +37,7 @@ struct PendingTarget {
3537
validators: SharedWeak<NodeValidators>,
3638
location: Location,
3739
absolute_path: Option<Arc<Uri<String>>>,
40+
applicable_types: JsonTypeSet,
3841
}
3942

4043
enum NodeValidators {
@@ -89,6 +92,15 @@ struct ArrayValidatorEntry {
8992
absolute_location: Option<Arc<Uri<String>>>,
9093
}
9194

95+
fn merged_applicable_types<'a, I>(validators: I) -> JsonTypeSet
96+
where
97+
I: Iterator<Item = &'a BoxedValidator>,
98+
{
99+
validators.fold(JsonTypeSet::all(), |acc, validator| {
100+
acc.intersection(validator.applicable_types())
101+
})
102+
}
103+
92104
impl PendingSchemaNode {
93105
pub(crate) fn new() -> Self {
94106
PendingSchemaNode {
@@ -101,6 +113,7 @@ impl PendingSchemaNode {
101113
validators: Shared::downgrade(&node.validators),
102114
location: node.location.clone(),
103115
absolute_path: node.absolute_path.clone(),
116+
applicable_types: node.applicable_types,
104117
};
105118
self.cell
106119
.set(target)
@@ -134,6 +147,7 @@ impl PendingTarget {
134147
validators,
135148
location: self.location.clone(),
136149
absolute_path: self.absolute_path.clone(),
150+
applicable_types: self.applicable_types,
137151
}
138152
}
139153
}
@@ -162,9 +176,13 @@ impl Validate for PendingSchemaNode {
162176

163177
impl SchemaNode {
164178
pub(crate) fn from_boolean(ctx: &Context<'_>, validator: Option<BoxedValidator>) -> SchemaNode {
179+
let applicable_types = validator
180+
.as_ref()
181+
.map_or(JsonTypeSet::all(), |validator| validator.applicable_types());
165182
SchemaNode {
166183
location: ctx.location().clone(),
167184
absolute_path: ctx.base_uri(),
185+
applicable_types,
168186
validators: Shared::new(NodeValidators::Boolean { validator }),
169187
}
170188
}
@@ -186,10 +204,13 @@ impl SchemaNode {
186204
absolute_location,
187205
}
188206
})
189-
.collect();
207+
.collect::<Vec<_>>();
208+
let applicable_types =
209+
merged_applicable_types(validators.iter().map(|entry| &entry.validator));
190210
SchemaNode {
191211
location: ctx.location().clone(),
192212
absolute_path,
213+
applicable_types,
193214
validators: Shared::new(NodeValidators::Keyword(KeywordValidators {
194215
unmatched_keywords,
195216
validators,
@@ -211,10 +232,13 @@ impl SchemaNode {
211232
absolute_location,
212233
}
213234
})
214-
.collect();
235+
.collect::<Vec<_>>();
236+
let applicable_types =
237+
merged_applicable_types(validators.iter().map(|entry| &entry.validator));
215238
SchemaNode {
216239
location: ctx.location().clone(),
217240
absolute_path,
241+
applicable_types,
218242
validators: Shared::new(NodeValidators::Array { validators }),
219243
}
220244
}
@@ -228,6 +252,7 @@ impl SchemaNode {
228252
validators: self.validators.clone(),
229253
location,
230254
absolute_path,
255+
applicable_types: self.applicable_types,
231256
}
232257
}
233258

@@ -466,6 +491,9 @@ impl Validate for SchemaNode {
466491
}
467492

468493
fn is_valid(&self, instance: &Value) -> bool {
494+
if !self.applicable_types.contains_value_type(instance) {
495+
return false;
496+
}
469497
match self.validators.as_ref() {
470498
// If we only have one validator then calling it's `is_valid` directly does
471499
// actually save the 20 or so instructions required to call the `slice::Iter::all`

0 commit comments

Comments
 (0)