perf: Improved dec_octet parsing
Removed the list folding method and reverted to a standard try_parser/parse_this_then method as used in the rest of the parser
This commit is contained in:
@@ -11,3 +11,4 @@
|
|||||||
## v2.0.1
|
## v2.0.1
|
||||||
|
|
||||||
- Improved parsing performance slightly and reduced memory usage up to 50%
|
- Improved parsing performance slightly and reduced memory usage up to 50%
|
||||||
|
- Significantly improved IPV4 parsing performance
|
||||||
|
|||||||
@@ -421,39 +421,73 @@ fn parse_ipv4address(str: String) {
|
|||||||
Ok(#(oct1 <> "." <> oct2 <> "." <> oct3 <> "." <> oct4, rest))
|
Ok(#(oct1 <> "." <> oct2 <> "." <> oct3 <> "." <> oct4, rest))
|
||||||
}
|
}
|
||||||
|
|
||||||
const octet_matches = [
|
|
||||||
["2", "5", "012345"],
|
|
||||||
["2", "01234", "0123456789"],
|
|
||||||
["1", "0123456789", "0123456789"],
|
|
||||||
["123456789", "0123456789"],
|
|
||||||
["0123456789"],
|
|
||||||
]
|
|
||||||
|
|
||||||
// dec-octet = DIGIT ; 0-9
|
// dec-octet = DIGIT ; 0-9
|
||||||
// / %x31-39 DIGIT ; 10-99
|
// / %x31-39 DIGIT ; 10-99
|
||||||
// / "1" 2DIGIT ; 100-199
|
// / "1" 2DIGIT ; 100-199
|
||||||
// / "2" %x30-34 DIGIT ; 200-249
|
// / "2" %x30-34 DIGIT ; 200-249
|
||||||
// / "25" %x30-35 ; 250-255
|
// / "25" %x30-35 ; 250-255
|
||||||
fn parse_dec_octet(str: String) -> Result(#(String, String), Nil) {
|
pub fn parse_dec_octet(str: String) -> Result(#(String, String), Nil) {
|
||||||
list.fold_until(octet_matches, Error(Nil), fn(_, chars) {
|
try_parsers(
|
||||||
case
|
[
|
||||||
list.fold_until(chars, #("", str), fn(acc, charset) {
|
parse_this_then(_, [
|
||||||
let #(octet, str) = acc
|
fn(str) {
|
||||||
case string.pop_grapheme(str) {
|
case str {
|
||||||
Error(_) -> Stop(#("", ""))
|
"2" as l <> rest -> Ok(#(l, rest))
|
||||||
Ok(#(char, rest)) -> {
|
_ -> Error(Nil)
|
||||||
case string.contains(charset, char) {
|
|
||||||
True -> Continue(#(octet <> char, rest))
|
|
||||||
False -> Stop(#("", ""))
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
fn(str) {
|
||||||
|
case str {
|
||||||
|
"5" as l <> rest -> Ok(#(l, rest))
|
||||||
|
_ -> Error(Nil)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
fn(str) {
|
||||||
|
case str {
|
||||||
|
"0" as l <> rest
|
||||||
|
| "1" as l <> rest
|
||||||
|
| "2" as l <> rest
|
||||||
|
| "3" as l <> rest
|
||||||
|
| "4" as l <> rest
|
||||||
|
| "5" as l <> rest -> Ok(#(l, rest))
|
||||||
|
_ -> Error(Nil)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
{
|
]),
|
||||||
#("", _) -> Continue(Error(Nil))
|
parse_this_then(_, [
|
||||||
#(octet, rest) -> Stop(Ok(#(octet, rest)))
|
fn(str) {
|
||||||
|
case str {
|
||||||
|
"2" as l <> rest -> Ok(#(l, rest))
|
||||||
|
_ -> Error(Nil)
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
fn(str) {
|
||||||
|
case str {
|
||||||
|
"0" as l <> rest
|
||||||
|
| "1" as l <> rest
|
||||||
|
| "2" as l <> rest
|
||||||
|
| "3" as l <> rest
|
||||||
|
| "4" as l <> rest -> Ok(#(l, rest))
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parse_digit,
|
||||||
|
]),
|
||||||
|
parse_this_then(_, [
|
||||||
|
fn(str) {
|
||||||
|
case str {
|
||||||
|
"1" as l <> rest -> Ok(#(l, rest))
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parse_digit,
|
||||||
|
parse_digit,
|
||||||
|
]),
|
||||||
|
parse_this_then(_, [parse_digit_nz, parse_digit]),
|
||||||
|
parse_digit,
|
||||||
|
],
|
||||||
|
str,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// reg-name = *( unreserved / pct-encoded / sub-delims )
|
// reg-name = *( unreserved / pct-encoded / sub-delims )
|
||||||
@@ -727,6 +761,21 @@ fn parse_digit(str: String) -> Result(#(String, String), Nil) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_digit_nz(str: String) -> Result(#(String, String), Nil) {
|
||||||
|
case str {
|
||||||
|
"1" as l <> rest
|
||||||
|
| "2" as l <> rest
|
||||||
|
| "3" as l <> rest
|
||||||
|
| "4" as l <> rest
|
||||||
|
| "5" as l <> rest
|
||||||
|
| "6" as l <> rest
|
||||||
|
| "7" as l <> rest
|
||||||
|
| "8" as l <> rest
|
||||||
|
| "9" as l <> rest -> Ok(#(l, rest))
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_digits(str: String, digits: String) {
|
fn parse_digits(str: String, digits: String) {
|
||||||
case parse_digit(str) {
|
case parse_digit(str) {
|
||||||
Ok(#(d, rest)) -> {
|
Ok(#(d, rest)) -> {
|
||||||
|
|||||||
@@ -154,15 +154,23 @@ pub fn parse_this_then(
|
|||||||
to_parse str: String,
|
to_parse str: String,
|
||||||
with parsers: List(fn(String) -> Result(#(String, String), Nil)),
|
with parsers: List(fn(String) -> Result(#(String, String), Nil)),
|
||||||
) -> Result(#(String, String), Nil) {
|
) -> Result(#(String, String), Nil) {
|
||||||
list.fold_until(parsers, Ok(#("", str)), fn(acc, parser) {
|
do_parse_this_then(str, "", parsers)
|
||||||
let assert Ok(#(res, str)) = acc
|
}
|
||||||
case parser(str) {
|
|
||||||
Ok(#(res2, rest)) -> {
|
fn do_parse_this_then(
|
||||||
Continue(Ok(#(res <> res2, rest)))
|
to_parse str: String,
|
||||||
|
from initial: String,
|
||||||
|
with parsers: List(fn(String) -> Result(#(String, String), Nil)),
|
||||||
|
) -> Result(#(String, String), Nil) {
|
||||||
|
case parsers {
|
||||||
|
[] -> Ok(#(initial, str))
|
||||||
|
[head, ..tail] -> {
|
||||||
|
case head(str) {
|
||||||
|
Ok(#(res, rest)) -> do_parse_this_then(rest, initial <> res, tail)
|
||||||
|
Error(_) -> Error(Nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Error(Nil) -> Stop(Error(Nil))
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_multiple(
|
pub fn parse_multiple(
|
||||||
|
|||||||
@@ -12,6 +12,28 @@ pub fn main() {
|
|||||||
|
|
||||||
parse_benchmark()
|
parse_benchmark()
|
||||||
// reg_name_benchmark()
|
// reg_name_benchmark()
|
||||||
|
// ip_benchmark()
|
||||||
|
}
|
||||||
|
|
||||||
|
@target(erlang)
|
||||||
|
pub fn ip_benchmark() {
|
||||||
|
benchmark.run(
|
||||||
|
[
|
||||||
|
benchmark.Function("ip_benchmark", fn(data) {
|
||||||
|
fn() {
|
||||||
|
let _ = parser.parse_dec_octet(data)
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
benchmark.Data("173", "173"),
|
||||||
|
benchmark.Data("5", "5"),
|
||||||
|
benchmark.Data("200", "200"),
|
||||||
|
benchmark.Data("255", "255"),
|
||||||
|
benchmark.Data("fail", "2b"),
|
||||||
|
],
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@target(erlang)
|
@target(erlang)
|
||||||
|
|||||||
Reference in New Issue
Block a user