Skip to content

Commit 6f8bbd3

Browse files
Simplify integer validation in JSON Path parser (#96)
Replaced range validation with a `safe_int` rule in the grammar to enforce integer bounds. Removed associated constants and validation code. There should be little to no performance loss for numbers less than 1 quadrillion.
1 parent db3450b commit 6f8bbd3

File tree

2 files changed

+32
-28
lines changed

2 files changed

+32
-28
lines changed

src/parser.rs

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use pest::Parser;
1616
#[derive(Parser)]
1717
#[grammar = "parser/grammar/json_path_9535.pest"]
1818
pub(super) struct JSPathParser;
19-
const MAX_VAL: i64 = 9007199254740991; // Maximum safe integer value in JavaScript
20-
const MIN_VAL: i64 = -9007199254740991; // Minimum safe integer value in JavaScript
19+
// const MAX_VAL: i64 = 9007199254740991; // Maximum safe integer value in JavaScript
20+
// const MIN_VAL: i64 = -9007199254740991; // Minimum safe integer value in JavaScript
2121

2222
pub type Parsed<T> = Result<T, JsonPathError>;
2323

@@ -122,13 +122,13 @@ pub fn selector(rule: Pair<Rule>) -> Parsed<Selector> {
122122
validate_js_str(child.as_str().trim())?.to_string(),
123123
)),
124124
Rule::wildcard_selector => Ok(Selector::Wildcard),
125-
Rule::index_selector => Ok(Selector::Index(validate_range(
125+
Rule::index_selector => Ok(Selector::Index(
126126
child
127127
.as_str()
128128
.trim()
129129
.parse::<i64>()
130130
.map_err(|e| (e, "wrong integer"))?,
131-
)?)),
131+
)),
132132
Rule::slice_selector => {
133133
let (start, end, step) = slice_selector(child)?;
134134
Ok(Selector::Slice(start, end, step))
@@ -237,16 +237,7 @@ pub fn singular_query_segments(rule: Pair<Rule>) -> Parsed<Vec<SingularQuerySegm
237237
}
238238
Ok(segments)
239239
}
240-
fn validate_range(val: i64) -> Result<i64, JsonPathError> {
241-
if val > MAX_VAL || val < MIN_VAL {
242-
Err(JsonPathError::InvalidJsonPath(format!(
243-
"Value {} is out of range",
244-
val
245-
)))
246-
} else {
247-
Ok(val)
248-
}
249-
}
240+
250241
pub fn slice_selector(rule: Pair<Rule>) -> Parsed<(Option<i64>, Option<i64>, Option<i64>)> {
251242
let mut start = None;
252243
let mut end = None;
@@ -255,12 +246,12 @@ pub fn slice_selector(rule: Pair<Rule>) -> Parsed<(Option<i64>, Option<i64>, Opt
255246

256247
for r in rule.into_inner() {
257248
match r.as_rule() {
258-
Rule::start => start = Some(validate_range(get_int(r)?)?),
259-
Rule::end => end = Some(validate_range(get_int(r)?)?),
249+
Rule::start => start = Some(get_int(r)?),
250+
Rule::end => end = Some(get_int(r)?),
260251
Rule::step => {
261252
step = {
262253
if let Some(int) = r.into_inner().next() {
263-
Some(validate_range(get_int(int)?)?)
254+
Some(get_int(int)?)
264255
} else {
265256
None
266257
}
@@ -319,15 +310,7 @@ pub fn literal(rule: Pair<Rule>) -> Parsed<Literal> {
319310
if num.contains('.') || num.contains('e') || num.contains('E') {
320311
Ok(Literal::Float(num.parse::<f64>().map_err(|e| (e, num))?))
321312
} else {
322-
let num = num.trim().parse::<i64>().map_err(|e| (e, num))?;
323-
if num > MAX_VAL || num < MIN_VAL {
324-
Err(JsonPathError::InvalidNumber(format!(
325-
"number out of bounds: {}",
326-
num
327-
)))
328-
} else {
329-
Ok(Literal::Int(num))
330-
}
313+
Ok(Literal::Int(num.trim().parse::<i64>().map_err(|e| (e, num))?))
331314
}
332315
}
333316

src/parser/grammar/json_path_9535.pest

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ root = _{"$"}
1111
name_selector = {string}
1212
wildcard_selector = {"*"}
1313
index_selector = {int}
14-
int = { "0" | ("-"? ~ DIGIT1 ~ DIGIT*) }
14+
int = { "0" | ("-"? ~ safe_int) }
1515
step = {":" ~ S ~ int?}
1616
start = {int}
1717
end = {int}
@@ -81,4 +81,25 @@ HEXDIG = _{ DIGIT | "A" | "B" | "C" | "D" | "E" | "F" }
8181
DIGIT = _{ ASCII_DIGIT }
8282
DIGIT1 = _{ ASCII_NONZERO_DIGIT}
8383
ALPHA = { ASCII_ALPHA }
84-
WHITESPACE = _{ " " | "\t" | "\r\n" | "\n" | "\r"}
84+
WHITESPACE = _{ " " | "\t" | "\r\n" | "\n" | "\r"}
85+
86+
// Matches any number less than 9007199254740991 early escape for any number <= 999999999999999
87+
safe_int = _{
88+
(
89+
DIGIT1 ~ DIGIT{0,14} ~ !ASCII_DIGIT // 1 to 15 digits (well below the max)
90+
| '1'..'8' ~ ASCII_DIGIT{15}
91+
| "900" ~ '0'..'6' ~ ASCII_DIGIT{12}
92+
| "90070" ~ ASCII_DIGIT{11}
93+
| "90071" ~ '0'..'8' ~ ASCII_DIGIT{10}
94+
| "900719" ~ '0'..'8' ~ ASCII_DIGIT{9}
95+
| "9007199" ~ '0'..'1' ~ ASCII_DIGIT{8}
96+
| "90071992" ~ '0'..'4' ~ ASCII_DIGIT{7}
97+
| "900719925" ~ '0'..'3' ~ ASCII_DIGIT{6}
98+
| "9007199254" ~ '0'..'6' ~ ASCII_DIGIT{5}
99+
| "90071992547" ~ '0'..'3' ~ ASCII_DIGIT{4}
100+
| "9007199254740" ~ '0'..'8' ~ ASCII_DIGIT{2}
101+
| "90071992547409" ~ '0'..'8' ~ ASCII_DIGIT
102+
| "900719925474099" ~ '0'..'1'
103+
) ~ !ASCII_DIGIT
104+
}
105+

0 commit comments

Comments
 (0)