Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add faster serde serialization, sacrificing invariant-safety for speed in release mode; test that it roudtrips #252

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -3,6 +3,8 @@ rust:
- nightly
- beta
- stable
script: make test
script:
- make test
- cd serde-code-generator && cargo run && diff="$(git diff)" && echo "${diff}" && [[ -z "${diff}" ]]
notifications:
webhooks: http://build.servo.org:54856/travis
@@ -24,6 +24,8 @@ test = false
[dev-dependencies]
rustc-test = "0.1"
rustc-serialize = "0.3"
serde = "0.8.20"
bincode = {version = "0.6.0", features = ["serde"] }

[features]
query_encoding = ["encoding"]
@@ -35,4 +37,4 @@ heapsize = {version = ">=0.1.1, <0.4", optional = true}
idna = { version = "0.1.0", path = "./idna" }
matches = "0.1"
rustc-serialize = {version = "0.3", optional = true}
serde = {version = ">=0.6.1, <0.9", optional = true}
serde = {version = "0.8.20", optional = true}
@@ -0,0 +1,7 @@
[package]
name = "serde-code-generator"
version = "0.1.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]

[dependencies]
serde_codegen = { version = "^0.8" }
@@ -0,0 +1,11 @@
# Serde Code Generator

It's complicated to write full featured serde implementations, but we don't
want to force our dependenices to depend on
[serde_codegen](https://serde.rs/codegen-stable.html), which takes a while to
compile. This executable avoids that overhead by externally regenerating these
implementations, so only the rust-url developers need to pay the cost of
compiling `serde_codegen`. It does come at the price of having to remember to
re-run the code generator if the types ever change.

To run, just execute `cargo run` and it will update the files in `src/codegen`.
@@ -0,0 +1,15 @@
extern crate serde_codegen;

use std::path::Path;

pub fn main() {
// let src = Path::new("../src/codegen/url.rs.in");
// let dst = Path::new("../src/codegen/url.rs");
//
// serde_codegen::expand(&src, &dst).unwrap();

let src = Path::new("../src/codegen/host.rs.in");
let dst = Path::new("../src/codegen/host.rs");

serde_codegen::expand(&src, &dst).unwrap();
}
@@ -0,0 +1,154 @@
// WARNING: Do not modify this file if it is `host.rs`.
//
// Instead, modify `host.rs.in` and rerun the `serde-code-generator`.

#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
#[cfg(feature = "serde")]
const _IMPL_DESERIALIZE_FOR_HostInternal: () =
{
extern crate serde as _serde;
#[automatically_derived]
impl _serde::de::Deserialize for HostInternal {
fn deserialize<__D>(deserializer: &mut __D)
-> ::std::result::Result<HostInternal, __D::Error> where
__D: _serde::de::Deserializer {
#[allow(non_camel_case_types)]
enum __Field {
__field0,
__field1,
__field2,
__field3,
__ignore,
}
impl _serde::de::Deserialize for __Field {
#[inline]
fn deserialize<__D>(deserializer: &mut __D)
-> ::std::result::Result<__Field, __D::Error> where
__D: _serde::de::Deserializer {
struct __FieldVisitor;
impl _serde::de::Visitor for __FieldVisitor {
type
Value
=
__Field;
fn visit_usize<__E>(&mut self, value: usize)
-> ::std::result::Result<__Field, __E> where
__E: _serde::de::Error {
match value {
0usize => { Ok(__Field::__field0) }
1usize => { Ok(__Field::__field1) }
2usize => { Ok(__Field::__field2) }
3usize => { Ok(__Field::__field3) }
_ =>
Err(_serde::de::Error::invalid_value("expected a variant")),
}
}
fn visit_str<__E>(&mut self, value: &str)
-> ::std::result::Result<__Field, __E> where
__E: _serde::de::Error {
match value {
"None" => { Ok(__Field::__field0) }
"Domain" => { Ok(__Field::__field1) }
"Ipv4" => { Ok(__Field::__field2) }
"Ipv6" => { Ok(__Field::__field3) }
_ =>
Err(_serde::de::Error::unknown_variant(value)),
}
}
fn visit_bytes<__E>(&mut self, value: &[u8])
-> ::std::result::Result<__Field, __E> where
__E: _serde::de::Error {
match value {
b"None" => { Ok(__Field::__field0) }
b"Domain" => { Ok(__Field::__field1) }
b"Ipv4" => { Ok(__Field::__field2) }
b"Ipv6" => { Ok(__Field::__field3) }
_ => {
let value =
::std::string::String::from_utf8_lossy(value);
Err(_serde::de::Error::unknown_variant(&value))
}
}
}
}
deserializer.deserialize_struct_field(__FieldVisitor)
}
}
struct __Visitor;
impl _serde::de::EnumVisitor for __Visitor {
type
Value
=
HostInternal;
fn visit<__V>(&mut self, mut visitor: __V)
-> ::std::result::Result<HostInternal, __V::Error> where
__V: _serde::de::VariantVisitor {
match try!(visitor . visit_variant ( )) {
__Field::__field0 => {
try!(visitor . visit_unit ( ));
Ok(HostInternal::None)
}
__Field::__field1 => {
try!(visitor . visit_unit ( ));
Ok(HostInternal::Domain)
}
__Field::__field2 =>
Ok(HostInternal::Ipv4(try!(visitor . visit_newtype
:: < Ipv4Addr > (
)))),
__Field::__field3 =>
Ok(HostInternal::Ipv6(try!(visitor . visit_newtype
:: < Ipv6Addr > (
)))),
__Field::__ignore => {
Err(_serde::de::Error::end_of_stream())
}
}
}
}
const VARIANTS: &'static [&'static str] =
&["None", "Domain", "Ipv4", "Ipv6"];
deserializer.deserialize_enum("HostInternal", VARIANTS,
__Visitor)
}
}
};
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
#[cfg(feature = "serde")]
const _IMPL_SERIALIZE_FOR_HostInternal: () =
{
extern crate serde as _serde;
#[automatically_derived]
impl _serde::ser::Serialize for HostInternal {
fn serialize<__S>(&self, _serializer: &mut __S)
-> ::std::result::Result<(), __S::Error> where
__S: _serde::ser::Serializer {
match *self {
HostInternal::None =>
_serde::ser::Serializer::serialize_unit_variant(_serializer,
"HostInternal",
0usize,
"None"),
HostInternal::Domain =>
_serde::ser::Serializer::serialize_unit_variant(_serializer,
"HostInternal",
1usize,
"Domain"),
HostInternal::Ipv4(ref __simple_value) =>
_serde::ser::Serializer::serialize_newtype_variant(_serializer,
"HostInternal",
2usize,
"Ipv4",
__simple_value),
HostInternal::Ipv6(ref __simple_value) =>
_serde::ser::Serializer::serialize_newtype_variant(_serializer,
"HostInternal",
3usize,
"Ipv6",
__simple_value),
}
}
}
};
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum HostInternal { None, Domain, Ipv4(Ipv4Addr), Ipv6(Ipv6Addr), }
@@ -0,0 +1,12 @@
// WARNING: Do not modify this file if it is `host.rs`.
//
// Instead, modify `host.rs.in` and rerun the `serde-code-generator`.

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature="serde", derive(Serialize, Deserialize))]
pub enum HostInternal {
None,
Domain,
Ipv4(Ipv4Addr),
Ipv6(Ipv6Addr),
}
@@ -16,13 +16,7 @@ use parser::{ParseResult, ParseError};
use percent_encoding::percent_decode;
use idna;

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum HostInternal {
None,
Domain,
Ipv4(Ipv4Addr),
Ipv6(Ipv6Addr),
}
include!("codegen/host.rs");

#[cfg(feature = "heapsize")]
known_heap_size!(0, HostInternal);
@@ -312,17 +312,23 @@ impl Url {
self.serialization
}

#[doc(hidden)]
pub fn assert_invariants(&self) {
self.assert_invariants_result().unwrap()
}

/// For internal testing, not part of the public API.
///
/// Methods of the `Url` struct assume a number of invariants.
/// This checks each of these invariants and panic if one is not met.
/// This is for testing rust-url itself.
#[doc(hidden)]
pub fn assert_invariants(&self) {
pub fn assert_invariants_result(&self) -> Result<(), String> {
macro_rules! assert {
($x: expr) => {
if !$x {
panic!("!( {} ) for URL {:?}", stringify!($x), self.serialization)
return Err(format!("!( {} ) for URL {:?}",
stringify!($x), self.serialization))
}
}
}
@@ -333,8 +339,9 @@ impl Url {
let a = $a;
let b = $b;
if a != b {
panic!("{:?} != {:?} ({} != {}) for URL {:?}",
a, b, stringify!($a), stringify!($b), self.serialization)
return Err(format!("{:?} != {:?} ({} != {}) for URL {:?}",
a, b, stringify!($a), stringify!($b),
self.serialization))
}
}
}
@@ -415,6 +422,7 @@ impl Url {
assert_eq!(self.path_start, other.path_start);
assert_eq!(self.query_start, other.query_start);
assert_eq!(self.fragment_start, other.fragment_start);
Ok(())
}

/// Return the origin of this URL (https://url.spec.whatwg.org/#origin)
@@ -1502,10 +1510,61 @@ impl rustc_serialize::Decodable for Url {
}
}

#[cfg(feature="serde")]
impl Url {
/// Serialize the URL efficiently, for use with IPC
pub fn serialize_unsafe<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
use serde::Serialize;
// Destructuring first lets us ensure that adding or removing fields forces this method
// to be updated
let Url { ref serialization, ref scheme_end,
ref username_end, ref host_start,
ref host_end, ref host, ref port,
ref path_start, ref query_start,
ref fragment_start} = *self;
(serialization, scheme_end, username_end,
host_start, host_end, host, port, path_start,
query_start, fragment_start).serialize(serializer)
}

/// Deserialize the URL efficiently, without re-parsing
///
/// Deserializer must be known to contain the output of serialize_unsafe
///
/// Cannot cause memory unsafety if used incorrectly, but may cause security issues.
/// Only use with trusted input.
pub fn deserialize_unsafe<D>(deserializer: &mut D)
-> Result<Url, D::Error>
where D: serde::Deserializer {
use serde::{Deserialize, Error};
let (serialization, scheme_end, username_end,
host_start, host_end, host, port, path_start,
query_start, fragment_start) = Deserialize::deserialize(deserializer)?;
let url = Url {
serialization: serialization,
scheme_end: scheme_end,
username_end: username_end,
host_start: host_start,
host_end: host_end,
host: host,
port: port,
path_start: path_start,
query_start: query_start,
fragment_start: fragment_start
};
if cfg!(debug_assertions) {
if let Err(s) = url.assert_invariants_result() {
return Err(Error::invalid_value(&s))
}
}
Ok(url)
}
}
/// Serializes this URL into a `serde` stream.
///
/// This implementation is only available if the `serde` Cargo feature is enabled.
#[cfg(feature="serde")]
#[deny(unused)]
impl serde::Serialize for Url {
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: serde::Serializer {
format!("{}", self).serialize(serializer)
@@ -1516,6 +1575,7 @@ impl serde::Serialize for Url {
///
/// This implementation is only available if the `serde` Cargo feature is enabled.
#[cfg(feature="serde")]
#[deny(unused)]
impl serde::Deserialize for Url {
fn deserialize<D>(deserializer: &mut D) -> Result<Url, D::Error> where D: serde::Deserializer {
let string_representation: String = try!(serde::Deserialize::deserialize(deserializer));
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.