diff options
author | Aaron O'Mullan <aaron.omullan@gmail.com> | 2021-08-18 23:21:33 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-18 23:21:33 +0200 |
commit | bf0bacbc0ec3bd7ef1c55ddc01ef44b2ce593d4a (patch) | |
tree | b0a39f028e22e3a7270bb711d146f87baeeece33 /ext/url/lib.rs | |
parent | e55454613cd5ad04c427a49c4287e60b1563c32e (diff) |
perf(ext/url): cleanup and optimize url parsing op args (#11763)
This splits the previous `op_url_parse` into:
- `op_url_parse`: parses a href with an optional base
- `op_url_reparse`: reparses a href with a modifier
This is a cleaner separation of concerns and it allows us to optimize & simplify args passed. Resulting in a 25% reduction in call overhead (~5000ns/call => ~3700ns/call in url_ops bench on my M1 Air)
Diffstat (limited to 'ext/url/lib.rs')
-rw-r--r-- | ext/url/lib.rs | 114 |
1 files changed, 64 insertions, 50 deletions
diff --git a/ext/url/lib.rs b/ext/url/lib.rs index 8ccc59eb8..25ecc1358 100644 --- a/ext/url/lib.rs +++ b/ext/url/lib.rs @@ -11,7 +11,6 @@ use deno_core::url::quirks; use deno_core::url::Url; use deno_core::Extension; use deno_core::ZeroCopyBuf; -use serde::Deserialize; use serde::Serialize; use std::panic::catch_unwind; use std::path::PathBuf; @@ -24,6 +23,7 @@ pub fn init() -> Extension { )) .ops(vec![ ("op_url_parse", op_sync(op_url_parse)), + ("op_url_reparse", op_sync(op_url_reparse)), ( "op_url_parse_search_params", op_sync(op_url_parse_search_params), @@ -36,24 +36,6 @@ pub fn init() -> Extension { .build() } -#[derive(Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct UrlParseArgs { - href: String, - base_href: Option<String>, - // If one of the following are present, this is a setter call. Apply the - // proper `Url::set_*()` method after (re)parsing `href`. - set_hash: Option<String>, - set_host: Option<String>, - set_hostname: Option<String>, - set_password: Option<String>, - set_pathname: Option<String>, - set_port: Option<String>, - set_protocol: Option<String>, - set_search: Option<String>, - set_username: Option<String>, -} - #[derive(Serialize)] pub struct UrlParts { href: String, @@ -73,56 +55,88 @@ pub struct UrlParts { /// optional part to "set" after parsing. Return `UrlParts`. pub fn op_url_parse( _state: &mut deno_core::OpState, - args: UrlParseArgs, - _: (), + href: String, + base_href: Option<String>, ) -> Result<UrlParts, AnyError> { - let base_url = args - .base_href + let base_url = base_href .as_ref() .map(|b| Url::parse(b).map_err(|_| type_error("Invalid base URL"))) .transpose()?; - let mut url = Url::options() + let url = Url::options() .base_url(base_url.as_ref()) - .parse(&args.href) + .parse(&href) .map_err(|_| type_error("Invalid URL"))?; - if let Some(hash) = args.set_hash.as_ref() { - quirks::set_hash(&mut url, hash); - } else if let Some(host) = args.set_host.as_ref() { - quirks::set_host(&mut url, host).map_err(|_| uri_error("Invalid host"))?; - } else if let Some(hostname) = args.set_hostname.as_ref() { - quirks::set_hostname(&mut url, hostname) - .map_err(|_| uri_error("Invalid hostname"))?; - } else if let Some(password) = args.set_password.as_ref() { - quirks::set_password(&mut url, password) - .map_err(|_| uri_error("Invalid password"))?; - } else if let Some(pathname) = args.set_pathname.as_ref() { - quirks::set_pathname(&mut url, pathname); - } else if let Some(port) = args.set_port.as_ref() { - quirks::set_port(&mut url, port).map_err(|_| uri_error("Invalid port"))?; - } else if let Some(protocol) = args.set_protocol.as_ref() { - quirks::set_protocol(&mut url, protocol) - .map_err(|_| uri_error("Invalid protocol"))?; - } else if let Some(search) = args.set_search.as_ref() { - quirks::set_search(&mut url, search); - } else if let Some(username) = args.set_username.as_ref() { - quirks::set_username(&mut url, username) - .map_err(|_| uri_error("Invalid username"))?; + url_result(url, href, base_href) +} + +#[derive( + serde_repr::Serialize_repr, serde_repr::Deserialize_repr, PartialEq, Debug, +)] +#[repr(u8)] +pub enum UrlSetter { + Hash = 1, + Host = 2, + Hostname = 3, + Password = 4, + Pathname = 5, + Port = 6, + Protocol = 7, + Search = 8, + Username = 9, +} + +pub fn op_url_reparse( + _state: &mut deno_core::OpState, + href: String, + setter_opts: (UrlSetter, String), +) -> Result<UrlParts, AnyError> { + let mut url = Url::options() + .parse(&href) + .map_err(|_| type_error("Invalid URL"))?; + + let (setter, setter_value) = setter_opts; + let value = setter_value.as_ref(); + + match setter { + UrlSetter::Hash => quirks::set_hash(&mut url, value), + UrlSetter::Host => quirks::set_host(&mut url, value) + .map_err(|_| uri_error("Invalid host"))?, + UrlSetter::Hostname => quirks::set_hostname(&mut url, value) + .map_err(|_| uri_error("Invalid hostname"))?, + UrlSetter::Password => quirks::set_password(&mut url, value) + .map_err(|_| uri_error("Invalid password"))?, + UrlSetter::Pathname => quirks::set_pathname(&mut url, value), + UrlSetter::Port => quirks::set_port(&mut url, value) + .map_err(|_| uri_error("Invalid port"))?, + UrlSetter::Protocol => quirks::set_protocol(&mut url, value) + .map_err(|_| uri_error("Invalid protocol"))?, + UrlSetter::Search => quirks::set_search(&mut url, value), + UrlSetter::Username => quirks::set_username(&mut url, value) + .map_err(|_| uri_error("Invalid username"))?, } + url_result(url, href, None) +} + +fn url_result( + url: Url, + href: String, + base_href: Option<String>, +) -> Result<UrlParts, AnyError> { // TODO(nayeemrmn): Panic that occurs in rust-url for the `non-spec:` // url-constructor wpt tests: https://github.com/servo/rust-url/issues/670. let username = catch_unwind(|| quirks::username(&url)).map_err(|_| { generic_error(format!( "Internal error while parsing \"{}\"{}, \ see https://github.com/servo/rust-url/issues/670", - args.href, - args - .base_href + href, + base_href .map(|b| format!(" against \"{}\"", b)) .unwrap_or_default() )) })?; + Ok(UrlParts { href: quirks::href(&url).to_string(), hash: quirks::hash(&url).to_string(), |