Closed lo48576 closed 2 years ago
Baseline:
test parser::tests::bench_parse_host ... bench: 2,367 ns/iter (+/- 46)
test parser::tests::bench_parse_normal ... bench: 1,919 ns/iter (+/- 37)
test resolve::tests::bench_resolve ... bench: 3,521 ns/iter (+/- 52)
commit 4428ca066d59863a39dea676e65f8463414f81cb
Author: YOSHIOKA Takuma <lo48576@hard-wi.red>
Date: 2022-01-08 04:10:25 +0900
Add bench tests
diff --git a/src/lib.rs b/src/lib.rs
index 593c7a3560fa..fd89efd6cace 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -115,6 +115,9 @@
#![warn(missing_docs)]
#![warn(clippy::missing_docs_in_private_items)]
#![cfg_attr(not(feature = "std"), no_std)]
+#![feature(test)]
+
+extern crate test;
// Inform users that `serde-alloc` is required when `serde` and `alloc` is enabled.
#[cfg(all(
diff --git a/src/parser.rs b/src/parser.rs
index f4ff1fa9e115..200af2e3d6b4 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -40,3 +40,40 @@ impl<'a, S: Spec> From<&'a RiReferenceStr<S>> for RiReferenceComponents<'a, S> {
.expect("Should never fail: `RiReferenceStr` should be already validated")
}
}
+
+#[cfg(test)]
+mod tests {
+ use crate::types::IriReferenceStr;
+
+ #[bench]
+ fn bench_parse_normal(b: &mut test::Bencher) {
+ b.iter(|| {
+ let s = concat!(
+ "scheme://user:pw@sub.example.com:8080/a/b/c/%30/%31/%32%33%34",
+ "/foo/foo/../../../foo.foo/foo/foo/././././//////foo",
+ "/\u{03B1}\u{03B2}\u{03B3}/\u{03B1}\u{03B2}\u{03B3}/\u{03B1}\u{03B2}\u{03B3}",
+ "?k1=v1&k2=v2&k3=v3#fragment"
+ );
+ let iri = IriReferenceStr::new(s);
+ iri
+ });
+ }
+
+ #[bench]
+ fn bench_parse_host(b: &mut test::Bencher) {
+ b.iter(|| {
+ let domain = "scheme://sub.sub.sub.example.com:8080/a/b/c";
+ let v4 = "scheme://198.51.100.23:8080/a/b/c";
+ let v6 = "scheme://[2001:db8:0123::cafe]:8080/a/b/c";
+ let v6v4 = "scheme://[2001:db8::198.51.100.23]:8080/a/b/c";
+ let vfuture = "scheme://[v2.ipv2-does-not-exist]:8080/a/b/c";
+ (
+ IriReferenceStr::new(domain),
+ IriReferenceStr::new(v4),
+ IriReferenceStr::new(v6),
+ IriReferenceStr::new(v6v4),
+ IriReferenceStr::new(vfuture),
+ )
+ });
+ }
+}
diff --git a/src/resolve.rs b/src/resolve.rs
index 10c075837a2b..8dbe9c61372e 100644
--- a/src/resolve.rs
+++ b/src/resolve.rs
@@ -1120,3 +1120,23 @@ mod tests {
}
}
}
+
+#[cfg(feature = "alloc")]
+#[cfg(test)]
+mod tests_bench {
+ use super::*;
+
+ use crate::types::{IriReferenceStr, IriStr};
+
+ #[bench]
+ fn bench_resolve(b: &mut test::Bencher) {
+ let base = IriStr::new("https://sub.example.com/foo1/foo2/foo3/foo4/foo5")
+ .expect("should be valid IRI");
+ let rel = IriReferenceStr::new(concat!(
+ "bar1/bar2/bar3/../bar4/../../bar5/bar6/bar7/../../../../..",
+ "/bar8/../../../bar9/././././././bar10/bar11",
+ ))
+ .expect("should be valid IRI");
+ b.iter(|| resolve(rel, base));
+ }
+}
Baseline:
commit f3451954d84b8765c5747be63b35e1c6022aca27
Author: YOSHIOKA Takuma <lo48576@hard-wi.red>
Date: 2022-01-08 00:51:44 +0900
Add examples and config to use `cargo flamegraph`
Run `cargo +nightly flamegraph --example flame` or `... resolve`.
diff --git a/Cargo.toml b/Cargo.toml
index 8d573b68c78e..cfaf41638a15 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -51,6 +51,9 @@ serde = { version = "1.0.103", default-features = false, features = ["derive"],
[dev-dependencies]
serde_test = "1.0.104"
+[profile.release]
+debug = true
+
[badges]
maintenance = { status = "actively-developed" }
travis-ci = { repository = "lo48576/iri-string" }
diff --git a/examples/flame.rs b/examples/flame.rs
new file mode 100644
index 000000000000..657459104a5f
--- /dev/null
+++ b/examples/flame.rs
@@ -0,0 +1,28 @@
+#![feature(bench_black_box)]
+
+use iri_string::types::IriReferenceStr;
+
+fn main() {
+ for _ in 0..1000000 {
+ let s = concat!(
+ "scheme://user:pw@sub.example.com:8080/a/b/c/%30/%31/%32%33%34",
+ "/foo/foo/../../../foo.foo/foo/foo/././././//////foo",
+ "/\u{03B1}\u{03B2}\u{03B3}/\u{03B1}\u{03B2}\u{03B3}/\u{03B1}\u{03B2}\u{03B3}",
+ "?k1=v1&k2=v2&k3=v3#fragment"
+ );
+
+ let domain = "scheme://sub.sub.sub.example.com:8080/a/b/c";
+ let v4 = "scheme://198.51.100.23:8080/a/b/c";
+ let v6 = "scheme://[2001:db8:0123::cafe]:8080/a/b/c";
+ let v6v4 = "scheme://[2001:db8::198.51.100.23]:8080/a/b/c";
+ let vfuture = "scheme://[v2.ipv2-does-not-exist]:8080/a/b/c";
+ let _ = std::hint::black_box((
+ IriReferenceStr::new(s),
+ IriReferenceStr::new(domain),
+ IriReferenceStr::new(v4),
+ IriReferenceStr::new(v6),
+ IriReferenceStr::new(v6v4),
+ IriReferenceStr::new(vfuture),
+ ));
+ }
+}
diff --git a/examples/resolve.rs b/examples/resolve.rs
new file mode 100644
index 000000000000..45aeb22faef4
--- /dev/null
+++ b/examples/resolve.rs
@@ -0,0 +1,18 @@
+#![feature(bench_black_box)]
+
+use iri_string::resolve::resolve;
+use iri_string::types::{IriAbsoluteStr, IriReferenceStr};
+
+fn main() {
+ let base = IriAbsoluteStr::new("https://sub.example.com/foo1/foo2/foo3/foo4/foo5")
+ .expect("should be valid IRI");
+ let rel = IriReferenceStr::new(concat!(
+ "bar1/bar2/bar3/../bar4/../../bar5/bar6/bar7/../../../../..",
+ "/bar8/../../../bar9/././././././bar10/bar11",
+ ))
+ .expect("should be valid IRI");
+ for _ in 0..1000000 {
+ let resolved = resolve(rel, base);
+ drop(std::hint::black_box(resolved));
+ }
+}
Run cargo flamegraph
.
commit 9f5d1c99b0454b677af377f19a7b0e40b2272431
Author: YOSHIOKA Takuma <lo48576@hard-wi.red>
Date: 2022-01-08 06:50:43 +0900
Utilize knowledge about valid IRIs in `RiReferenceComponent::from`
This makes `resolve()` quite fast (3,521 ns/iter to 1,362 ns/iter
for some bench test).
diff --git a/src/parser.rs b/src/parser.rs
index 200af2e3d6b4..9b01029ce57c 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -2,15 +2,14 @@
use core::marker::PhantomData;
-use nom::combinator::all_consuming;
+use crate::spec::Spec;
+use crate::types::RiReferenceStr;
-use crate::{spec::Spec, types::RiReferenceStr};
-
-use self::details::decompose_uri_reference;
pub(crate) use self::details::{absolute_uri, fragment, path, relative_ref, uri, uri_reference};
pub(crate) mod char;
mod details;
+mod validated;
/// Components of an IRI reference.
///
@@ -34,15 +33,80 @@ pub(crate) struct RiReferenceComponents<'a, S> {
}
impl<'a, S: Spec> From<&'a RiReferenceStr<S>> for RiReferenceComponents<'a, S> {
+ #[inline]
fn from(s: &'a RiReferenceStr<S>) -> Self {
- all_consuming(decompose_uri_reference::<(), S>)(s.as_str())
- .map(|(_rest, components)| components)
- .expect("Should never fail: `RiReferenceStr` should be already validated")
+ validated::decompose_iri_reference(s)
}
}
#[cfg(test)]
mod tests {
+ use super::*;
+
+ use crate::types::IriReferenceStr;
+
+ /// Creates an `IriReferenceStr`.
+ fn iri_ref(s: &str) -> &IriReferenceStr {
+ IriReferenceStr::new(s).expect("should be valid")
+ }
+
+ #[test]
+ fn absolute_slashes() {
+ let c0 = RiReferenceComponents::from(iri_ref("scheme:"));
+ assert_eq!(c0.authority, None);
+ assert_eq!(c0.path, "");
+
+ let c1 = RiReferenceComponents::from(iri_ref("scheme:/"));
+ assert_eq!(c1.authority, None);
+ assert_eq!(c1.path, "/");
+
+ let c2 = RiReferenceComponents::from(iri_ref("scheme://"));
+ assert_eq!(c2.authority, Some(""));
+ assert_eq!(c2.path, "");
+
+ let c3 = RiReferenceComponents::from(iri_ref("scheme:///"));
+ assert_eq!(c3.authority, Some(""));
+ assert_eq!(c3.path, "/");
+
+ let c4 = RiReferenceComponents::from(iri_ref("scheme:////"));
+ assert_eq!(c4.authority, Some(""));
+ assert_eq!(c4.path, "//");
+
+ let c5 = RiReferenceComponents::from(iri_ref("scheme://///"));
+ assert_eq!(c5.authority, Some(""));
+ assert_eq!(c5.path, "///");
+ }
+
+ #[test]
+ fn relative_slashes() {
+ let c0 = RiReferenceComponents::from(iri_ref(""));
+ assert_eq!(c0.authority, None);
+ assert_eq!(c0.path, "");
+
+ let c1 = RiReferenceComponents::from(iri_ref("/"));
+ assert_eq!(c1.authority, None);
+ assert_eq!(c1.path, "/");
+
+ let c2 = RiReferenceComponents::from(iri_ref("//"));
+ assert_eq!(c2.authority, Some(""));
+ assert_eq!(c2.path, "");
+
+ let c3 = RiReferenceComponents::from(iri_ref("///"));
+ assert_eq!(c3.authority, Some(""));
+ assert_eq!(c3.path, "/");
+
+ let c4 = RiReferenceComponents::from(iri_ref("////"));
+ assert_eq!(c4.authority, Some(""));
+ assert_eq!(c4.path, "//");
+
+ let c5 = RiReferenceComponents::from(iri_ref("/////"));
+ assert_eq!(c5.authority, Some(""));
+ assert_eq!(c5.path, "///");
+ }
+}
+
+#[cfg(test)]
+mod tests_bench {
use crate::types::IriReferenceStr;
#[bench]
@@ -54,8 +118,7 @@ mod tests {
"/\u{03B1}\u{03B2}\u{03B3}/\u{03B1}\u{03B2}\u{03B3}/\u{03B1}\u{03B2}\u{03B3}",
"?k1=v1&k2=v2&k3=v3#fragment"
);
- let iri = IriReferenceStr::new(s);
- iri
+ IriReferenceStr::new(s)
});
}
diff --git a/src/parser/details.rs b/src/parser/details.rs
index 5e63eb8e65b4..a993dc2cba78 100644
--- a/src/parser/details.rs
+++ b/src/parser/details.rs
@@ -1,7 +1,5 @@
//! Parser implementatitons.
-use core::marker::PhantomData;
-
use nom::{
branch::alt,
bytes::complete::{tag, take_while, take_while1, take_while_m_n},
@@ -14,7 +12,7 @@ use nom::{
};
use crate::{
- parser::{char::is_sub_delim, RiReferenceComponents},
+ parser::char::is_sub_delim,
spec::{Spec, SpecInternal, UriSpec},
};
@@ -47,32 +45,6 @@ pub(crate) fn uri<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
)(i)
}
-/// Parses RFC 3986 / 3987 IRI and returns components.
-fn decompose_uri<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
- i: &'a str,
-) -> IResult<&'a str, RiReferenceComponents<'a, S>, E> {
- context(
- "uri",
- tuple((
- scheme,
- char_(':'),
- decompose_hier_part::<E, S>,
- opt(preceded(char_('?'), query::<E, S>)),
- opt(preceded(char_('#'), fragment::<E, S>)),
- ))
- .map(|(scheme, _colon, (authority, path), query, fragment)| {
- RiReferenceComponents {
- scheme: Some(scheme),
- authority,
- path,
- query,
- fragment,
- _spec: PhantomData,
- }
- }),
- )(i)
-}
-
/// Parses `hier-part` and `ihier-part` rules.
fn hier_part<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
i: &'a str,
@@ -91,24 +63,6 @@ fn hier_part<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
)(i)
}
-/// Parses `hier-part` and `ihier-part` rules and returns authority and path.
-fn decompose_hier_part<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
- i: &'a str,
-) -> IResult<&'a str, (Option<&'a str>, &'a str), E> {
- context(
- "hier-part",
- alt((
- preceded(
- tag("//"),
- pair(authority::<E, S>.map(Some), path_abempty::<E, S>),
- ),
- path_absolute::<E, S>.map(|path| (None, path)),
- path_rootless::<E, S>.map(|path| (None, path)),
- path_empty::<E>.map(|path| (None, path)),
- )),
- )(i)
-}
-
/// Parses RFC 3986 / 3987 IRI reference.
pub(crate) fn uri_reference<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
i: &'a str,
@@ -116,20 +70,6 @@ pub(crate) fn uri_reference<'a, E: ParseError<&'a str> + ContextError<&'a str>,
context("uri_reference", alt((uri::<E, S>, relative_ref::<E, S>)))(i)
}
-/// Parses RFC 3986 / 3987 IRI reference and returns components.
-pub(crate) fn decompose_uri_reference<
- 'a,
- E: ParseError<&'a str> + ContextError<&'a str>,
- S: Spec,
->(
- i: &'a str,
-) -> IResult<&'a str, RiReferenceComponents<'a, S>, E> {
- context(
- "uri_reference",
- alt((decompose_uri::<E, S>, decompose_relative_ref::<E, S>)),
- )(i)
-}
-
/// Parses RFC 3986 / 3987 absolute IRI.
pub(crate) fn absolute_uri<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
i: &'a str,
@@ -159,30 +99,6 @@ pub(crate) fn relative_ref<'a, E: ParseError<&'a str> + ContextError<&'a str>, S
)(i)
}
-/// Parses RFC 3986 / 3987 relative reference and returns components.
-fn decompose_relative_ref<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
- i: &'a str,
-) -> IResult<&'a str, RiReferenceComponents<'a, S>, E> {
- context(
- "relative_ref",
- tuple((
- decompose_relative_part::<E, S>,
- opt(preceded(char_('?'), query::<E, S>)),
- opt(preceded(char_('#'), fragment::<E, S>)),
- ))
- .map(
- |((authority, path), query, fragment)| RiReferenceComponents {
- scheme: None,
- authority,
- path,
- query,
- fragment,
- _spec: PhantomData,
- },
- ),
- )(i)
-}
-
/// Parses `relative_part` rule.
fn relative_part<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
i: &'a str,
@@ -198,24 +114,6 @@ fn relative_part<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
)(i)
}
-/// Parses `relative_part` rule and returns authority and path.
-fn decompose_relative_part<'a, E: ParseError<&'a str> + ContextError<&'a str>, S: Spec>(
- i: &'a str,
-) -> IResult<&'a str, (Option<&'a str>, &'a str), E> {
- context(
- "relative-part",
- alt((
- preceded(
- tag("//"),
- pair(authority::<E, S>.map(Some), path_abempty::<E, S>),
- ),
- path_absolute::<E, S>.map(|path| (None, path)),
- path_noscheme::<E, S>.map(|path| (None, path)),
- path_empty.map(|path| (None, path)),
- )),
- )(i)
-}
-
/// Parses `scheme` rule.
fn scheme<'a, E: ParseError<&'a str> + ContextError<&'a str>>(
i: &'a str,
@@ -853,64 +751,4 @@ mod tests {
&["", "/", "//", "///", "////", "/////"]
);
}
-
- #[test]
- fn test_decompose_hier_part_only_slashes() {
- assert_complete!(decompose_hier_part::<Error<'_>, IriSpec>, "", (None, ""));
- assert_complete!(decompose_hier_part::<Error<'_>, IriSpec>, "/", (None, "/"));
- assert_complete!(
- decompose_hier_part::<Error<'_>, IriSpec>,
- "//",
- (Some(""), "")
- );
- assert_complete!(
- decompose_hier_part::<Error<'_>, IriSpec>,
- "///",
- (Some(""), "/")
- );
- assert_complete!(
- decompose_hier_part::<Error<'_>, IriSpec>,
- "////",
- (Some(""), "//")
- );
- assert_complete!(
- decompose_hier_part::<Error<'_>, IriSpec>,
- "/////",
- (Some(""), "///")
- );
- }
-
- #[test]
- fn test_decompose_relative_part_only_slashes() {
- assert_complete!(
- decompose_relative_part::<Error<'_>, IriSpec>,
- "",
- (None, "")
- );
- assert_complete!(
- decompose_relative_part::<Error<'_>, IriSpec>,
- "/",
- (None, "/")
- );
- assert_complete!(
- decompose_relative_part::<Error<'_>, IriSpec>,
- "//",
- (Some(""), "")
- );
- assert_complete!(
- decompose_relative_part::<Error<'_>, IriSpec>,
- "///",
- (Some(""), "/")
- );
- assert_complete!(
- decompose_relative_part::<Error<'_>, IriSpec>,
- "////",
- (Some(""), "//")
- );
- assert_complete!(
- decompose_relative_part::<Error<'_>, IriSpec>,
- "/////",
- (Some(""), "///")
- );
- }
}
diff --git a/src/parser/validated.rs b/src/parser/validated.rs
new file mode 100644
index 000000000000..cdbbc0fcf738
--- /dev/null
+++ b/src/parser/validated.rs
@@ -0,0 +1,112 @@
+//! Parsers for already validated string.
+//!
+//! Parsers in this module make assumptions below:
+//!
+//! * The given IRI strings are valid.
+//! * The parser is parsing the "correct position" in the IRI.
+
+use core::marker::PhantomData;
+
+use crate::parser::RiReferenceComponents;
+use crate::spec::Spec;
+use crate::types::RiReferenceStr;
+
+/// Eats a scheme and a following colon if available, and returns the scheme.
+///
+/// This should be called at the head of an `IRI-reference`.
+fn scheme_colon(i: &str) -> (&str, Option<&str>) {
+ // Get a string before the first colon.
+ let scheme = match i.find(&[':', '/']).map(|pos| (pos, i.as_bytes()[pos])) {
+ Some((_, b'/')) => {
+ // A slash appears before the first colon. This is a relative IRI reference.
+ // Example: `/foo/bar`, `//[2001:db8::1]/`, `//example.com:80/`.
+ return (i, None);
+ }
+ Some((colon_pos, c)) => {
+ debug_assert_eq!(c, b':');
+ // Scheme.
+ &i[..colon_pos]
+ }
+ None => {
+ // No colon or no slash appears. This is a relative IRI reference.
+ return (i, None);
+ }
+ };
+ let rest = &i[(scheme.len() + 1)..];
+ debug_assert!(!scheme.is_empty());
+ debug_assert!(scheme.as_bytes()[0].is_ascii_alphabetic());
+ debug_assert!(scheme
+ .bytes()
+ .skip(1)
+ .all(|b| b.is_ascii_alphanumeric() || [b'+', b'-', b'.'].contains(&b)));
+ (rest, Some(scheme))
+}
+
+/// Eats double slash and the following authority if available, and returns the authority.
+///
+/// This should be called at the head of an `IRI-reference`, or at the result of `scheme_colon`.
+fn slash_slash_authority(i: &str) -> (&str, Option<&str>) {
+ let s = match i.strip_prefix("//") {
+ Some(rest) => rest,
+ None => return (i, None),
+ };
+ // `i` might match `path-abempty` (which can start with `//`), but it is not
+ // allowed as `relative-part`, so no need to care `path-abempty` rule here.
+ // A slash won't appear in `authority`.
+ let authority_end = s.find('/').unwrap_or(s.len());
+ let (authority, rest) = s.split_at(authority_end);
+
+ (rest, Some(authority))
+}
+
+/// Eats a string until the query, and returns that part (excluding `?` for the query).
+fn until_query(i: &str) -> (&str, &str) {
+ // `?` won't appear before the query part.
+ let before_query_pos = i.find(&['?', '#']).unwrap_or(i.len());
+ let (before_query, rest) = i.split_at(before_query_pos);
+ (rest, before_query)
+}
+
+/// Decomposes query and fragment, if available.
+///
+/// The string must starts with `?`, or `#`, or be empty.
+fn decompose_query_and_fragment(i: &str) -> (Option<&str>, Option<&str>) {
+ match i.as_bytes().get(0).copied() {
+ None => (None, None),
+ Some(b'?') => {
+ let rest = &i[1..];
+ match rest.find('#') {
+ Some(hash_pos) => (Some(&rest[..hash_pos]), Some(&rest[(hash_pos + 1)..])),
+ None => (Some(rest), None),
+ }
+ }
+ Some(c) => {
+ debug_assert_eq!(c, b'#');
+ (None, Some(&i[1..]))
+ }
+ }
+}
+
+/// Decomposes the given valid `IRI-reference`.
+pub(super) fn decompose_iri_reference<S: Spec>(
+ i: &RiReferenceStr<S>,
+) -> RiReferenceComponents<'_, S> {
+ /// Inner function to avoid unnecessary monomorphizations on `S`.
+ fn decompose(i: &str) -> (Option<&str>, Option<&str>, &str, Option<&str>, Option<&str>) {
+ let (i, scheme) = scheme_colon(i);
+ let (i, authority) = slash_slash_authority(i);
+ let (i, path) = until_query(i);
+ let (query, fragment) = decompose_query_and_fragment(i);
+ (scheme, authority, path, query, fragment)
+ }
+
+ let (scheme, authority, path, query, fragment) = decompose(i.as_str());
+ RiReferenceComponents {
+ scheme,
+ authority,
+ path,
+ query,
+ fragment,
+ _spec: PhantomData,
+ }
+}
Before:
test parser::tests::bench_parse_host ... bench: 2,367 ns/iter (+/- 46)
test parser::tests::bench_parse_normal ... bench: 1,919 ns/iter (+/- 37)
test resolve::tests::bench_resolve ... bench: 3,521 ns/iter (+/- 52)
After:
test parser::tests_bench::bench_parse_host ... bench: 2,344 ns/iter (+/- 48)
test parser::tests_bench::bench_parse_normal ... bench: 2,117 ns/iter (+/- 23)
test resolve::tests::bench_resolve ... bench: 1,362 ns/iter (+/- 22)
WOW.
resolve()
is now 2.5x faster for loooong IRIs.
Now I'm quite confident that I should rewrite parsers.
Oops, impl<'a, 'b, const N: usize> Pattern<'a> for &'b [char; N]
is introduced since Rust 1.58.0, which will be released at 2022-01-13.
I'll bump MSRV to 1.58, and delay the v0.5.0 release even if this refactoring is done earlier than Rust 1.58 release.
EDIT: I avoided to use string.find(&[a, b, c])
so this is not a problem now.
Current progress:
(Note that ordering of benches is changed)
test resolve::tests_bench::bench_resolve ... bench: 969 ns/iter (+/- 15)
test tests_bench::bench_parse_host ... bench: 548 ns/iter (+/- 30)
test tests_bench::bench_parse_normal ... bench: 454 ns/iter (+/- 6)
Merged to develop branch, but I'm still enhancing tests and benchmarks.
Done. Will be included in the next release.
Bonus: dependencies for nom
is dropped.
As I noticed at https://github.com/lo48576/iri-string/issues/6#issuecomment-1007519376, the current parsing is slow, and I'd be able to make it faster relatively easily.