зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 6 changesets (bug 1396821) for linux tier2 build bustages on a CLOSED TREE
Backed out changeset 2fa2975f97e3 (bug 1396821) Backed out changeset c5895db52483 (bug 1396821) Backed out changeset 5c0ddd45f926 (bug 1396821) Backed out changeset 7c97853a85b9 (bug 1396821) Backed out changeset b61ce753f01e (bug 1396821) Backed out changeset 7ef3912feb2c (bug 1396821) --HG-- rename : third_party/rust/serde_json/LICENSE-APACHE => third_party/rust/rustc-serialize/LICENSE-APACHE rename : third_party/rust/serde_json/LICENSE-MIT => third_party/rust/rustc-serialize/LICENSE-MIT
This commit is contained in:
Родитель
bf12f26ad9
Коммит
0bcfe81a06
|
@ -806,7 +806,6 @@ dependencies = [
|
|||
name = "geckodriver"
|
||||
version = "0.21.0"
|
||||
dependencies = [
|
||||
"base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -816,9 +815,7 @@ dependencies = [
|
|||
"mozrunner 0.7.0",
|
||||
"mozversion 0.1.3",
|
||||
"regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
|
||||
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uuid 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webdriver 0.36.0",
|
||||
"zip 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1839,6 +1836,11 @@ name = "rustc-demangle"
|
|||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.1"
|
||||
|
@ -1847,11 +1849,6 @@ dependencies = [
|
|||
"semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.2.0"
|
||||
|
@ -1931,16 +1928,6 @@ dependencies = [
|
|||
"syn 0.14.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"itoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ryu 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "servo_arc"
|
||||
version = "0.1.1"
|
||||
|
@ -2425,15 +2412,11 @@ dependencies = [
|
|||
name = "webdriver"
|
||||
version = "0.36.0"
|
||||
dependencies = [
|
||||
"base64 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cookie 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hyper 0.10.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)",
|
||||
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2812,8 +2795,8 @@ dependencies = [
|
|||
"checksum runloop 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d79b4b604167921892e84afbbaad9d5ad74e091bf6c511d9dbfb0593f09fabd"
|
||||
"checksum rust-ini 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8a654c5bda722c699be6b0fe4c0d90de218928da5b724c3e467fc48865c37263"
|
||||
"checksum rustc-demangle 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "76d7ba1feafada44f2d38eed812bd2489a03c0f5abb975799251518b68848649"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum rustc_version 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9743a7670d88d5d52950408ecdb7c71d8986251ab604d4689dd2ca25c9bca69"
|
||||
"checksum ryu 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "fd0568787116e13c652377b6846f5931454a363a8fdf8ae50463ee40935b278b"
|
||||
"checksum safemem 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e27a8b19b835f7aea908818e871f5cc3a5a186550c30773be987e155e8163d8f"
|
||||
"checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637"
|
||||
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
|
||||
|
@ -2823,7 +2806,6 @@ dependencies = [
|
|||
"checksum serde 1.0.66 (registry+https://github.com/rust-lang/crates.io-index)" = "e9a2d9a9ac5120e0f768801ca2b58ad6eec929dc9d1d616c162f208869c2ce95"
|
||||
"checksum serde_bytes 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)" = "adb6e51a6b3696b301bc221d785f898b4457c619b51d7ce195a6d20baecb37b3"
|
||||
"checksum serde_derive 1.0.66 (git+https://github.com/servo/serde?branch=deserialize_from_enums8)" = "<none>"
|
||||
"checksum serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)" = "44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae"
|
||||
"checksum simd 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ed3686dd9418ebcc3a26a0c0ae56deab0681e53fe899af91f5bbcee667ebffb1"
|
||||
"checksum siphasher 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2ffc669b726f2bc9a3bcff66e5e23b56ba6bf70e22a34c3d7b6d0b3450b65b84"
|
||||
"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23"
|
||||
|
|
|
@ -9,7 +9,6 @@ license = "MPL-2.0"
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.6"
|
||||
chrono = "^0.2"
|
||||
clap = { version = "^2.19", default-features = false, features = ["suggestions", "wrap_help"] }
|
||||
hyper = "0.10"
|
||||
|
@ -19,9 +18,7 @@ mozprofile = { path = "../mozbase/rust/mozprofile" }
|
|||
mozrunner = { path = "../mozbase/rust/mozrunner" }
|
||||
mozversion = { path = "../mozbase/rust/mozversion" }
|
||||
regex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
rustc-serialize = "0.3"
|
||||
uuid = { version = "0.5", features = ["v4"] }
|
||||
webdriver = { path = "../webdriver" }
|
||||
zip = "0.3"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use serde_json::Value;
|
||||
use std::fmt;
|
||||
|
||||
use rustc_serialize::json::{Json};
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/build-info.rs"));
|
||||
|
||||
pub struct BuildInfo;
|
||||
|
@ -31,10 +32,10 @@ impl fmt::Display for BuildInfo {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO(Henrik): Change into From
|
||||
//std::convert::From<&str>` is not implemented for `rustc_serialize::json::Json
|
||||
impl Into<Value> for BuildInfo {
|
||||
fn into(self) -> Value {
|
||||
Value::String(BuildInfo::version().to_string())
|
||||
|
||||
impl Into<Json> for BuildInfo {
|
||||
fn into(self) -> Json {
|
||||
Json::String(BuildInfo::version().to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use base64;
|
||||
use logging::Level;
|
||||
use marionette::LogOptions;
|
||||
use mozprofile::preferences::Pref;
|
||||
|
@ -6,14 +5,15 @@ use mozprofile::profile::Profile;
|
|||
use mozrunner::runner::platform::firefox_default_path;
|
||||
use mozversion::{self, firefox_version, Version};
|
||||
use regex::bytes::Regex;
|
||||
use serde_json::{Map, Value};
|
||||
use rustc_serialize::base64::FromBase64;
|
||||
use rustc_serialize::json::Json;
|
||||
use std::collections::BTreeMap;
|
||||
use std::default::Default;
|
||||
use std::error::Error;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::io::BufWriter;
|
||||
use std::io::Cursor;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, Stdio};
|
||||
use std::str::{self, FromStr};
|
||||
|
@ -34,6 +34,7 @@ pub struct FirefoxCapabilities<'a> {
|
|||
version_cache: BTreeMap<PathBuf, String>,
|
||||
}
|
||||
|
||||
|
||||
impl<'a> FirefoxCapabilities<'a> {
|
||||
pub fn new(fallback_binary: Option<&'a PathBuf>) -> FirefoxCapabilities<'a> {
|
||||
FirefoxCapabilities {
|
||||
|
@ -43,11 +44,11 @@ impl<'a> FirefoxCapabilities<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn set_binary(&mut self, capabilities: &Map<String, Value>) {
|
||||
fn set_binary(&mut self, capabilities: &BTreeMap<String, Json>) {
|
||||
self.chosen_binary = capabilities
|
||||
.get("moz:firefoxOptions")
|
||||
.and_then(|x| x.get("binary"))
|
||||
.and_then(|x| x.as_str())
|
||||
.and_then(|x| x.find("binary"))
|
||||
.and_then(|x| x.as_string())
|
||||
.map(|x| PathBuf::from(x))
|
||||
.or_else(|| self.fallback_binary.map(|x| x.clone()))
|
||||
.or_else(|| firefox_default_path())
|
||||
|
@ -68,7 +69,8 @@ impl<'a> FirefoxCapabilities<'a> {
|
|||
});
|
||||
if let Some(ref version) = rv {
|
||||
debug!("Found version {}", version);
|
||||
self.version_cache.insert(binary.clone(), version.clone());
|
||||
self.version_cache
|
||||
.insert(binary.clone(), version.clone());
|
||||
} else {
|
||||
debug!("Failed to get binary version");
|
||||
}
|
||||
|
@ -79,8 +81,7 @@ impl<'a> FirefoxCapabilities<'a> {
|
|||
}
|
||||
|
||||
fn version_from_binary(&self, binary: &PathBuf) -> Option<String> {
|
||||
let version_regexp =
|
||||
Regex::new(r#"\d+\.\d+(?:[a-z]\d+)?"#).expect("Error parsing version regexp");
|
||||
let version_regexp = Regex::new(r#"\d+\.\d+(?:[a-z]\d+)?"#).expect("Error parsing version regexp");
|
||||
let output = Command::new(binary)
|
||||
.args(&["-version"])
|
||||
.stdout(Stdio::piped())
|
||||
|
@ -89,8 +90,7 @@ impl<'a> FirefoxCapabilities<'a> {
|
|||
.ok();
|
||||
|
||||
if let Some(x) = output {
|
||||
version_regexp
|
||||
.captures(&*x.stdout)
|
||||
version_regexp.captures(&*x.stdout)
|
||||
.and_then(|captures| captures.get(0))
|
||||
.and_then(|m| str::from_utf8(m.as_bytes()).ok())
|
||||
.map(|x| x.into())
|
||||
|
@ -104,8 +104,7 @@ impl<'a> FirefoxCapabilities<'a> {
|
|||
fn convert_version_error(err: mozversion::Error) -> WebDriverError {
|
||||
WebDriverError::new(
|
||||
ErrorStatus::SessionNotCreated,
|
||||
err.description().to_string(),
|
||||
)
|
||||
err.description().to_string())
|
||||
}
|
||||
|
||||
impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
|
||||
|
@ -123,8 +122,8 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
|
|||
|
||||
fn platform_name(&mut self, _: &Capabilities) -> WebDriverResult<Option<String>> {
|
||||
Ok(if cfg!(target_os = "windows") {
|
||||
Some("windows".into())
|
||||
} else if cfg!(target_os = "macos") {
|
||||
Some("windows".into())
|
||||
} else if cfg!(target_os = "macos") {
|
||||
Some("mac".into())
|
||||
} else if cfg!(target_os = "linux") {
|
||||
Some("linux".into())
|
||||
|
@ -146,11 +145,10 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn compare_browser_version(
|
||||
&mut self,
|
||||
version: &str,
|
||||
comparison: &str,
|
||||
) -> WebDriverResult<bool> {
|
||||
fn compare_browser_version(&mut self,
|
||||
version: &str,
|
||||
comparison: &str)
|
||||
-> WebDriverResult<bool> {
|
||||
try!(Version::from_str(version).or_else(|x| Err(convert_version_error(x))))
|
||||
.matches(comparison)
|
||||
.or_else(|x| Err(convert_version_error(x)))
|
||||
|
@ -160,100 +158,78 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
|
|||
Ok(true)
|
||||
}
|
||||
|
||||
fn validate_custom(&self, name: &str, value: &Value) -> WebDriverResult<()> {
|
||||
fn validate_custom(&self, name: &str, value: &Json) -> WebDriverResult<()> {
|
||||
if !name.starts_with("moz:") {
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
match name {
|
||||
"moz:firefoxOptions" => {
|
||||
let data = try_opt!(
|
||||
value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"moz:firefoxOptions is not an object"
|
||||
);
|
||||
let data = try_opt!(value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"moz:firefoxOptions is not an object");
|
||||
for (key, value) in data.iter() {
|
||||
match &**key {
|
||||
"binary" => {
|
||||
if !value.is_string() {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"binary path is not a string",
|
||||
));
|
||||
"binary path is not a string"));
|
||||
}
|
||||
}
|
||||
},
|
||||
"args" => {
|
||||
if !try_opt!(
|
||||
value.as_array(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"args is not an array"
|
||||
).iter()
|
||||
.all(|value| value.is_string())
|
||||
{
|
||||
if !try_opt!(value.as_array(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"args is not an array")
|
||||
.iter()
|
||||
.all(|value| value.is_string()) {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"args entry is not a string",
|
||||
));
|
||||
}
|
||||
}
|
||||
"args entry is not a string"));
|
||||
}
|
||||
},
|
||||
"profile" => {
|
||||
if !value.is_string() {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"profile is not a string",
|
||||
));
|
||||
"profile is not a string"));
|
||||
}
|
||||
}
|
||||
},
|
||||
"log" => {
|
||||
let log_data = try_opt!(
|
||||
value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"log value is not an object"
|
||||
);
|
||||
let log_data = try_opt!(value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"log value is not an object");
|
||||
for (log_key, log_value) in log_data.iter() {
|
||||
match &**log_key {
|
||||
"level" => {
|
||||
let level = try_opt!(
|
||||
log_value.as_str(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"log level is not a string"
|
||||
);
|
||||
let level = try_opt!(log_value.as_string(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"log level is not a string");
|
||||
if Level::from_str(level).is_err() {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Not a valid log level: {}", level),
|
||||
));
|
||||
format!("Not a valid log level: {}", level)))
|
||||
}
|
||||
}
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid log field {}", x),
|
||||
))
|
||||
}
|
||||
x => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid log field {}", x)))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"prefs" => {
|
||||
let prefs_data = try_opt!(
|
||||
value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"prefs value is not an object"
|
||||
);
|
||||
if !prefs_data.values().all(|x| {
|
||||
x.is_string() || x.is_i64() || x.is_u64() || x.is_boolean()
|
||||
}) {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Preference values not all string or integer or boolean",
|
||||
));
|
||||
}
|
||||
}
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid moz:firefoxOptions field {}", x),
|
||||
))
|
||||
let prefs_data = try_opt!(value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"prefs value is not an object");
|
||||
if !prefs_data.values()
|
||||
.all(|x| x.is_string() || x.is_i64() || x.is_u64() || x.is_boolean()) {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Preference values not all string or integer or boolean"));
|
||||
}
|
||||
}
|
||||
x => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid moz:firefoxOptions field {}", x)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -261,29 +237,23 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
|
|||
if !value.is_boolean() {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"moz:useNonSpecCompliantPointerOrigin is not a boolean",
|
||||
));
|
||||
"moz:useNonSpecCompliantPointerOrigin is not a boolean"));
|
||||
}
|
||||
}
|
||||
"moz:webdriverClick" => {
|
||||
if !value.is_boolean() {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"moz:webdriverClick is not a boolean",
|
||||
));
|
||||
"moz:webdriverClick is not a boolean"));
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Unrecognised option {}", name),
|
||||
))
|
||||
}
|
||||
_ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
||||
format!("Unrecognised option {}", name)))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn accept_custom(&mut self, _: &str, _: &Value, _: &Capabilities) -> WebDriverResult<bool> {
|
||||
fn accept_custom(&mut self, _: &str, _: &Json, _: &Capabilities) -> WebDriverResult<bool> {
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
@ -294,7 +264,7 @@ impl<'a> BrowserCapabilities for FirefoxCapabilities<'a> {
|
|||
/// the encoded profile, the binary arguments, log settings, and additional
|
||||
/// preferences to be checked and unmarshaled from the `moz:firefoxOptions`
|
||||
/// JSON Object into a Rust representation.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default)]
|
||||
pub struct FirefoxOptions {
|
||||
pub binary: Option<PathBuf>,
|
||||
pub profile: Option<Profile>,
|
||||
|
@ -308,19 +278,17 @@ impl FirefoxOptions {
|
|||
Default::default()
|
||||
}
|
||||
|
||||
pub fn from_capabilities(
|
||||
binary_path: Option<PathBuf>,
|
||||
matched: &mut Capabilities,
|
||||
) -> WebDriverResult<FirefoxOptions> {
|
||||
pub fn from_capabilities(binary_path: Option<PathBuf>,
|
||||
matched: &mut Capabilities)
|
||||
-> WebDriverResult<FirefoxOptions> {
|
||||
let mut rv = FirefoxOptions::new();
|
||||
rv.binary = binary_path;
|
||||
|
||||
if let Some(json) = matched.remove("moz:firefoxOptions") {
|
||||
let options = try!(json.as_object().ok_or(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"'moz:firefoxOptions' \
|
||||
capability is not an object"
|
||||
)));
|
||||
let options = try!(json.as_object()
|
||||
.ok_or(WebDriverError::new(ErrorStatus::InvalidArgument,
|
||||
"'moz:firefoxOptions' \
|
||||
capability is not an object")));
|
||||
|
||||
rv.profile = try!(FirefoxOptions::load_profile(&options));
|
||||
rv.args = try!(FirefoxOptions::load_args(&options));
|
||||
|
@ -333,22 +301,21 @@ impl FirefoxOptions {
|
|||
|
||||
fn load_profile(options: &Capabilities) -> WebDriverResult<Option<Profile>> {
|
||||
if let Some(profile_json) = options.get("profile") {
|
||||
let profile_base64 = try!(profile_json.as_str().ok_or(WebDriverError::new(
|
||||
ErrorStatus::UnknownError,
|
||||
"Profile is not a string"
|
||||
)));
|
||||
let profile_zip = &*try!(base64::decode(profile_base64));
|
||||
let profile_base64 =
|
||||
try!(profile_json
|
||||
.as_string()
|
||||
.ok_or(WebDriverError::new(ErrorStatus::UnknownError,
|
||||
"Profile is not a string")));
|
||||
let profile_zip = &*try!(profile_base64.from_base64());
|
||||
|
||||
// Create an emtpy profile directory
|
||||
let profile = try!(Profile::new(None));
|
||||
try!(unzip_buffer(
|
||||
profile_zip,
|
||||
profile
|
||||
.temp_dir
|
||||
.as_ref()
|
||||
.expect("Profile doesn't have a path")
|
||||
.path()
|
||||
));
|
||||
try!(unzip_buffer(profile_zip,
|
||||
profile
|
||||
.temp_dir
|
||||
.as_ref()
|
||||
.expect("Profile doesn't have a path")
|
||||
.path()));
|
||||
|
||||
Ok(Some(profile))
|
||||
} else {
|
||||
|
@ -358,22 +325,18 @@ impl FirefoxOptions {
|
|||
|
||||
fn load_args(options: &Capabilities) -> WebDriverResult<Option<Vec<String>>> {
|
||||
if let Some(args_json) = options.get("args") {
|
||||
let args_array = try!(args_json.as_array().ok_or(WebDriverError::new(
|
||||
ErrorStatus::UnknownError,
|
||||
"Arguments were not an \
|
||||
array"
|
||||
)));
|
||||
let args = try!(
|
||||
args_array
|
||||
.iter()
|
||||
.map(|x| x.as_str().map(|x| x.to_owned()))
|
||||
.collect::<Option<Vec<String>>>()
|
||||
.ok_or(WebDriverError::new(
|
||||
ErrorStatus::UnknownError,
|
||||
"Arguments entries were not all \
|
||||
strings"
|
||||
))
|
||||
);
|
||||
let args_array = try!(args_json
|
||||
.as_array()
|
||||
.ok_or(WebDriverError::new(ErrorStatus::UnknownError,
|
||||
"Arguments were not an \
|
||||
array")));
|
||||
let args = try!(args_array
|
||||
.iter()
|
||||
.map(|x| x.as_string().map(|x| x.to_owned()))
|
||||
.collect::<Option<Vec<String>>>()
|
||||
.ok_or(WebDriverError::new(ErrorStatus::UnknownError,
|
||||
"Arguments entries were not all \
|
||||
strings")));
|
||||
Ok(Some(args))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
@ -389,7 +352,7 @@ impl FirefoxOptions {
|
|||
|
||||
let level = match log.get("level") {
|
||||
Some(json) => {
|
||||
let s = json.as_str().ok_or(WebDriverError::new(
|
||||
let s = json.as_string().ok_or(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Log level is not a string",
|
||||
))?;
|
||||
|
@ -409,10 +372,10 @@ impl FirefoxOptions {
|
|||
|
||||
pub fn load_prefs(options: &Capabilities) -> WebDriverResult<Vec<(String, Pref)>> {
|
||||
if let Some(prefs_data) = options.get("prefs") {
|
||||
let prefs = try!(prefs_data.as_object().ok_or(WebDriverError::new(
|
||||
ErrorStatus::UnknownError,
|
||||
"Prefs were not an object"
|
||||
)));
|
||||
let prefs = try!(prefs_data
|
||||
.as_object()
|
||||
.ok_or(WebDriverError::new(ErrorStatus::UnknownError,
|
||||
"Prefs were not an object")));
|
||||
let mut rv = Vec::with_capacity(prefs.len());
|
||||
for (key, value) in prefs.iter() {
|
||||
rv.push((key.clone(), try!(pref_from_json(value))));
|
||||
|
@ -424,30 +387,27 @@ impl FirefoxOptions {
|
|||
}
|
||||
}
|
||||
|
||||
fn pref_from_json(value: &Value) -> WebDriverResult<Pref> {
|
||||
fn pref_from_json(value: &Json) -> WebDriverResult<Pref> {
|
||||
match value {
|
||||
&Value::String(ref x) => Ok(Pref::new(x.clone())),
|
||||
&Value::Number(ref x) => Ok(Pref::new(x.as_i64().unwrap())),
|
||||
&Value::Bool(x) => Ok(Pref::new(x)),
|
||||
_ => Err(WebDriverError::new(
|
||||
ErrorStatus::UnknownError,
|
||||
"Could not convert pref value to string, boolean, or integer",
|
||||
)),
|
||||
&Json::String(ref x) => Ok(Pref::new(x.clone())),
|
||||
&Json::I64(x) => Ok(Pref::new(x)),
|
||||
&Json::U64(x) => Ok(Pref::new(x as i64)),
|
||||
&Json::Boolean(x) => Ok(Pref::new(x)),
|
||||
_ => Err(WebDriverError::new(ErrorStatus::UnknownError,
|
||||
"Could not convert pref value to string, boolean, or integer"))
|
||||
}
|
||||
}
|
||||
|
||||
fn unzip_buffer(buf: &[u8], dest_dir: &Path) -> WebDriverResult<()> {
|
||||
let reader = Cursor::new(buf);
|
||||
let mut zip = try!(
|
||||
zip::ZipArchive::new(reader)
|
||||
.map_err(|_| WebDriverError::new(ErrorStatus::UnknownError, "Failed to unzip profile"))
|
||||
);
|
||||
let mut zip = try!(zip::ZipArchive::new(reader).map_err(|_| {
|
||||
WebDriverError::new(ErrorStatus::UnknownError, "Failed to unzip profile")
|
||||
}));
|
||||
|
||||
for i in 0..zip.len() {
|
||||
let mut file = try!(zip.by_index(i).map_err(|_| WebDriverError::new(
|
||||
ErrorStatus::UnknownError,
|
||||
"Processing profile zip file failed"
|
||||
)));
|
||||
let mut file = try!(zip.by_index(i).map_err(|_| {
|
||||
WebDriverError::new(ErrorStatus::UnknownError, "Processing profile zip file failed")
|
||||
}));
|
||||
let unzip_path = {
|
||||
let name = file.name();
|
||||
let is_dir = name.ends_with("/");
|
||||
|
@ -491,26 +451,36 @@ fn unzip_buffer(buf: &[u8], dest_dir: &Path) -> WebDriverResult<()> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate mozprofile;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use self::mozprofile::preferences::Pref;
|
||||
use super::*;
|
||||
use self::rustc_serialize::base64::{CharacterSet, Config, Newline, ToBase64};
|
||||
use self::rustc_serialize::json::Json;
|
||||
use super::FirefoxOptions;
|
||||
use marionette::MarionetteHandler;
|
||||
use std::collections::BTreeMap;
|
||||
use std::default::Default;
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use webdriver::capabilities::Capabilities;
|
||||
|
||||
fn example_profile() -> Value {
|
||||
fn example_profile() -> Json {
|
||||
let mut profile_data = Vec::with_capacity(1024);
|
||||
let mut profile = File::open("src/tests/profile.zip").unwrap();
|
||||
profile.read_to_end(&mut profile_data).unwrap();
|
||||
Value::String(base64::encode(&profile_data))
|
||||
let base64_config = Config {
|
||||
char_set: CharacterSet::Standard,
|
||||
newline: Newline::LF,
|
||||
pad: true,
|
||||
line_length: None,
|
||||
};
|
||||
Json::String(profile_data.to_base64(base64_config))
|
||||
}
|
||||
|
||||
fn make_options(firefox_opts: Capabilities) -> FirefoxOptions {
|
||||
let mut caps = Capabilities::new();
|
||||
caps.insert("moz:firefoxOptions".into(), Value::Object(firefox_opts));
|
||||
caps.insert("moz:firefoxOptions".into(), Json::Object(firefox_opts));
|
||||
let binary = None;
|
||||
FirefoxOptions::from_capabilities(binary, &mut caps).unwrap()
|
||||
}
|
||||
|
@ -527,24 +497,22 @@ mod tests {
|
|||
|
||||
println!("{:#?}", prefs.prefs);
|
||||
|
||||
assert_eq!(
|
||||
prefs.get("startup.homepage_welcome_url"),
|
||||
Some(&Pref::new("data:text/html,PASS"))
|
||||
);
|
||||
assert_eq!(prefs.get("startup.homepage_welcome_url"),
|
||||
Some(&Pref::new("data:text/html,PASS")));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prefs() {
|
||||
let encoded_profile = example_profile();
|
||||
let mut prefs: Map<String, Value> = Map::new();
|
||||
let mut prefs: BTreeMap<String, Json> = BTreeMap::new();
|
||||
prefs.insert(
|
||||
"browser.display.background_color".into(),
|
||||
Value::String("#00ff00".into()),
|
||||
Json::String("#00ff00".into()),
|
||||
);
|
||||
|
||||
let mut firefox_opts = Capabilities::new();
|
||||
firefox_opts.insert("profile".into(), encoded_profile);
|
||||
firefox_opts.insert("prefs".into(), Value::Object(prefs));
|
||||
firefox_opts.insert("prefs".into(), Json::Object(prefs));
|
||||
|
||||
let opts = make_options(firefox_opts);
|
||||
let mut profile = opts.profile.unwrap();
|
||||
|
|
|
@ -279,10 +279,7 @@ mod tests {
|
|||
];
|
||||
|
||||
for &(lvl, s) in tests.iter() {
|
||||
let expected = Pref {
|
||||
value: PrefValue::String(s.to_string()),
|
||||
sticky: false,
|
||||
};
|
||||
let expected = Pref { value: PrefValue::String(s.to_string()), sticky: false };
|
||||
assert_eq!(Into::<Pref>::into(lvl), expected);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
extern crate base64;
|
||||
extern crate chrono;
|
||||
#[macro_use]
|
||||
extern crate clap;
|
||||
|
@ -9,13 +8,10 @@ extern crate mozprofile;
|
|||
extern crate mozrunner;
|
||||
extern crate mozversion;
|
||||
extern crate regex;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate rustc_serialize;
|
||||
extern crate uuid;
|
||||
extern crate webdriver;
|
||||
extern crate zip;
|
||||
extern crate webdriver;
|
||||
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
@ -28,25 +24,22 @@ use std::str::FromStr;
|
|||
use clap::{App, Arg};
|
||||
|
||||
macro_rules! try_opt {
|
||||
($expr:expr, $err_type:expr, $err_msg:expr) => {{
|
||||
($expr:expr, $err_type:expr, $err_msg:expr) => ({
|
||||
match $expr {
|
||||
Some(x) => x,
|
||||
None => return Err(WebDriverError::new($err_type, $err_msg)),
|
||||
None => return Err(WebDriverError::new($err_type, $err_msg))
|
||||
}
|
||||
}};
|
||||
})
|
||||
}
|
||||
|
||||
mod build;
|
||||
mod capabilities;
|
||||
mod logging;
|
||||
mod marionette;
|
||||
mod prefs;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test;
|
||||
mod marionette;
|
||||
mod capabilities;
|
||||
|
||||
use build::BuildInfo;
|
||||
use marionette::{extension_routes, MarionetteHandler, MarionetteSettings};
|
||||
use marionette::{MarionetteHandler, MarionetteSettings, extension_routes};
|
||||
|
||||
type ProgramResult = std::result::Result<(), (ExitCode, String)>;
|
||||
|
||||
|
@ -69,70 +62,52 @@ fn print_version() {
|
|||
fn app<'a, 'b>() -> App<'a, 'b> {
|
||||
App::new(format!("geckodriver {}", crate_version!()))
|
||||
.about("WebDriver implementation for Firefox.")
|
||||
.arg(
|
||||
Arg::with_name("webdriver_host")
|
||||
.long("host")
|
||||
.value_name("HOST")
|
||||
.help("Host ip to use for WebDriver server (default: 127.0.0.1)")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("webdriver_port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
.value_name("PORT")
|
||||
.help("Port to use for WebDriver server (default: 4444)")
|
||||
.takes_value(true)
|
||||
.alias("webdriver-port"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("binary")
|
||||
.short("b")
|
||||
.long("binary")
|
||||
.value_name("BINARY")
|
||||
.help("Path to the Firefox binary")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("marionette_port")
|
||||
.long("marionette-port")
|
||||
.value_name("PORT")
|
||||
.help("Port to use to connect to Gecko (default: random free port)")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("connect_existing")
|
||||
.long("connect-existing")
|
||||
.requires("marionette_port")
|
||||
.help("Connect to an existing Firefox instance"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("jsdebugger")
|
||||
.long("jsdebugger")
|
||||
.takes_value(false)
|
||||
.help("Attach browser toolbox debugger for Firefox"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("verbosity")
|
||||
.short("v")
|
||||
.multiple(true)
|
||||
.conflicts_with("log_level")
|
||||
.help("Log level verbosity (-v for debug and -vv for trace level)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("log_level")
|
||||
.long("log")
|
||||
.takes_value(true)
|
||||
.value_name("LEVEL")
|
||||
.possible_values(&["fatal", "error", "warn", "info", "config", "debug", "trace"])
|
||||
.help("Set Gecko log level"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("version")
|
||||
.short("V")
|
||||
.long("version")
|
||||
.help("Prints version and copying information"),
|
||||
)
|
||||
.arg(Arg::with_name("webdriver_host")
|
||||
.long("host")
|
||||
.value_name("HOST")
|
||||
.help("Host ip to use for WebDriver server (default: 127.0.0.1)")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("webdriver_port")
|
||||
.short("p")
|
||||
.long("port")
|
||||
.value_name("PORT")
|
||||
.help("Port to use for WebDriver server (default: 4444)")
|
||||
.takes_value(true)
|
||||
.alias("webdriver-port"))
|
||||
.arg(Arg::with_name("binary")
|
||||
.short("b")
|
||||
.long("binary")
|
||||
.value_name("BINARY")
|
||||
.help("Path to the Firefox binary")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("marionette_port")
|
||||
.long("marionette-port")
|
||||
.value_name("PORT")
|
||||
.help("Port to use to connect to Gecko (default: random free port)")
|
||||
.takes_value(true))
|
||||
.arg(Arg::with_name("connect_existing")
|
||||
.long("connect-existing")
|
||||
.requires("marionette_port")
|
||||
.help("Connect to an existing Firefox instance"))
|
||||
.arg(Arg::with_name("jsdebugger")
|
||||
.long("jsdebugger")
|
||||
.takes_value(false)
|
||||
.help("Attach browser toolbox debugger for Firefox"))
|
||||
.arg(Arg::with_name("verbosity")
|
||||
.short("v")
|
||||
.multiple(true)
|
||||
.conflicts_with("log_level")
|
||||
.help("Log level verbosity (-v for debug and -vv for trace level)"))
|
||||
.arg(Arg::with_name("log_level")
|
||||
.long("log")
|
||||
.takes_value(true)
|
||||
.value_name("LEVEL")
|
||||
.possible_values(&["fatal", "error", "warn", "info", "config", "debug", "trace"])
|
||||
.help("Set Gecko log level"))
|
||||
.arg(Arg::with_name("version")
|
||||
.short("V")
|
||||
.long("version")
|
||||
.help("Prints version and copying information"))
|
||||
}
|
||||
|
||||
fn run() -> ProgramResult {
|
||||
|
@ -161,10 +136,12 @@ fn run() -> ProgramResult {
|
|||
let binary = matches.value_of("binary").map(|x| PathBuf::from(x));
|
||||
|
||||
let marionette_port = match matches.value_of("marionette_port") {
|
||||
Some(x) => match u16::from_str(x) {
|
||||
Ok(x) => Some(x),
|
||||
Err(_) => return Err((ExitCode::Usage, "invalid Marionette port".into())),
|
||||
},
|
||||
Some(x) => {
|
||||
match u16::from_str(x) {
|
||||
Ok(x) => Some(x),
|
||||
Err(_) => return Err((ExitCode::Usage, "invalid Marionette port".into())),
|
||||
}
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,19 +0,0 @@
|
|||
use regex::Regex;
|
||||
use serde;
|
||||
use serde_json;
|
||||
use std;
|
||||
|
||||
lazy_static! {
|
||||
static ref MIN_REGEX: Regex = Regex::new(r"[\n\t]|\s{4}").unwrap();
|
||||
}
|
||||
|
||||
pub fn check_deserialize<T>(json: &str, data: &T)
|
||||
where
|
||||
T: std::fmt::Debug,
|
||||
T: std::cmp::PartialEq,
|
||||
T: serde::de::DeserializeOwned,
|
||||
{
|
||||
let min_json = MIN_REGEX.replace_all(json, "");
|
||||
|
||||
assert_eq!(serde_json::from_str::<T>(&min_json).unwrap(), *data);
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
[merge.py]
|
||||
[test_merge_platformName]
|
||||
expected:
|
||||
if os != "linux": FAIL
|
||||
|
||||
[test_platform_name[body0\]]
|
||||
expected:
|
||||
if os != "linux": FAIL
|
||||
|
||||
[test_platform_name[body1\]]
|
||||
expected:
|
||||
if os != "linux": FAIL
|
||||
|
|
@ -11,14 +11,10 @@ license = "MPL-2.0"
|
|||
|
||||
[dependencies]
|
||||
cookie = { version = "0.10", default-features = false }
|
||||
base64 = "0.6"
|
||||
hyper = "0.10"
|
||||
lazy_static = "1.0"
|
||||
log = "0.4"
|
||||
regex = "1.0"
|
||||
serde = "1.0"
|
||||
serde_json = "1.0"
|
||||
serde_derive = "1.0"
|
||||
rustc-serialize = "0.3"
|
||||
time = "0.1"
|
||||
unicode-segmentation = "1.2"
|
||||
url = "1"
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,9 +1,10 @@
|
|||
use command::Parameters;
|
||||
use error::{ErrorStatus, WebDriverError, WebDriverResult};
|
||||
use serde_json::{Map, Value};
|
||||
use std::convert::From;
|
||||
use rustc_serialize::json::{Json, ToJson};
|
||||
use std::collections::BTreeMap;
|
||||
use url::Url;
|
||||
|
||||
pub type Capabilities = Map<String, Value>;
|
||||
pub type Capabilities = BTreeMap<String, Json>;
|
||||
|
||||
/// Trait for objects that can be used to inspect browser capabilities
|
||||
///
|
||||
|
@ -45,7 +46,7 @@ pub trait BrowserCapabilities {
|
|||
|
||||
fn accept_proxy(
|
||||
&mut self,
|
||||
proxy_settings: &Map<String, Value>,
|
||||
proxy_settings: &BTreeMap<String, Json>,
|
||||
&Capabilities,
|
||||
) -> WebDriverResult<bool>;
|
||||
|
||||
|
@ -54,7 +55,7 @@ pub trait BrowserCapabilities {
|
|||
/// Check that custom properties containing ":" have the correct data types.
|
||||
/// Properties that are unrecognised must be ignored i.e. return without
|
||||
/// error.
|
||||
fn validate_custom(&self, name: &str, value: &Value) -> WebDriverResult<()>;
|
||||
fn validate_custom(&self, name: &str, value: &Json) -> WebDriverResult<()>;
|
||||
|
||||
/// Check if custom properties are accepted capabilites
|
||||
///
|
||||
|
@ -63,7 +64,7 @@ pub trait BrowserCapabilities {
|
|||
fn accept_custom(
|
||||
&mut self,
|
||||
name: &str,
|
||||
value: &Value,
|
||||
value: &Json,
|
||||
merged: &Capabilities,
|
||||
) -> WebDriverResult<bool>;
|
||||
}
|
||||
|
@ -78,33 +79,16 @@ pub trait CapabilitiesMatching {
|
|||
/// Takes a BrowserCapabilites object and returns a set of capabilites that
|
||||
/// are valid for that browser, if any, or None if there are no matching
|
||||
/// capabilities.
|
||||
fn match_browser<T: BrowserCapabilities>(
|
||||
&self,
|
||||
browser_capabilities: &mut T,
|
||||
) -> WebDriverResult<Option<Capabilities>>;
|
||||
fn match_browser<T: BrowserCapabilities>(&self, browser_capabilities: &mut T)
|
||||
-> WebDriverResult<Option<Capabilities>>;
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct SpecNewSessionParameters {
|
||||
#[serde(default = "Capabilities::default")]
|
||||
pub alwaysMatch: Capabilities,
|
||||
#[serde(default = "firstMatch_default")]
|
||||
pub firstMatch: Vec<Capabilities>,
|
||||
}
|
||||
|
||||
impl Default for SpecNewSessionParameters {
|
||||
fn default() -> Self {
|
||||
SpecNewSessionParameters {
|
||||
alwaysMatch: Capabilities::new(),
|
||||
firstMatch: vec![Capabilities::new()],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn firstMatch_default() -> Vec<Capabilities> {
|
||||
vec![Capabilities::default()]
|
||||
}
|
||||
|
||||
impl SpecNewSessionParameters {
|
||||
fn validate<T: BrowserCapabilities>(
|
||||
&self,
|
||||
|
@ -114,7 +98,7 @@ impl SpecNewSessionParameters {
|
|||
// Filter out entries with the value `null`
|
||||
let null_entries = capabilities
|
||||
.iter()
|
||||
.filter(|&(_, ref value)| **value == Value::Null)
|
||||
.filter(|&(_, ref value)| **value == Json::Null)
|
||||
.map(|(k, _)| k.clone())
|
||||
.collect::<Vec<String>>();
|
||||
for key in null_entries {
|
||||
|
@ -161,66 +145,57 @@ impl SpecNewSessionParameters {
|
|||
Ok(capabilities)
|
||||
}
|
||||
|
||||
fn validate_page_load_strategy(value: &Value) -> WebDriverResult<()> {
|
||||
fn validate_page_load_strategy(value: &Json) -> WebDriverResult<()> {
|
||||
match value {
|
||||
&Value::String(ref x) => match &**x {
|
||||
"normal" | "eager" | "none" => {}
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid page load strategy: {}", x),
|
||||
))
|
||||
&Json::String(ref x) => {
|
||||
match &**x {
|
||||
"normal" |
|
||||
"eager" |
|
||||
"none" => {},
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid page load strategy: {}", x)))
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"pageLoadStrategy is not a string",
|
||||
))
|
||||
}
|
||||
_ => return Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
||||
"pageLoadStrategy is not a string"))
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_proxy(proxy_value: &Value) -> WebDriverResult<()> {
|
||||
let obj = try_opt!(
|
||||
proxy_value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"proxy is not an object"
|
||||
);
|
||||
fn validate_proxy(proxy_value: &Json) -> WebDriverResult<()> {
|
||||
let obj = try_opt!(proxy_value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"proxy is not an object");
|
||||
|
||||
for (key, value) in obj.iter() {
|
||||
match &**key {
|
||||
"proxyType" => match value.as_str() {
|
||||
Some("pac") | Some("direct") | Some("autodetect") | Some("system")
|
||||
| Some("manual") => {}
|
||||
Some(x) => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid proxyType value: {}", x),
|
||||
))
|
||||
}
|
||||
None => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("proxyType is not a string: {}", value),
|
||||
))
|
||||
}
|
||||
"proxyType" => match value.as_string() {
|
||||
Some("pac") |
|
||||
Some("direct") |
|
||||
Some("autodetect") |
|
||||
Some("system") |
|
||||
Some("manual") => {},
|
||||
Some(x) => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid proxyType value: {}", x))),
|
||||
None => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("proxyType is not a string: {}", value))),
|
||||
},
|
||||
|
||||
"proxyAutoconfigUrl" => match value.as_str() {
|
||||
"proxyAutoconfigUrl" => match value.as_string() {
|
||||
Some(x) => {
|
||||
Url::parse(x).or(Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("proxyAutoconfigUrl is not a valid URL: {}", x),
|
||||
)))?;
|
||||
}
|
||||
None => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"proxyAutoconfigUrl is not a string",
|
||||
))
|
||||
}
|
||||
format!("proxyAutoconfigUrl is not a valid URL: {}", x))))?;
|
||||
},
|
||||
None => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"proxyAutoconfigUrl is not a string"
|
||||
))
|
||||
},
|
||||
|
||||
"ftpProxy" => SpecNewSessionParameters::validate_host(value, "ftpProxy")?,
|
||||
|
@ -231,43 +206,36 @@ impl SpecNewSessionParameters {
|
|||
"socksVersion" => if !value.is_number() {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("socksVersion is not a number: {}", value),
|
||||
));
|
||||
format!("socksVersion is not a number: {}", value)
|
||||
))
|
||||
},
|
||||
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid proxy configuration entry: {}", x),
|
||||
))
|
||||
}
|
||||
x => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("Invalid proxy configuration entry: {}", x)))
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_no_proxy(value: &Value) -> WebDriverResult<()> {
|
||||
fn validate_no_proxy(value: &Json) -> WebDriverResult<()> {
|
||||
match value.as_array() {
|
||||
Some(hosts) => {
|
||||
for host in hosts {
|
||||
match host.as_str() {
|
||||
Some(_) => {}
|
||||
None => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("noProxy item is not a string: {}", host),
|
||||
))
|
||||
}
|
||||
match host.as_string() {
|
||||
Some(_) => {},
|
||||
None => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("noProxy item is not a string: {}", host)
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("noProxy is not an array: {}", value),
|
||||
))
|
||||
}
|
||||
},
|
||||
None => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("noProxy is not an array: {}", value)
|
||||
))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -275,45 +243,42 @@ impl SpecNewSessionParameters {
|
|||
|
||||
/// Validate whether a named capability is JSON value is a string containing a host
|
||||
/// and possible port
|
||||
fn validate_host(value: &Value, entry: &str) -> WebDriverResult<()> {
|
||||
match value.as_str() {
|
||||
fn validate_host(value: &Json, entry: &str) -> WebDriverResult<()> {
|
||||
match value.as_string() {
|
||||
Some(host) => {
|
||||
if host.contains("://") {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("{} must not contain a scheme: {}", entry, host),
|
||||
));
|
||||
format!("{} must not contain a scheme: {}", entry, host)));
|
||||
}
|
||||
|
||||
// Temporarily add a scheme so the host can be parsed as URL
|
||||
let s = String::from(format!("http://{}", host));
|
||||
let url = Url::parse(s.as_str()).or(Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("{} is not a valid URL: {}", entry, host),
|
||||
)))?;
|
||||
format!("{} is not a valid URL: {}", entry, host))))?;
|
||||
|
||||
if url.username() != "" || url.password() != None || url.path() != "/"
|
||||
|| url.query() != None || url.fragment() != None
|
||||
{
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("{} is not of the form host[:port]: {}", entry, host),
|
||||
));
|
||||
}
|
||||
}
|
||||
if url.username() != "" ||
|
||||
url.password() != None ||
|
||||
url.path() != "/" ||
|
||||
url.query() != None ||
|
||||
url.fragment() != None {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("{} is not of the form host[:port]: {}", entry, host)));
|
||||
}
|
||||
},
|
||||
|
||||
None => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("{} is not a string: {}", entry, value),
|
||||
))
|
||||
}
|
||||
None => return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("{} is not a string: {}", entry, value)
|
||||
))
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_timeouts(value: &Value) -> WebDriverResult<()> {
|
||||
fn validate_timeouts(value: &Json) -> WebDriverResult<()> {
|
||||
let obj = try_opt!(
|
||||
value.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
|
@ -348,15 +313,19 @@ impl SpecNewSessionParameters {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_unhandled_prompt_behaviour(value: &Value) -> WebDriverResult<()> {
|
||||
fn validate_unhandled_prompt_behaviour(value: &Json) -> WebDriverResult<()> {
|
||||
let behaviour = try_opt!(
|
||||
value.as_str(),
|
||||
value.as_string(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
format!("unhandledPromptBehavior is not a string: {}", value)
|
||||
);
|
||||
|
||||
match behaviour {
|
||||
"accept" | "accept and notify" | "dismiss" | "dismiss and notify" | "ignore" => {}
|
||||
"accept" |
|
||||
"accept and notify" |
|
||||
"dismiss" |
|
||||
"dismiss and notify" |
|
||||
"ignore" => {},
|
||||
x => {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
|
@ -369,12 +338,74 @@ impl SpecNewSessionParameters {
|
|||
}
|
||||
}
|
||||
|
||||
impl Parameters for SpecNewSessionParameters {
|
||||
fn from_json(body: &Json) -> WebDriverResult<SpecNewSessionParameters> {
|
||||
let data = try_opt!(
|
||||
body.as_object(),
|
||||
ErrorStatus::UnknownError,
|
||||
format!("Malformed capabilities, message body is not an object: {}", body)
|
||||
);
|
||||
|
||||
let capabilities = try_opt!(
|
||||
try_opt!(
|
||||
data.get("capabilities"),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Malformed capabilities, missing \"capabilities\" field"
|
||||
).as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Malformed capabilities, \"capabilities\" field is not an object}"
|
||||
);
|
||||
|
||||
let default_always_match = Json::Object(Capabilities::new());
|
||||
let always_match = try_opt!(
|
||||
capabilities
|
||||
.get("alwaysMatch")
|
||||
.unwrap_or(&default_always_match)
|
||||
.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Malformed capabilities, alwaysMatch field is not an object"
|
||||
);
|
||||
let default_first_matches = Json::Array(vec![]);
|
||||
let first_matches = try_opt!(
|
||||
capabilities
|
||||
.get("firstMatch")
|
||||
.unwrap_or(&default_first_matches)
|
||||
.as_array(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Malformed capabilities, firstMatch field is not an array"
|
||||
).iter()
|
||||
.map(|x| {
|
||||
x.as_object().map(|x| x.clone()).ok_or(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Malformed capabilities, firstMatch entry is not an object",
|
||||
))
|
||||
})
|
||||
.collect::<WebDriverResult<Vec<Capabilities>>>()?;
|
||||
|
||||
return Ok(SpecNewSessionParameters {
|
||||
alwaysMatch: always_match.clone(),
|
||||
firstMatch: first_matches,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for SpecNewSessionParameters {
|
||||
fn to_json(&self) -> Json {
|
||||
let mut body = BTreeMap::new();
|
||||
let mut capabilities = BTreeMap::new();
|
||||
capabilities.insert("alwaysMatch".into(), self.alwaysMatch.to_json());
|
||||
capabilities.insert("firstMatch".into(), self.firstMatch.to_json());
|
||||
body.insert("capabilities".into(), capabilities.to_json());
|
||||
Json::Object(body)
|
||||
}
|
||||
}
|
||||
|
||||
impl CapabilitiesMatching for SpecNewSessionParameters {
|
||||
fn match_browser<T: BrowserCapabilities>(
|
||||
&self,
|
||||
browser_capabilities: &mut T,
|
||||
) -> WebDriverResult<Option<Capabilities>> {
|
||||
let default = vec![Map::new()];
|
||||
let default = vec![BTreeMap::new()];
|
||||
let capabilities_list = if self.firstMatch.len() > 0 {
|
||||
&self.firstMatch
|
||||
} else {
|
||||
|
@ -384,22 +415,19 @@ impl CapabilitiesMatching for SpecNewSessionParameters {
|
|||
let merged_capabilities = capabilities_list
|
||||
.iter()
|
||||
.map(|first_match_entry| {
|
||||
if first_match_entry
|
||||
.keys()
|
||||
.any(|k| self.alwaysMatch.contains_key(k))
|
||||
{
|
||||
if first_match_entry.keys().any(|k| self.alwaysMatch.contains_key(k)) {
|
||||
return Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidArgument,
|
||||
"firstMatch key shadowed a value in alwaysMatch",
|
||||
));
|
||||
}
|
||||
let mut merged = self.alwaysMatch.clone();
|
||||
for (key, value) in first_match_entry.clone().into_iter() {
|
||||
merged.insert(key, value);
|
||||
}
|
||||
merged.append(&mut first_match_entry.clone());
|
||||
Ok(merged)
|
||||
})
|
||||
.map(|merged| merged.and_then(|x| self.validate(x, browser_capabilities)))
|
||||
.map(|merged| {
|
||||
merged.and_then(|x| self.validate(x, browser_capabilities))
|
||||
})
|
||||
.collect::<WebDriverResult<Vec<Capabilities>>>()?;
|
||||
|
||||
let selected = merged_capabilities
|
||||
|
@ -415,7 +443,7 @@ impl CapabilitiesMatching for SpecNewSessionParameters {
|
|||
.ok()
|
||||
.and_then(|x| x);
|
||||
|
||||
if value.as_str() != browserValue.as_ref().map(|x| &**x) {
|
||||
if value.as_string() != browserValue.as_ref().map(|x| &**x) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +453,7 @@ impl CapabilitiesMatching for SpecNewSessionParameters {
|
|||
.ok()
|
||||
.and_then(|x| x);
|
||||
// We already validated this was a string
|
||||
let version_cond = value.as_str().unwrap_or("");
|
||||
let version_cond = value.as_string().unwrap_or("");
|
||||
if let Some(version) = browserValue {
|
||||
if !browser_capabilities
|
||||
.compare_browser_version(&*version, version_cond)
|
||||
|
@ -442,13 +470,13 @@ impl CapabilitiesMatching for SpecNewSessionParameters {
|
|||
.platform_name(merged)
|
||||
.ok()
|
||||
.and_then(|x| x);
|
||||
if value.as_str() != browserValue.as_ref().map(|x| &**x) {
|
||||
if value.as_string() != browserValue.as_ref().map(|x| &**x) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
"acceptInsecureCerts" => {
|
||||
if value.as_bool().unwrap_or(false)
|
||||
&& !browser_capabilities
|
||||
if value.as_boolean().unwrap_or(false) &&
|
||||
!browser_capabilities
|
||||
.accept_insecure_certs(merged)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
|
@ -456,8 +484,8 @@ impl CapabilitiesMatching for SpecNewSessionParameters {
|
|||
}
|
||||
}
|
||||
"setWindowRect" => {
|
||||
if value.as_bool().unwrap_or(false)
|
||||
&& !browser_capabilities
|
||||
if value.as_boolean().unwrap_or(false) &&
|
||||
!browser_capabilities
|
||||
.set_window_rect(merged)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
|
@ -465,7 +493,7 @@ impl CapabilitiesMatching for SpecNewSessionParameters {
|
|||
}
|
||||
}
|
||||
"proxy" => {
|
||||
let default = Map::new();
|
||||
let default = BTreeMap::new();
|
||||
let proxy = value.as_object().unwrap_or(&default);
|
||||
if !browser_capabilities
|
||||
.accept_proxy(&proxy, merged)
|
||||
|
@ -497,11 +525,9 @@ impl CapabilitiesMatching for SpecNewSessionParameters {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct LegacyNewSessionParameters {
|
||||
#[serde(default = "Capabilities::default")]
|
||||
pub desired: Capabilities,
|
||||
#[serde(default = "Capabilities::default")]
|
||||
pub required: Capabilities,
|
||||
}
|
||||
|
||||
|
@ -513,7 +539,7 @@ impl CapabilitiesMatching for LegacyNewSessionParameters {
|
|||
// For now don't do anything much, just merge the
|
||||
// desired and required and return the merged list.
|
||||
|
||||
let mut capabilities: Capabilities = Map::new();
|
||||
let mut capabilities: Capabilities = BTreeMap::new();
|
||||
self.required.iter().chain(self.desired.iter()).fold(
|
||||
&mut capabilities,
|
||||
|caps, (key, value)| {
|
||||
|
@ -528,153 +554,57 @@ impl CapabilitiesMatching for LegacyNewSessionParameters {
|
|||
}
|
||||
}
|
||||
|
||||
impl Parameters for LegacyNewSessionParameters {
|
||||
fn from_json(body: &Json) -> WebDriverResult<LegacyNewSessionParameters> {
|
||||
let data = try_opt!(
|
||||
body.as_object(),
|
||||
ErrorStatus::UnknownError,
|
||||
format!("Malformed legacy capabilities, message body is not an object: {}", body)
|
||||
);
|
||||
|
||||
let desired = if let Some(capabilities) = data.get("desiredCapabilities") {
|
||||
try_opt!(
|
||||
capabilities.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Malformed legacy capabilities, desiredCapabilities field is not an object"
|
||||
).clone()
|
||||
} else {
|
||||
BTreeMap::new()
|
||||
};
|
||||
|
||||
let required = if let Some(capabilities) = data.get("requiredCapabilities") {
|
||||
try_opt!(
|
||||
capabilities.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Malformed legacy capabilities, requiredCapabilities field is not an object"
|
||||
).clone()
|
||||
} else {
|
||||
BTreeMap::new()
|
||||
};
|
||||
|
||||
Ok(LegacyNewSessionParameters { desired, required })
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for LegacyNewSessionParameters {
|
||||
fn to_json(&self) -> Json {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("desiredCapabilities".to_owned(), self.desired.to_json());
|
||||
data.insert("requiredCapabilities".to_owned(), self.required.to_json());
|
||||
Json::Object(data)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json::{self, Value};
|
||||
use test::check_deserialize;
|
||||
use rustc_serialize::json::Json;
|
||||
use super::{SpecNewSessionParameters, WebDriverResult};
|
||||
|
||||
fn validate_proxy(value: &str) -> WebDriverResult<()> {
|
||||
let data = serde_json::from_str::<Value>(value).unwrap();
|
||||
let data = Json::from_str(value).unwrap();
|
||||
SpecNewSessionParameters::validate_proxy(&data)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_new_session_parameters_alwaysMatch_only() {
|
||||
let json = r#"{
|
||||
"alwaysMatch":{}
|
||||
}"#;
|
||||
let data = SpecNewSessionParameters {
|
||||
alwaysMatch: Capabilities::new(),
|
||||
firstMatch: vec![Capabilities::new()],
|
||||
};
|
||||
|
||||
check_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_new_session_parameters_firstMatch_only() {
|
||||
let json = r#"{
|
||||
"firstMatch":[{}]
|
||||
}"#;
|
||||
let data = SpecNewSessionParameters {
|
||||
alwaysMatch: Capabilities::new(),
|
||||
firstMatch: vec![Capabilities::new()],
|
||||
};
|
||||
|
||||
check_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_new_session_parameters_alwaysMatch_null() {
|
||||
let json = r#"{
|
||||
"alwaysMatch":null,
|
||||
"firstMatch":[{}]
|
||||
}"#;
|
||||
|
||||
assert!(serde_json::from_str::<SpecNewSessionParameters>(&json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_new_session_parameters_firstMatch_null() {
|
||||
let json = r#"{
|
||||
"alwaysMatch":{},
|
||||
"firstMatch":null
|
||||
}"#;
|
||||
|
||||
assert!(serde_json::from_str::<SpecNewSessionParameters>(&json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_new_session_parameters_both_empty() {
|
||||
let json = r#"{
|
||||
"alwaysMatch":{},
|
||||
"firstMatch":[{}]
|
||||
}"#;
|
||||
let data = SpecNewSessionParameters {
|
||||
alwaysMatch: Capabilities::new(),
|
||||
firstMatch: vec![Capabilities::new()],
|
||||
};
|
||||
|
||||
check_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_new_session_parameters_both_with_capability() {
|
||||
let json = r#"{
|
||||
"alwaysMatch":{"foo":"bar"},
|
||||
"firstMatch":[{"foo2":"bar2"}]
|
||||
}"#;
|
||||
let mut data = SpecNewSessionParameters {
|
||||
alwaysMatch: Capabilities::new(),
|
||||
firstMatch: vec![Capabilities::new()],
|
||||
};
|
||||
data.alwaysMatch.insert("foo".into(), "bar".into());
|
||||
data.firstMatch[0].insert("foo2".into(), "bar2".into());
|
||||
|
||||
check_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_legacy_new_session_parameters_desired_only() {
|
||||
let json = r#"{"desired":{}}"#;
|
||||
let data = LegacyNewSessionParameters {
|
||||
desired: Capabilities::new(),
|
||||
required: Capabilities::new(),
|
||||
};
|
||||
|
||||
check_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_legacy_new_session_parameters_required_only() {
|
||||
let json = r#"{"required":{}}"#;
|
||||
let data = LegacyNewSessionParameters {
|
||||
desired: Capabilities::new(),
|
||||
required: Capabilities::new(),
|
||||
};
|
||||
|
||||
check_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_legacy_new_session_parameters_desired_null() {
|
||||
let json = r#"{"desired":null,"required":{}}"#;
|
||||
|
||||
assert!(serde_json::from_str::<LegacyNewSessionParameters>(&json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_legacy_new_session_parameters_required_null() {
|
||||
let json = r#"{"desired":{}, "required":null}"#;
|
||||
|
||||
assert!(serde_json::from_str::<LegacyNewSessionParameters>(&json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_legacy_new_session_parameters_both_empty() {
|
||||
let json = r#"{"desired":{},"required":{}}"#;
|
||||
let data = LegacyNewSessionParameters {
|
||||
desired: Capabilities::new(),
|
||||
required: Capabilities::new(),
|
||||
};
|
||||
|
||||
check_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_spec_legacy_new_session_parameters_both_with_capabilities() {
|
||||
let json = r#"{"desired":{"foo":"bar"},"required":{"foo2":"bar2"}}"#;
|
||||
let mut data = LegacyNewSessionParameters {
|
||||
desired: Capabilities::new(),
|
||||
required: Capabilities::new(),
|
||||
};
|
||||
data.desired.insert("foo".into(), "bar".into());
|
||||
data.required.insert("foo2".into(), "bar2".into());
|
||||
|
||||
check_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_validate_proxy() {
|
||||
// proxy hosts
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,169 +1,224 @@
|
|||
use serde::ser::{Serialize, Serializer};
|
||||
use rustc_serialize::{Encodable, Encoder};
|
||||
use rustc_serialize::json::{Json, ToJson};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use error::{WebDriverResult, WebDriverError, ErrorStatus};
|
||||
|
||||
pub static ELEMENT_KEY: &'static str = "element-6066-11e4-a52e-4f735466cecf";
|
||||
pub static FRAME_KEY: &'static str = "frame-075b-4da1-b6ba-e579c2d3230a";
|
||||
pub static WINDOW_KEY: &'static str = "window-fcc6-11e5-b4f8-330a88ab9d7f";
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct Cookie {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub path: Option<String>,
|
||||
pub domain: Option<String>,
|
||||
#[serde(default)]
|
||||
pub secure: bool,
|
||||
#[serde(default)]
|
||||
pub httpOnly: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub expiry: Option<Date>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Debug, PartialEq, RustcEncodable)]
|
||||
pub struct Date(pub u64);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum FrameId {
|
||||
Short(u16),
|
||||
#[serde(
|
||||
rename = "element-6066-11e4-a52e-4f735466cecf", serialize_with = "serialize_webelement_id"
|
||||
)]
|
||||
Element(WebElement),
|
||||
impl Date {
|
||||
pub fn new(timestamp: u64) -> Date {
|
||||
Date(timestamp)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(Henrik): Remove when ToMarionette trait has been fixed
|
||||
fn serialize_webelement_id<S>(element: &WebElement, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
element.id.serialize(serializer)
|
||||
impl ToJson for Date {
|
||||
fn to_json(&self) -> Json {
|
||||
let &Date(x) = self;
|
||||
x.to_json()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum LocatorStrategy {
|
||||
#[serde(rename = "css selector")]
|
||||
CSSSelector,
|
||||
#[serde(rename = "link text")]
|
||||
LinkText,
|
||||
#[serde(rename = "partial link text")]
|
||||
PartialLinkText,
|
||||
#[serde(rename = "tag name")]
|
||||
TagName,
|
||||
#[serde(rename = "xpath")]
|
||||
XPath,
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Nullable<T: ToJson> {
|
||||
Value(T),
|
||||
Null
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
impl<T: ToJson> Nullable<T> {
|
||||
pub fn is_null(&self) -> bool {
|
||||
match *self {
|
||||
Nullable::Value(_) => false,
|
||||
Nullable::Null => true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_value(&self) -> bool {
|
||||
match *self {
|
||||
Nullable::Value(_) => true,
|
||||
Nullable::Null => false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map<F, U: ToJson>(self, f: F) -> Nullable<U>
|
||||
where F: FnOnce(T) -> U {
|
||||
match self {
|
||||
Nullable::Value(val) => Nullable::Value(f(val)),
|
||||
Nullable::Null => Nullable::Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToJson> Nullable<T> {
|
||||
//This is not very pretty
|
||||
pub fn from_json<F: FnOnce(&Json) -> WebDriverResult<T>>(value: &Json, f: F) -> WebDriverResult<Nullable<T>> {
|
||||
if value.is_null() {
|
||||
Ok(Nullable::Null)
|
||||
} else {
|
||||
Ok(Nullable::Value(try!(f(value))))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToJson> ToJson for Nullable<T> {
|
||||
fn to_json(&self) -> Json {
|
||||
match *self {
|
||||
Nullable::Value(ref x) => x.to_json(),
|
||||
Nullable::Null => Json::Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToJson> Encodable for Nullable<T> {
|
||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
match *self {
|
||||
Nullable::Value(ref x) => x.to_json().encode(s),
|
||||
Nullable::Null => s.emit_option_none()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToJson> Into<Option<T>> for Nullable<T> {
|
||||
fn into(self) -> Option<T> {
|
||||
match self {
|
||||
Nullable::Value(val) => Some(val),
|
||||
Nullable::Null => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToJson> From<Option<T>> for Nullable<T> {
|
||||
fn from(option: Option<T>) -> Nullable<T> {
|
||||
match option {
|
||||
Some(val) => Nullable::Value(val),
|
||||
None => Nullable::Null,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct WebElement {
|
||||
#[serde(rename = "element-6066-11e4-a52e-4f735466cecf")]
|
||||
pub id: String,
|
||||
pub id: String
|
||||
}
|
||||
|
||||
impl WebElement {
|
||||
pub fn new(id: String) -> WebElement {
|
||||
WebElement { id: id }
|
||||
WebElement {
|
||||
id: id
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_json(data: &Json) -> WebDriverResult<WebElement> {
|
||||
let object = try_opt!(data.as_object(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Could not convert webelement to object");
|
||||
let id_value = try_opt!(object.get(ELEMENT_KEY),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Could not find webelement key");
|
||||
|
||||
let id = try_opt!(id_value.as_string(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Could not convert web element to string").to_string();
|
||||
|
||||
Ok(WebElement::new(id))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use serde_json;
|
||||
use test::check_serialize_deserialize;
|
||||
|
||||
#[test]
|
||||
fn test_json_date() {
|
||||
let json = r#"1234"#;
|
||||
let data = Date(1234);
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_date_invalid() {
|
||||
let json = r#""2018-01-01""#;
|
||||
assert!(serde_json::from_str::<Date>(&json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_frame_id_short() {
|
||||
let json = r#"1234"#;
|
||||
let data = FrameId::Short(1234);
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_frame_id_webelement() {
|
||||
let json = r#""elem""#;
|
||||
let data = FrameId::Element(WebElement::new("elem".into()));
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_frame_id_invalid() {
|
||||
let json = r#"true"#;
|
||||
assert!(serde_json::from_str::<FrameId>(&json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_locator_strategy_css_selector() {
|
||||
let json = r#""css selector""#;
|
||||
let data = LocatorStrategy::CSSSelector;
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_locator_strategy_link_text() {
|
||||
let json = r#""link text""#;
|
||||
let data = LocatorStrategy::LinkText;
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_locator_strategy_partial_link_text() {
|
||||
let json = r#""partial link text""#;
|
||||
let data = LocatorStrategy::PartialLinkText;
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_locator_strategy_tag_name() {
|
||||
let json = r#""tag name""#;
|
||||
let data = LocatorStrategy::TagName;
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_locator_strategy_xpath() {
|
||||
let json = r#""xpath""#;
|
||||
let data = LocatorStrategy::XPath;
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_locator_strategy_invalid() {
|
||||
let json = r#""foo""#;
|
||||
assert!(serde_json::from_str::<LocatorStrategy>(&json).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_webelement() {
|
||||
let json = r#"{"element-6066-11e4-a52e-4f735466cecf":"elem"}"#;
|
||||
let data = WebElement::new("elem".into());
|
||||
|
||||
check_serialize_deserialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_webelement_invalid() {
|
||||
let data = r#"{"elem-6066-11e4-a52e-4f735466cecf":"elem"}"#;
|
||||
assert!(serde_json::from_str::<WebElement>(&data).is_err());
|
||||
impl ToJson for WebElement {
|
||||
fn to_json(&self) -> Json {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert(ELEMENT_KEY.to_string(), self.id.to_json());
|
||||
Json::Object(data)
|
||||
}
|
||||
}
|
||||
|
||||
impl <T> From<T> for WebElement
|
||||
where T: Into<String> {
|
||||
fn from(data: T) -> WebElement {
|
||||
WebElement::new(data.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum FrameId {
|
||||
Short(u16),
|
||||
Element(WebElement),
|
||||
Null
|
||||
}
|
||||
|
||||
impl FrameId {
|
||||
pub fn from_json(data: &Json) -> WebDriverResult<FrameId> {
|
||||
match data {
|
||||
&Json::U64(x) => {
|
||||
if x > u16::max_value() as u64 || x < u16::min_value() as u64 {
|
||||
return Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
|
||||
"frame id out of range"))
|
||||
};
|
||||
Ok(FrameId::Short(x as u16))
|
||||
},
|
||||
&Json::Null => Ok(FrameId::Null),
|
||||
&Json::Object(_) => Ok(FrameId::Element(
|
||||
try!(WebElement::from_json(data)))),
|
||||
_ => Err(WebDriverError::new(ErrorStatus::NoSuchFrame,
|
||||
"frame id has unexpected type"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for FrameId {
|
||||
fn to_json(&self) -> Json {
|
||||
match *self {
|
||||
FrameId::Short(x) => {
|
||||
Json::U64(x as u64)
|
||||
},
|
||||
FrameId::Element(ref x) => {
|
||||
Json::String(x.id.clone())
|
||||
},
|
||||
FrameId::Null => {
|
||||
Json::Null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum LocatorStrategy {
|
||||
CSSSelector,
|
||||
LinkText,
|
||||
PartialLinkText,
|
||||
TagName,
|
||||
XPath,
|
||||
}
|
||||
|
||||
impl LocatorStrategy {
|
||||
pub fn from_json(body: &Json) -> WebDriverResult<LocatorStrategy> {
|
||||
match try_opt!(body.as_string(),
|
||||
ErrorStatus::InvalidArgument,
|
||||
"Expected locator strategy as string") {
|
||||
"css selector" => Ok(LocatorStrategy::CSSSelector),
|
||||
"link text" => Ok(LocatorStrategy::LinkText),
|
||||
"partial link text" => Ok(LocatorStrategy::PartialLinkText),
|
||||
"tag name" => Ok(LocatorStrategy::TagName),
|
||||
"xpath" => Ok(LocatorStrategy::XPath),
|
||||
x => Err(WebDriverError::new(ErrorStatus::InvalidArgument,
|
||||
format!("Unknown locator strategy {}", x)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for LocatorStrategy {
|
||||
fn to_json(&self) -> Json {
|
||||
Json::String(match *self {
|
||||
LocatorStrategy::CSSSelector => "css selector",
|
||||
LocatorStrategy::LinkText => "link text",
|
||||
LocatorStrategy::PartialLinkText => "partial link text",
|
||||
LocatorStrategy::TagName => "tag name",
|
||||
LocatorStrategy::XPath => "xpath"
|
||||
}.to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use base64::DecodeError;
|
||||
use hyper::status::StatusCode;
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_json;
|
||||
use rustc_serialize::base64::FromBase64Error;
|
||||
use rustc_serialize::json::{DecoderError, Json, ParserError, ToJson};
|
||||
use std::borrow::Cow;
|
||||
use std::collections::BTreeMap;
|
||||
use std::convert::From;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
@ -140,15 +140,6 @@ pub enum ErrorStatus {
|
|||
UnsupportedOperation,
|
||||
}
|
||||
|
||||
impl Serialize for ErrorStatus {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.error_code().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl ErrorStatus {
|
||||
/// Returns the string serialisation of the error type.
|
||||
pub fn error_code(&self) -> &'static str {
|
||||
|
@ -178,7 +169,8 @@ impl ErrorStatus {
|
|||
UnableToCaptureScreen => "unable to capture screen",
|
||||
UnableToSetCookie => "unable to set cookie",
|
||||
UnexpectedAlertOpen => "unexpected alert open",
|
||||
UnknownCommand | UnknownError => "unknown error",
|
||||
UnknownCommand |
|
||||
UnknownError => "unknown error",
|
||||
UnknownMethod => "unknown method",
|
||||
UnknownPath => "unknown command",
|
||||
UnsupportedOperation => "unsupported operation",
|
||||
|
@ -261,36 +253,17 @@ impl From<String> for ErrorStatus {
|
|||
|
||||
pub type WebDriverResult<T> = Result<T, WebDriverError>;
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[serde(remote = "Self")]
|
||||
#[derive(Debug)]
|
||||
pub struct WebDriverError {
|
||||
pub error: ErrorStatus,
|
||||
pub message: Cow<'static, str>,
|
||||
#[serde(rename = "stacktrace")]
|
||||
pub stack: Cow<'static, str>,
|
||||
#[serde(skip)]
|
||||
pub delete_session: bool,
|
||||
}
|
||||
|
||||
impl Serialize for WebDriverError {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
#[derive(Serialize)]
|
||||
struct Wrapper<'a> {
|
||||
#[serde(with = "WebDriverError")]
|
||||
value: &'a WebDriverError,
|
||||
}
|
||||
|
||||
Wrapper { value: self }.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
impl WebDriverError {
|
||||
pub fn new<S>(error: ErrorStatus, message: S) -> WebDriverError
|
||||
where
|
||||
S: Into<Cow<'static, str>>,
|
||||
where S: Into<Cow<'static, str>>
|
||||
{
|
||||
WebDriverError {
|
||||
error: error,
|
||||
|
@ -301,8 +274,7 @@ impl WebDriverError {
|
|||
}
|
||||
|
||||
pub fn new_with_stack<S>(error: ErrorStatus, message: S, stack: S) -> WebDriverError
|
||||
where
|
||||
S: Into<Cow<'static, str>>,
|
||||
where S: Into<Cow<'static, str>>
|
||||
{
|
||||
WebDriverError {
|
||||
error: error,
|
||||
|
@ -319,6 +291,23 @@ impl WebDriverError {
|
|||
pub fn http_status(&self) -> StatusCode {
|
||||
self.error.http_status()
|
||||
}
|
||||
|
||||
pub fn to_json_string(&self) -> String {
|
||||
self.to_json().to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for WebDriverError {
|
||||
fn to_json(&self) -> Json {
|
||||
let mut data = BTreeMap::new();
|
||||
data.insert("error".into(), self.error_code().to_json());
|
||||
data.insert("message".into(), self.message.to_json());
|
||||
data.insert("stacktrace".into(), self.stack.to_json());
|
||||
|
||||
let mut wrapper = BTreeMap::new();
|
||||
wrapper.insert("value".into(), Json::Object(data));
|
||||
Json::Object(wrapper)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for WebDriverError {
|
||||
|
@ -337,9 +326,9 @@ impl fmt::Display for WebDriverError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<serde_json::Error> for WebDriverError {
|
||||
fn from(err: serde_json::Error) -> WebDriverError {
|
||||
WebDriverError::new(ErrorStatus::InvalidArgument, err.to_string())
|
||||
impl From<ParserError> for WebDriverError {
|
||||
fn from(err: ParserError) -> WebDriverError {
|
||||
WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -349,8 +338,14 @@ impl From<io::Error> for WebDriverError {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<DecodeError> for WebDriverError {
|
||||
fn from(err: DecodeError) -> WebDriverError {
|
||||
impl From<DecoderError> for WebDriverError {
|
||||
fn from(err: DecoderError) -> WebDriverError {
|
||||
WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<FromBase64Error> for WebDriverError {
|
||||
fn from(err: FromBase64Error) -> WebDriverError {
|
||||
WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
|
||||
}
|
||||
}
|
||||
|
@ -360,34 +355,3 @@ impl From<Box<Error>> for WebDriverError {
|
|||
WebDriverError::new(ErrorStatus::UnknownError, err.description().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use test::check_serialize;
|
||||
|
||||
#[test]
|
||||
fn test_json_webdriver_error() {
|
||||
let json = r#"{"value":{
|
||||
"error":"unknown error",
|
||||
"message":"foo bar",
|
||||
"stacktrace":"foo\nbar"
|
||||
}}"#;
|
||||
let data = WebDriverError {
|
||||
error: ErrorStatus::UnknownError,
|
||||
message: "foo bar".into(),
|
||||
stack: "foo\nbar".into(),
|
||||
delete_session: true,
|
||||
};
|
||||
|
||||
check_serialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_error_status() {
|
||||
let json = format!(r#""unknown error""#);
|
||||
let data = ErrorStatus::UnknownError;
|
||||
|
||||
check_serialize(&json, &data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,228 +1,78 @@
|
|||
use regex::{Captures, Regex};
|
||||
use regex::{Regex, Captures};
|
||||
use rustc_serialize::json::Json;
|
||||
|
||||
use hyper::method::Method;
|
||||
use hyper::method::Method::{Delete, Get, Post};
|
||||
use serde_json::Value;
|
||||
use hyper::method::Method::{Get, Post, Delete};
|
||||
|
||||
use command::{VoidWebDriverExtensionCommand, WebDriverCommand, WebDriverExtensionCommand,
|
||||
WebDriverMessage};
|
||||
use error::{ErrorStatus, WebDriverError, WebDriverResult};
|
||||
use command::{WebDriverCommand, WebDriverMessage, WebDriverExtensionCommand,
|
||||
VoidWebDriverExtensionCommand};
|
||||
use error::{WebDriverResult, WebDriverError, ErrorStatus};
|
||||
|
||||
fn standard_routes<U: WebDriverExtensionRoute>() -> Vec<(Method, &'static str, Route<U>)> {
|
||||
return vec![
|
||||
(Post, "/session", Route::NewSession),
|
||||
(Delete, "/session/{sessionId}", Route::DeleteSession),
|
||||
(Post, "/session/{sessionId}/url", Route::Get),
|
||||
(Get, "/session/{sessionId}/url", Route::GetCurrentUrl),
|
||||
(Post, "/session/{sessionId}/back", Route::GoBack),
|
||||
(Post, "/session/{sessionId}/forward", Route::GoForward),
|
||||
(Post, "/session/{sessionId}/refresh", Route::Refresh),
|
||||
(Get, "/session/{sessionId}/title", Route::GetTitle),
|
||||
(Get, "/session/{sessionId}/source", Route::GetPageSource),
|
||||
(Get, "/session/{sessionId}/window", Route::GetWindowHandle),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/window/handles",
|
||||
Route::GetWindowHandles,
|
||||
),
|
||||
(Delete, "/session/{sessionId}/window", Route::CloseWindow),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/window/size",
|
||||
Route::GetWindowSize,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/window/size",
|
||||
Route::SetWindowSize,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/window/position",
|
||||
Route::GetWindowPosition,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/window/position",
|
||||
Route::SetWindowPosition,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/window/rect",
|
||||
Route::GetWindowRect,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/window/rect",
|
||||
Route::SetWindowRect,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/window/minimize",
|
||||
Route::MinimizeWindow,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/window/maximize",
|
||||
Route::MaximizeWindow,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/window/fullscreen",
|
||||
Route::FullscreenWindow,
|
||||
),
|
||||
(Post, "/session/{sessionId}/window", Route::SwitchToWindow),
|
||||
(Post, "/session/{sessionId}/frame", Route::SwitchToFrame),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/frame/parent",
|
||||
Route::SwitchToParentFrame,
|
||||
),
|
||||
(Post, "/session/{sessionId}/element", Route::FindElement),
|
||||
(Post, "/session/{sessionId}/elements", Route::FindElements),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/element/{elementId}/element",
|
||||
Route::FindElementElement,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/element/{elementId}/elements",
|
||||
Route::FindElementElements,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/active",
|
||||
Route::GetActiveElement,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/displayed",
|
||||
Route::IsDisplayed,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/selected",
|
||||
Route::IsSelected,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/attribute/{name}",
|
||||
Route::GetElementAttribute,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/property/{name}",
|
||||
Route::GetElementProperty,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/css/{propertyName}",
|
||||
Route::GetCSSValue,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/text",
|
||||
Route::GetElementText,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/name",
|
||||
Route::GetElementTagName,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/rect",
|
||||
Route::GetElementRect,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/enabled",
|
||||
Route::IsEnabled,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/execute/sync",
|
||||
Route::ExecuteScript,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/execute/async",
|
||||
Route::ExecuteAsyncScript,
|
||||
),
|
||||
(Get, "/session/{sessionId}/cookie", Route::GetCookies),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/cookie/{name}",
|
||||
Route::GetNamedCookie,
|
||||
),
|
||||
(Post, "/session/{sessionId}/cookie", Route::AddCookie),
|
||||
(Delete, "/session/{sessionId}/cookie", Route::DeleteCookies),
|
||||
(
|
||||
Delete,
|
||||
"/session/{sessionId}/cookie/{name}",
|
||||
Route::DeleteCookie,
|
||||
),
|
||||
(Get, "/session/{sessionId}/timeouts", Route::GetTimeouts),
|
||||
(Post, "/session/{sessionId}/timeouts", Route::SetTimeouts),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/element/{elementId}/click",
|
||||
Route::ElementClick,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/element/{elementId}/tap",
|
||||
Route::ElementTap,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/element/{elementId}/clear",
|
||||
Route::ElementClear,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/element/{elementId}/value",
|
||||
Route::ElementSendKeys,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/alert/dismiss",
|
||||
Route::DismissAlert,
|
||||
),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/alert/accept",
|
||||
Route::AcceptAlert,
|
||||
),
|
||||
(Get, "/session/{sessionId}/alert/text", Route::GetAlertText),
|
||||
(
|
||||
Post,
|
||||
"/session/{sessionId}/alert/text",
|
||||
Route::SendAlertText,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/screenshot",
|
||||
Route::TakeScreenshot,
|
||||
),
|
||||
(
|
||||
Get,
|
||||
"/session/{sessionId}/element/{elementId}/screenshot",
|
||||
Route::TakeElementScreenshot,
|
||||
),
|
||||
(Post, "/session/{sessionId}/actions", Route::PerformActions),
|
||||
(
|
||||
Delete,
|
||||
"/session/{sessionId}/actions",
|
||||
Route::ReleaseActions,
|
||||
),
|
||||
(Get, "/status", Route::Status),
|
||||
];
|
||||
fn standard_routes<U:WebDriverExtensionRoute>() -> Vec<(Method, &'static str, Route<U>)> {
|
||||
return vec![(Post, "/session", Route::NewSession),
|
||||
(Delete, "/session/{sessionId}", Route::DeleteSession),
|
||||
(Post, "/session/{sessionId}/url", Route::Get),
|
||||
(Get, "/session/{sessionId}/url", Route::GetCurrentUrl),
|
||||
(Post, "/session/{sessionId}/back", Route::GoBack),
|
||||
(Post, "/session/{sessionId}/forward", Route::GoForward),
|
||||
(Post, "/session/{sessionId}/refresh", Route::Refresh),
|
||||
(Get, "/session/{sessionId}/title", Route::GetTitle),
|
||||
(Get, "/session/{sessionId}/source", Route::GetPageSource),
|
||||
(Get, "/session/{sessionId}/window", Route::GetWindowHandle),
|
||||
(Get, "/session/{sessionId}/window/handles", Route::GetWindowHandles),
|
||||
(Delete, "/session/{sessionId}/window", Route::CloseWindow),
|
||||
(Get, "/session/{sessionId}/window/size", Route::GetWindowSize),
|
||||
(Post, "/session/{sessionId}/window/size", Route::SetWindowSize),
|
||||
(Get, "/session/{sessionId}/window/position", Route::GetWindowPosition),
|
||||
(Post, "/session/{sessionId}/window/position", Route::SetWindowPosition),
|
||||
(Get, "/session/{sessionId}/window/rect", Route::GetWindowRect),
|
||||
(Post, "/session/{sessionId}/window/rect", Route::SetWindowRect),
|
||||
(Post, "/session/{sessionId}/window/minimize", Route::MinimizeWindow),
|
||||
(Post, "/session/{sessionId}/window/maximize", Route::MaximizeWindow),
|
||||
(Post, "/session/{sessionId}/window/fullscreen", Route::FullscreenWindow),
|
||||
(Post, "/session/{sessionId}/window", Route::SwitchToWindow),
|
||||
(Post, "/session/{sessionId}/frame", Route::SwitchToFrame),
|
||||
(Post, "/session/{sessionId}/frame/parent", Route::SwitchToParentFrame),
|
||||
(Post, "/session/{sessionId}/element", Route::FindElement),
|
||||
(Post, "/session/{sessionId}/elements", Route::FindElements),
|
||||
(Post, "/session/{sessionId}/element/{elementId}/element", Route::FindElementElement),
|
||||
(Post, "/session/{sessionId}/element/{elementId}/elements", Route::FindElementElements),
|
||||
(Get, "/session/{sessionId}/element/active", Route::GetActiveElement),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/displayed", Route::IsDisplayed),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/selected", Route::IsSelected),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/attribute/{name}", Route::GetElementAttribute),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/property/{name}", Route::GetElementProperty),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/css/{propertyName}", Route::GetCSSValue),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/text", Route::GetElementText),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/name", Route::GetElementTagName),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/rect", Route::GetElementRect),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/enabled", Route::IsEnabled),
|
||||
(Post, "/session/{sessionId}/execute/sync", Route::ExecuteScript),
|
||||
(Post, "/session/{sessionId}/execute/async", Route::ExecuteAsyncScript),
|
||||
(Get, "/session/{sessionId}/cookie", Route::GetCookies),
|
||||
(Get, "/session/{sessionId}/cookie/{name}", Route::GetNamedCookie),
|
||||
(Post, "/session/{sessionId}/cookie", Route::AddCookie),
|
||||
(Delete, "/session/{sessionId}/cookie", Route::DeleteCookies),
|
||||
(Delete, "/session/{sessionId}/cookie/{name}", Route::DeleteCookie),
|
||||
(Get, "/session/{sessionId}/timeouts", Route::GetTimeouts),
|
||||
(Post, "/session/{sessionId}/timeouts", Route::SetTimeouts),
|
||||
(Post, "/session/{sessionId}/element/{elementId}/click", Route::ElementClick),
|
||||
(Post, "/session/{sessionId}/element/{elementId}/tap", Route::ElementTap),
|
||||
(Post, "/session/{sessionId}/element/{elementId}/clear", Route::ElementClear),
|
||||
(Post, "/session/{sessionId}/element/{elementId}/value", Route::ElementSendKeys),
|
||||
(Post, "/session/{sessionId}/alert/dismiss", Route::DismissAlert),
|
||||
(Post, "/session/{sessionId}/alert/accept", Route::AcceptAlert),
|
||||
(Get, "/session/{sessionId}/alert/text", Route::GetAlertText),
|
||||
(Post, "/session/{sessionId}/alert/text", Route::SendAlertText),
|
||||
(Get, "/session/{sessionId}/screenshot", Route::TakeScreenshot),
|
||||
(Get, "/session/{sessionId}/element/{elementId}/screenshot", Route::TakeElementScreenshot),
|
||||
(Post, "/session/{sessionId}/actions", Route::PerformActions),
|
||||
(Delete, "/session/{sessionId}/actions", Route::ReleaseActions),
|
||||
(Get, "/status", Route::Status),]
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Route<U: WebDriverExtensionRoute> {
|
||||
pub enum Route<U:WebDriverExtensionRoute> {
|
||||
NewSession,
|
||||
DeleteSession,
|
||||
Get,
|
||||
|
@ -235,10 +85,10 @@ pub enum Route<U: WebDriverExtensionRoute> {
|
|||
GetWindowHandle,
|
||||
GetWindowHandles,
|
||||
CloseWindow,
|
||||
GetWindowSize, // deprecated
|
||||
SetWindowSize, // deprecated
|
||||
GetWindowPosition, // deprecated
|
||||
SetWindowPosition, // deprecated
|
||||
GetWindowSize, // deprecated
|
||||
SetWindowSize, // deprecated
|
||||
GetWindowPosition, // deprecated
|
||||
SetWindowPosition, // deprecated
|
||||
GetWindowRect,
|
||||
SetWindowRect,
|
||||
MinimizeWindow,
|
||||
|
@ -286,10 +136,10 @@ pub enum Route<U: WebDriverExtensionRoute> {
|
|||
Extension(U),
|
||||
}
|
||||
|
||||
pub trait WebDriverExtensionRoute: Clone + Send + PartialEq {
|
||||
pub trait WebDriverExtensionRoute : Clone + Send + PartialEq {
|
||||
type Command: WebDriverExtensionCommand + 'static;
|
||||
|
||||
fn command(&self, &Captures, &Value) -> WebDriverResult<WebDriverCommand<Self::Command>>;
|
||||
fn command(&self, &Captures, &Json) -> WebDriverResult<WebDriverCommand<Self::Command>>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
|
@ -298,11 +148,7 @@ pub struct VoidWebDriverExtensionRoute;
|
|||
impl WebDriverExtensionRoute for VoidWebDriverExtensionRoute {
|
||||
type Command = VoidWebDriverExtensionCommand;
|
||||
|
||||
fn command(
|
||||
&self,
|
||||
_: &Captures,
|
||||
_: &Value,
|
||||
) -> WebDriverResult<WebDriverCommand<VoidWebDriverExtensionCommand>> {
|
||||
fn command(&self, _:&Captures, _:&Json) -> WebDriverResult<WebDriverCommand<VoidWebDriverExtensionCommand>> {
|
||||
panic!("No extensions implemented");
|
||||
}
|
||||
}
|
||||
|
@ -311,16 +157,16 @@ impl WebDriverExtensionRoute for VoidWebDriverExtensionRoute {
|
|||
struct RequestMatcher<U: WebDriverExtensionRoute> {
|
||||
method: Method,
|
||||
path_regexp: Regex,
|
||||
match_type: Route<U>,
|
||||
match_type: Route<U>
|
||||
}
|
||||
|
||||
impl<U: WebDriverExtensionRoute> RequestMatcher<U> {
|
||||
impl <U: WebDriverExtensionRoute> RequestMatcher<U> {
|
||||
pub fn new(method: Method, path: &str, match_type: Route<U>) -> RequestMatcher<U> {
|
||||
let path_regexp = RequestMatcher::<U>::compile_path(path);
|
||||
RequestMatcher {
|
||||
method: method,
|
||||
path_regexp: path_regexp,
|
||||
match_type: match_type,
|
||||
match_type: match_type
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -338,7 +184,7 @@ impl<U: WebDriverExtensionRoute> RequestMatcher<U> {
|
|||
if !component.ends_with("}") {
|
||||
panic!("Invalid url pattern")
|
||||
}
|
||||
rv.push_str(&format!("(?P<{}>[^/]+)/", &component[1..component.len() - 1])[..]);
|
||||
rv.push_str(&format!("(?P<{}>[^/]+)/", &component[1..component.len()-1])[..]);
|
||||
} else {
|
||||
rv.push_str(&format!("{}/", component)[..]);
|
||||
}
|
||||
|
@ -356,20 +202,18 @@ pub struct WebDriverHttpApi<U: WebDriverExtensionRoute> {
|
|||
routes: Vec<(Method, RequestMatcher<U>)>,
|
||||
}
|
||||
|
||||
impl<U: WebDriverExtensionRoute> WebDriverHttpApi<U> {
|
||||
impl <U: WebDriverExtensionRoute> WebDriverHttpApi<U> {
|
||||
pub fn new(extension_routes: &[(Method, &str, U)]) -> WebDriverHttpApi<U> {
|
||||
let mut rv = WebDriverHttpApi::<U> { routes: vec![] };
|
||||
let mut rv = WebDriverHttpApi::<U> {
|
||||
routes: vec![],
|
||||
};
|
||||
debug!("Creating routes");
|
||||
for &(ref method, ref url, ref match_type) in standard_routes::<U>().iter() {
|
||||
rv.add(method.clone(), *url, (*match_type).clone());
|
||||
}
|
||||
};
|
||||
for &(ref method, ref url, ref extension_route) in extension_routes.iter() {
|
||||
rv.add(
|
||||
method.clone(),
|
||||
*url,
|
||||
Route::Extension(extension_route.clone()),
|
||||
);
|
||||
}
|
||||
rv.add(method.clone(), *url, Route::Extension(extension_route.clone()));
|
||||
};
|
||||
rv
|
||||
}
|
||||
|
||||
|
@ -378,33 +222,24 @@ impl<U: WebDriverExtensionRoute> WebDriverHttpApi<U> {
|
|||
self.routes.push((method, http_matcher));
|
||||
}
|
||||
|
||||
pub fn decode_request(
|
||||
&self,
|
||||
method: Method,
|
||||
path: &str,
|
||||
body: &str,
|
||||
) -> WebDriverResult<WebDriverMessage<U>> {
|
||||
pub fn decode_request(&self, method: Method, path: &str, body: &str) -> WebDriverResult<WebDriverMessage<U>> {
|
||||
let mut error = ErrorStatus::UnknownPath;
|
||||
for &(ref match_method, ref matcher) in self.routes.iter() {
|
||||
if method == *match_method {
|
||||
let (method_match, captures) = matcher.get_match(method.clone(), path);
|
||||
if captures.is_some() {
|
||||
if method_match {
|
||||
return WebDriverMessage::from_http(
|
||||
matcher.match_type.clone(),
|
||||
&captures.unwrap(),
|
||||
body,
|
||||
method == Post,
|
||||
);
|
||||
return WebDriverMessage::from_http(matcher.match_type.clone(),
|
||||
&captures.unwrap(),
|
||||
body,
|
||||
method == Post)
|
||||
} else {
|
||||
error = ErrorStatus::UnknownMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(WebDriverError::new(
|
||||
error,
|
||||
format!("{} {} did not match a known command", method, path),
|
||||
))
|
||||
Err(WebDriverError::new(error,
|
||||
format!("{} {} did not match a known command", method, path)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,31 +1,44 @@
|
|||
#![allow(non_snake_case)]
|
||||
|
||||
extern crate base64;
|
||||
extern crate cookie;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate rustc_serialize;
|
||||
extern crate hyper;
|
||||
extern crate regex;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
extern crate serde_json;
|
||||
extern crate cookie;
|
||||
extern crate time;
|
||||
extern crate unicode_segmentation;
|
||||
extern crate url;
|
||||
extern crate unicode_segmentation;
|
||||
|
||||
#[macro_use]
|
||||
pub mod macros;
|
||||
#[macro_use] pub mod macros;
|
||||
pub mod actions;
|
||||
pub mod httpapi;
|
||||
pub mod capabilities;
|
||||
pub mod command;
|
||||
pub mod common;
|
||||
pub mod error;
|
||||
pub mod httpapi;
|
||||
pub mod response;
|
||||
pub mod server;
|
||||
pub mod response;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test;
|
||||
mod nullable_tests {
|
||||
use super::common::Nullable;
|
||||
|
||||
#[test]
|
||||
fn test_nullable_map() {
|
||||
let mut test = Nullable::Value(21);
|
||||
|
||||
assert_eq!(test.map(|x| x << 1), Nullable::Value(42));
|
||||
|
||||
test = Nullable::Null;
|
||||
|
||||
assert_eq!(test.map(|x| x << 1), Nullable::Null);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nullable_into() {
|
||||
let test: Option<i32> = Nullable::Value(42).into();
|
||||
|
||||
assert_eq!(test, Some(42));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
macro_rules! try_opt {
|
||||
($expr:expr, $err_type:expr, $err_msg:expr) => {{
|
||||
($expr:expr, $err_type:expr, $err_msg:expr) => ({
|
||||
match $expr {
|
||||
Some(x) => x,
|
||||
None => return Err(WebDriverError::new($err_type, $err_msg)),
|
||||
None => return Err(WebDriverError::new($err_type, $err_msg))
|
||||
}
|
||||
}};
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
use common::Cookie;
|
||||
use serde::ser::{Serialize, Serializer};
|
||||
use serde_json::Value;
|
||||
use common::{Date, Nullable};
|
||||
use cookie;
|
||||
use rustc_serialize::json::{self, Json, ToJson};
|
||||
use std::collections::BTreeMap;
|
||||
use time;
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[serde(untagged, remote = "Self")]
|
||||
#[derive(Debug)]
|
||||
pub enum WebDriverResponse {
|
||||
CloseWindow(CloseWindowResponse),
|
||||
Cookie(CookieResponse),
|
||||
|
@ -17,71 +18,72 @@ pub enum WebDriverResponse {
|
|||
WindowRect(WindowRectResponse),
|
||||
}
|
||||
|
||||
impl Serialize for WebDriverResponse {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
#[derive(Serialize)]
|
||||
struct Wrapper<'a> {
|
||||
#[serde(with = "WebDriverResponse")]
|
||||
value: &'a WebDriverResponse,
|
||||
}
|
||||
impl WebDriverResponse {
|
||||
pub fn to_json_string(self) -> String {
|
||||
use response::WebDriverResponse::*;
|
||||
|
||||
Wrapper { value: self }.serialize(serializer)
|
||||
let obj = match self {
|
||||
CloseWindow(ref x) => json::encode(&x.to_json()),
|
||||
Cookie(ref x) => json::encode(x),
|
||||
Cookies(ref x) => json::encode(x),
|
||||
DeleteSession => Ok("null".to_string()),
|
||||
ElementRect(ref x) => json::encode(x),
|
||||
Generic(ref x) => json::encode(x),
|
||||
NewSession(ref x) => json::encode(x),
|
||||
Timeouts(ref x) => json::encode(x),
|
||||
Void => Ok("null".to_string()),
|
||||
WindowRect(ref x) => json::encode(&x.to_json()),
|
||||
}.unwrap();
|
||||
|
||||
match self {
|
||||
Generic(_) | Cookie(_) | Cookies(_) => obj,
|
||||
_ => {
|
||||
let mut data = String::with_capacity(11 + obj.len());
|
||||
data.push_str("{\"value\": ");
|
||||
data.push_str(&*obj);
|
||||
data.push_str("}");
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub struct CloseWindowResponse(pub Vec<String>);
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize)]
|
||||
pub struct CookieResponse(pub Cookie);
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub struct CookiesResponse(pub Vec<Cookie>);
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub struct ElementRectResponse {
|
||||
/// X axis position of the top-left corner of the element relative
|
||||
/// to the current browsing context’s document element in CSS reference
|
||||
/// pixels.
|
||||
pub x: f64,
|
||||
|
||||
/// Y axis position of the top-left corner of the element relative
|
||||
/// to the current browsing context’s document element in CSS reference
|
||||
/// pixels.
|
||||
pub y: f64,
|
||||
|
||||
/// Height of the element’s [bounding rectangle] in CSS reference
|
||||
/// pixels.
|
||||
///
|
||||
/// [bounding rectangle]: https://drafts.fxtf.org/geometry/#rectangle
|
||||
pub width: f64,
|
||||
|
||||
/// Width of the element’s [bounding rectangle] in CSS reference
|
||||
/// pixels.
|
||||
///
|
||||
/// [bounding rectangle]: https://drafts.fxtf.org/geometry/#rectangle
|
||||
pub height: f64,
|
||||
#[derive(Debug, RustcEncodable)]
|
||||
pub struct CloseWindowResponse {
|
||||
pub window_handles: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
impl CloseWindowResponse {
|
||||
pub fn new(handles: Vec<String>) -> CloseWindowResponse {
|
||||
CloseWindowResponse { window_handles: handles }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for CloseWindowResponse {
|
||||
fn to_json(&self) -> Json {
|
||||
Json::Array(self.window_handles
|
||||
.iter()
|
||||
.map(|x| Json::String(x.clone()))
|
||||
.collect::<Vec<Json>>())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, RustcEncodable)]
|
||||
pub struct NewSessionResponse {
|
||||
pub sessionId: String,
|
||||
pub capabilities: Value,
|
||||
pub capabilities: json::Json
|
||||
}
|
||||
|
||||
impl NewSessionResponse {
|
||||
pub fn new(session_id: String, capabilities: Value) -> NewSessionResponse {
|
||||
pub fn new(session_id: String, capabilities: json::Json) -> NewSessionResponse {
|
||||
NewSessionResponse {
|
||||
capabilities: capabilities,
|
||||
sessionId: session_id,
|
||||
sessionId: session_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[derive(Debug, RustcEncodable)]
|
||||
pub struct TimeoutsResponse {
|
||||
pub script: u64,
|
||||
pub pageLoad: u64,
|
||||
|
@ -98,10 +100,45 @@ impl TimeoutsResponse {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
pub struct ValueResponse(pub Value);
|
||||
#[derive(Debug, RustcEncodable)]
|
||||
pub struct ValueResponse {
|
||||
pub value: json::Json
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
impl ValueResponse {
|
||||
pub fn new(value: json::Json) -> ValueResponse {
|
||||
ValueResponse {
|
||||
value: value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, RustcEncodable)]
|
||||
pub struct ElementRectResponse {
|
||||
/// X axis position of the top-left corner of the element relative
|
||||
// to the current browsing context’s document element in CSS reference
|
||||
// pixels.
|
||||
pub x: f64,
|
||||
|
||||
/// Y axis position of the top-left corner of the element relative
|
||||
// to the current browsing context’s document element in CSS reference
|
||||
// pixels.
|
||||
pub y: f64,
|
||||
|
||||
/// Height of the element’s [bounding rectangle] in CSS reference
|
||||
/// pixels.
|
||||
///
|
||||
/// [bounding rectangle]: https://drafts.fxtf.org/geometry/#rectangle
|
||||
pub width: f64,
|
||||
|
||||
/// Width of the element’s [bounding rectangle] in CSS reference
|
||||
/// pixels.
|
||||
///
|
||||
/// [bounding rectangle]: https://drafts.fxtf.org/geometry/#rectangle
|
||||
pub height: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct WindowRectResponse {
|
||||
/// `WindowProxy`’s [screenX] attribute.
|
||||
///
|
||||
|
@ -124,163 +161,170 @@ pub struct WindowRectResponse {
|
|||
pub height: i32,
|
||||
}
|
||||
|
||||
impl ToJson for WindowRectResponse {
|
||||
fn to_json(&self) -> Json {
|
||||
let mut body = BTreeMap::new();
|
||||
body.insert("x".to_owned(), self.x.to_json());
|
||||
body.insert("y".to_owned(), self.y.to_json());
|
||||
body.insert("width".to_owned(), self.width.to_json());
|
||||
body.insert("height".to_owned(), self.height.to_json());
|
||||
Json::Object(body)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, RustcEncodable)]
|
||||
pub struct Cookie {
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
pub path: Nullable<String>,
|
||||
pub domain: Nullable<String>,
|
||||
pub expiry: Nullable<Date>,
|
||||
pub secure: bool,
|
||||
pub httpOnly: bool,
|
||||
}
|
||||
|
||||
impl Into<cookie::Cookie<'static>> for Cookie {
|
||||
fn into(self) -> cookie::Cookie<'static> {
|
||||
let cookie = cookie::Cookie::build(self.name, self.value)
|
||||
.secure(self.secure)
|
||||
.http_only(self.httpOnly);
|
||||
let cookie = match self.domain {
|
||||
Nullable::Value(domain) => cookie.domain(domain),
|
||||
Nullable::Null => cookie,
|
||||
};
|
||||
let cookie = match self.path {
|
||||
Nullable::Value(path) => cookie.path(path),
|
||||
Nullable::Null => cookie,
|
||||
};
|
||||
let cookie = match self.expiry {
|
||||
Nullable::Value(Date(expiry)) => {
|
||||
cookie.expires(time::at(time::Timespec::new(expiry as i64, 0)))
|
||||
}
|
||||
Nullable::Null => cookie,
|
||||
};
|
||||
cookie.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, RustcEncodable)]
|
||||
pub struct CookieResponse {
|
||||
pub value: Cookie,
|
||||
}
|
||||
|
||||
#[derive(Debug, RustcEncodable)]
|
||||
pub struct CookiesResponse {
|
||||
pub value: Vec<Cookie>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use common::Date;
|
||||
use serde_json;
|
||||
use test::check_serialize;
|
||||
use super::{CloseWindowResponse, Cookie, CookieResponse, CookiesResponse, ElementRectResponse,
|
||||
NewSessionResponse, Nullable, TimeoutsResponse, ValueResponse, WebDriverResponse,
|
||||
WindowRectResponse};
|
||||
use rustc_serialize::json::Json;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
#[test]
|
||||
fn test_json_close_window_response() {
|
||||
let json = r#"{"value":["1234"]}"#;
|
||||
let data = WebDriverResponse::CloseWindow(CloseWindowResponse(vec!["1234".into()]));
|
||||
|
||||
check_serialize(&json, &data);
|
||||
fn test(resp: WebDriverResponse, expected_str: &str) {
|
||||
let data = resp.to_json_string();
|
||||
let actual = Json::from_str(&*data).unwrap();
|
||||
let expected = Json::from_str(expected_str).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_cookie_response_with_optional() {
|
||||
let json = r#"{"value":{
|
||||
"name":"foo",
|
||||
"value":"bar",
|
||||
"path":"/",
|
||||
"domain":"foo.bar",
|
||||
"secure":true,
|
||||
"httpOnly":false,
|
||||
"expiry":123
|
||||
}}"#;
|
||||
let data = WebDriverResponse::Cookie(CookieResponse(Cookie {
|
||||
name: "foo".into(),
|
||||
value: "bar".into(),
|
||||
path: Some("/".into()),
|
||||
domain: Some("foo.bar".into()),
|
||||
expiry: Some(Date(123)),
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
}));
|
||||
|
||||
check_serialize(&json, &data);
|
||||
fn test_close_window() {
|
||||
let resp = WebDriverResponse::CloseWindow(
|
||||
CloseWindowResponse::new(vec!["test".into()]));
|
||||
let expected = r#"{"value": ["test"]}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_cookie_response_without_optional() {
|
||||
let json = r#"{"value":{
|
||||
"name":"foo",
|
||||
"value":"bar",
|
||||
"path":"/",
|
||||
"domain":null,
|
||||
"secure":true,
|
||||
"httpOnly":false
|
||||
}}"#;
|
||||
let data = WebDriverResponse::Cookie(CookieResponse(Cookie {
|
||||
name: "foo".into(),
|
||||
value: "bar".into(),
|
||||
path: Some("/".into()),
|
||||
domain: None,
|
||||
expiry: None,
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
}));
|
||||
|
||||
check_serialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_cookies_response() {
|
||||
let json = r#"{"value":[{
|
||||
"name":"name",
|
||||
"value":"value",
|
||||
"path":"/",
|
||||
"domain":null,
|
||||
"secure":true,
|
||||
"httpOnly":false
|
||||
}]}"#;
|
||||
let data = WebDriverResponse::Cookies(CookiesResponse(vec![Cookie {
|
||||
fn test_cookie() {
|
||||
let cookie = Cookie {
|
||||
name: "name".into(),
|
||||
value: "value".into(),
|
||||
path: Some("/".into()),
|
||||
domain: None,
|
||||
expiry: None,
|
||||
path: Nullable::Value("/".into()),
|
||||
domain: Nullable::Null,
|
||||
expiry: Nullable::Null,
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
}]));
|
||||
|
||||
check_serialize(&json, &data);
|
||||
};
|
||||
let resp = WebDriverResponse::Cookie(CookieResponse { value: cookie });
|
||||
let expected = r#"{"value": {"name": "name", "expiry": null, "value": "value",
|
||||
"path": "/", "domain": null, "secure": true, "httpOnly": false}}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_delete_session_response() {
|
||||
let json = r#"{"value":null}"#;
|
||||
let data = WebDriverResponse::DeleteSession;
|
||||
|
||||
check_serialize(&json, &data);
|
||||
fn test_cookies() {
|
||||
let resp = WebDriverResponse::Cookies(CookiesResponse {
|
||||
value: vec![
|
||||
Cookie {
|
||||
name: "name".into(),
|
||||
value: "value".into(),
|
||||
path: Nullable::Value("/".into()),
|
||||
domain: Nullable::Null,
|
||||
expiry: Nullable::Null,
|
||||
secure: true,
|
||||
httpOnly: false,
|
||||
}
|
||||
]});
|
||||
let expected = r#"{"value": [{"name": "name", "value": "value", "path": "/",
|
||||
"domain": null, "expiry": null, "secure": true, "httpOnly": false}]}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_element_rect_response() {
|
||||
let json = r#"{"value":{"x":0.0,"y":1.0,"width":2.0,"height":3.0}}"#;
|
||||
let data = WebDriverResponse::ElementRect(ElementRectResponse {
|
||||
fn test_element_rect() {
|
||||
let rect = ElementRectResponse {
|
||||
x: 0f64,
|
||||
y: 1f64,
|
||||
width: 2f64,
|
||||
height: 3f64,
|
||||
});
|
||||
|
||||
check_serialize(&json, &data);
|
||||
};
|
||||
let resp = WebDriverResponse::ElementRect(rect);
|
||||
let expected = r#"{"value": {"x": 0.0, "y": 1.0, "width": 2.0, "height": 3.0}}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_generic_value_response() {
|
||||
let json = r#"{"value":{"example":["test"]}}"#;
|
||||
let mut value = serde_json::Map::new();
|
||||
value.insert(
|
||||
"example".into(),
|
||||
Value::Array(vec![Value::String("test".into())]),
|
||||
);
|
||||
|
||||
let data = WebDriverResponse::Generic(ValueResponse(Value::Object(value)));
|
||||
|
||||
check_serialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_new_session_response() {
|
||||
let json = r#"{"value":{"sessionId":"id","capabilities":{}}}"#;
|
||||
let data = WebDriverResponse::NewSession(NewSessionResponse::new(
|
||||
"id".into(),
|
||||
Value::Object(serde_json::Map::new()),
|
||||
));
|
||||
|
||||
check_serialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_timeouts_response() {
|
||||
let json = r#"{"value":{"script":1,"pageLoad":2,"implicit":3}}"#;
|
||||
let data = WebDriverResponse::Timeouts(TimeoutsResponse::new(1, 2, 3));
|
||||
|
||||
check_serialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_void_response() {
|
||||
let json = r#"{"value":null}"#;
|
||||
let data = WebDriverResponse::Void;
|
||||
|
||||
check_serialize(&json, &data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_json_window_rect_response() {
|
||||
let json = r#"{"value":{"x":0,"y":1,"width":2,"height":3}}"#;
|
||||
let data = WebDriverResponse::WindowRect(WindowRectResponse {
|
||||
fn test_window_rect() {
|
||||
let rect = WindowRectResponse {
|
||||
x: 0i32,
|
||||
y: 1i32,
|
||||
width: 2i32,
|
||||
height: 3i32,
|
||||
});
|
||||
};
|
||||
let resp = WebDriverResponse::WindowRect(rect);
|
||||
let expected = r#"{"value": {"x": 0, "y": 1, "width": 2, "height": 3}}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
check_serialize(&json, &data);
|
||||
#[test]
|
||||
fn test_new_session() {
|
||||
let resp = WebDriverResponse::NewSession(
|
||||
NewSessionResponse::new("test".into(),
|
||||
Json::Object(BTreeMap::new())));
|
||||
let expected = r#"{"value": {"sessionId": "test", "capabilities": {}}}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_timeouts() {
|
||||
let resp = WebDriverResponse::Timeouts(TimeoutsResponse::new(
|
||||
1, 2, 3));
|
||||
let expected = r#"{"value": {"script": 1, "pageLoad": 2, "implicit": 3}}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value() {
|
||||
let mut value = BTreeMap::new();
|
||||
value.insert("example".into(), Json::Array(vec![Json::String("test".into())]));
|
||||
let resp = WebDriverResponse::Generic(ValueResponse::new(
|
||||
Json::Object(value)));
|
||||
let expected = r#"{"value": {"example": ["test"]}}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use serde_json;
|
||||
use std::io::Read;
|
||||
use std::marker::PhantomData;
|
||||
use std::net::SocketAddr;
|
||||
|
@ -7,49 +6,45 @@ use std::sync::Mutex;
|
|||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use hyper::header::{CacheControl, CacheDirective, ContentType};
|
||||
use hyper::header::{ContentType, CacheControl, CacheDirective};
|
||||
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
|
||||
use hyper::method::Method;
|
||||
use hyper::mime::{Attr, Mime, SubLevel, TopLevel, Value};
|
||||
use hyper::Result;
|
||||
use hyper::server::{Handler, Listening, Request, Response, Server};
|
||||
use hyper::status::StatusCode;
|
||||
use hyper::uri::RequestUri::AbsolutePath;
|
||||
use hyper::Result;
|
||||
|
||||
use command::{WebDriverCommand, WebDriverMessage};
|
||||
use error::{ErrorStatus, WebDriverError, WebDriverResult};
|
||||
use httpapi::{VoidWebDriverExtensionRoute, WebDriverExtensionRoute, WebDriverHttpApi};
|
||||
use command::{WebDriverMessage, WebDriverCommand};
|
||||
use error::{WebDriverResult, WebDriverError, ErrorStatus};
|
||||
use httpapi::{WebDriverHttpApi, WebDriverExtensionRoute, VoidWebDriverExtensionRoute};
|
||||
use response::{CloseWindowResponse, WebDriverResponse};
|
||||
|
||||
enum DispatchMessage<U: WebDriverExtensionRoute> {
|
||||
HandleWebDriver(
|
||||
WebDriverMessage<U>,
|
||||
Sender<WebDriverResult<WebDriverResponse>>,
|
||||
),
|
||||
Quit,
|
||||
HandleWebDriver(WebDriverMessage<U>, Sender<WebDriverResult<WebDriverResponse>>),
|
||||
Quit
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Session {
|
||||
pub id: String,
|
||||
pub id: String
|
||||
}
|
||||
|
||||
impl Session {
|
||||
fn new(id: String) -> Session {
|
||||
Session { id: id }
|
||||
Session {
|
||||
id: id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WebDriverHandler<U: WebDriverExtensionRoute = VoidWebDriverExtensionRoute>: Send {
|
||||
fn handle_command(
|
||||
&mut self,
|
||||
session: &Option<Session>,
|
||||
msg: WebDriverMessage<U>,
|
||||
) -> WebDriverResult<WebDriverResponse>;
|
||||
pub trait WebDriverHandler<U: WebDriverExtensionRoute=VoidWebDriverExtensionRoute> : Send {
|
||||
fn handle_command(&mut self, session: &Option<Session>, msg: WebDriverMessage<U>) -> WebDriverResult<WebDriverResponse>;
|
||||
fn delete_session(&mut self, session: &Option<Session>);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Dispatcher<T: WebDriverHandler<U>, U: WebDriverExtensionRoute> {
|
||||
struct Dispatcher<T: WebDriverHandler<U>,
|
||||
U: WebDriverExtensionRoute> {
|
||||
handler: T,
|
||||
session: Option<Session>,
|
||||
extension_type: PhantomData<U>,
|
||||
|
@ -77,8 +72,8 @@ impl<T: WebDriverHandler<U>, U: WebDriverExtensionRoute> Dispatcher<T, U> {
|
|||
Ok(WebDriverResponse::NewSession(ref new_session)) => {
|
||||
self.session = Some(Session::new(new_session.sessionId.clone()));
|
||||
}
|
||||
Ok(WebDriverResponse::CloseWindow(CloseWindowResponse(ref handles))) => {
|
||||
if handles.len() == 0 {
|
||||
Ok(WebDriverResponse::CloseWindow(CloseWindowResponse { ref window_handles })) => {
|
||||
if window_handles.len() == 0 {
|
||||
debug!("Last window was closed, deleting session");
|
||||
self.delete_session();
|
||||
}
|
||||
|
@ -106,46 +101,49 @@ impl<T: WebDriverHandler<U>, U: WebDriverExtensionRoute> Dispatcher<T, U> {
|
|||
|
||||
fn check_session(&self, msg: &WebDriverMessage<U>) -> WebDriverResult<()> {
|
||||
match msg.session_id {
|
||||
Some(ref msg_session_id) => match self.session {
|
||||
Some(ref existing_session) => {
|
||||
if existing_session.id != *msg_session_id {
|
||||
Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidSessionId,
|
||||
format!("Got unexpected session id {}", msg_session_id),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
Some(ref msg_session_id) => {
|
||||
match self.session {
|
||||
Some(ref existing_session) => {
|
||||
if existing_session.id != *msg_session_id {
|
||||
Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidSessionId,
|
||||
format!("Got unexpected session id {}",
|
||||
msg_session_id)))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
None => Ok(())
|
||||
}
|
||||
None => Ok(()),
|
||||
},
|
||||
None => {
|
||||
match self.session {
|
||||
Some(_) => {
|
||||
match msg.command {
|
||||
WebDriverCommand::Status => Ok(()),
|
||||
WebDriverCommand::NewSession(_) => Err(WebDriverError::new(
|
||||
ErrorStatus::SessionNotCreated,
|
||||
"Session is already started",
|
||||
)),
|
||||
WebDriverCommand::NewSession(_) => {
|
||||
Err(WebDriverError::new(
|
||||
ErrorStatus::SessionNotCreated,
|
||||
"Session is already started"))
|
||||
},
|
||||
_ => {
|
||||
//This should be impossible
|
||||
error!("Got a message with no session id");
|
||||
Err(WebDriverError::new(
|
||||
ErrorStatus::UnknownError,
|
||||
"Got a command with no session?!",
|
||||
))
|
||||
"Got a command with no session?!"))
|
||||
}
|
||||
}
|
||||
}
|
||||
None => match msg.command {
|
||||
WebDriverCommand::NewSession(_) => Ok(()),
|
||||
WebDriverCommand::Status => Ok(()),
|
||||
_ => Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidSessionId,
|
||||
"Tried to run a command before creating a session",
|
||||
)),
|
||||
},
|
||||
None => {
|
||||
match msg.command {
|
||||
WebDriverCommand::NewSession(_) => Ok(()),
|
||||
WebDriverCommand::Status => Ok(()),
|
||||
_ => Err(WebDriverError::new(
|
||||
ErrorStatus::InvalidSessionId,
|
||||
"Tried to run a command before creating a session"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,14 +153,14 @@ impl<T: WebDriverHandler<U>, U: WebDriverExtensionRoute> Dispatcher<T, U> {
|
|||
#[derive(Debug)]
|
||||
struct HttpHandler<U: WebDriverExtensionRoute> {
|
||||
chan: Mutex<Sender<DispatchMessage<U>>>,
|
||||
api: Mutex<WebDriverHttpApi<U>>,
|
||||
api: Mutex<WebDriverHttpApi<U>>
|
||||
}
|
||||
|
||||
impl<U: WebDriverExtensionRoute> HttpHandler<U> {
|
||||
impl <U: WebDriverExtensionRoute> HttpHandler<U> {
|
||||
fn new(api: WebDriverHttpApi<U>, chan: Sender<DispatchMessage<U>>) -> HttpHandler<U> {
|
||||
HttpHandler {
|
||||
chan: Mutex::new(chan),
|
||||
api: Mutex::new(api),
|
||||
api: Mutex::new(api)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,18 +208,16 @@ impl<U: WebDriverExtensionRoute> Handler for HttpHandler<U> {
|
|||
}
|
||||
}
|
||||
match recv_res.recv() {
|
||||
Ok(data) => match data {
|
||||
Ok(response) => {
|
||||
(StatusCode::Ok, serde_json::to_string(&response).unwrap())
|
||||
Ok(data) => {
|
||||
match data {
|
||||
Ok(response) => (StatusCode::Ok, response.to_json_string()),
|
||||
Err(err) => (err.http_status(), err.to_json_string()),
|
||||
}
|
||||
Err(err) => {
|
||||
(err.http_status(), serde_json::to_string(&err).unwrap())
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => panic!("Error reading response: {:?}", e),
|
||||
}
|
||||
}
|
||||
Err(err) => (err.http_status(), serde_json::to_string(&err).unwrap()),
|
||||
Err(err) => (err.http_status(), err.to_json_string()),
|
||||
};
|
||||
|
||||
debug!("<- {} {}", status, resp_body);
|
||||
|
@ -230,11 +226,10 @@ impl<U: WebDriverExtensionRoute> Handler for HttpHandler<U> {
|
|||
let resp_status = res.status_mut();
|
||||
*resp_status = status;
|
||||
}
|
||||
res.headers_mut().set(ContentType(Mime(
|
||||
TopLevel::Application,
|
||||
SubLevel::Json,
|
||||
vec![(Attr::Charset, Value::Utf8)],
|
||||
)));
|
||||
res.headers_mut()
|
||||
.set(ContentType(Mime(TopLevel::Application,
|
||||
SubLevel::Json,
|
||||
vec![(Attr::Charset, Value::Utf8)])));
|
||||
res.headers_mut()
|
||||
.set(CacheControl(vec![CacheDirective::NoCache]));
|
||||
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
use regex::Regex;
|
||||
use serde;
|
||||
use serde_json;
|
||||
use std;
|
||||
|
||||
lazy_static! {
|
||||
static ref MIN_REGEX: Regex = Regex::new(r"[\n\t]|\s{4}").unwrap();
|
||||
}
|
||||
|
||||
pub fn check_serialize_deserialize<T>(json: &str, data: &T)
|
||||
where
|
||||
T: std::fmt::Debug,
|
||||
T: std::cmp::PartialEq,
|
||||
T: serde::de::DeserializeOwned,
|
||||
T: serde::Serialize,
|
||||
{
|
||||
let min_json = MIN_REGEX.replace_all(json, "");
|
||||
|
||||
assert_eq!(*data, serde_json::from_str::<T>(&min_json).unwrap());
|
||||
assert_eq!(min_json, serde_json::to_string(data).unwrap());
|
||||
}
|
||||
|
||||
pub fn check_deserialize<T>(json: &str, data: &T)
|
||||
where
|
||||
T: std::fmt::Debug,
|
||||
T: std::cmp::PartialEq,
|
||||
T: serde::de::DeserializeOwned,
|
||||
{
|
||||
let min_json = MIN_REGEX.replace_all(json, "");
|
||||
|
||||
assert_eq!(serde_json::from_str::<T>(&min_json).unwrap(), *data);
|
||||
}
|
||||
|
||||
pub fn check_serialize<T>(json: &str, data: &T)
|
||||
where
|
||||
T: std::fmt::Debug,
|
||||
T: std::cmp::PartialEq,
|
||||
T: serde::Serialize,
|
||||
{
|
||||
let min_json = MIN_REGEX.replace_all(json, "");
|
||||
|
||||
assert_eq!(min_json, serde_json::to_string(data).unwrap());
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
{"files":{".travis.yml":"03dcea6dcd54625b42d91176e86718626dfd911744a343dee3edefa001e87dc5","Cargo.toml":"01199fa6ca6337a7513e9ef8951268b8882347e5affaa50e710ac4960d9c65e0","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"0aebc3beb6fc32d6073582d5fea170761689a2c83cddb5436aa26e57b7d04e7b","appveyor.yml":"da991211b72fa6f231af7adb84c9fb72f5a9131d1c0a3d47b8ceffe5a82c8542","benches/base64.rs":"96f7d0c7d260362e41b8cefb4839f1e1b3c18c2f10344f6ccafac7c434f99ca9","benches/hex.rs":"057821307b4b7de02f2c267f9248457386035382916c5afe4b72d6f2e905062c","benches/json.rs":"659f2ae2e1ad5ed022fafce6418d17dfe09c3dcb3f054857dce0effc907da850","src/base64.rs":"57649c590c1fba643ff955910f1d4427feda43414bb0863cd279bea56c3ff178","src/collection_impls.rs":"8ae6bc0d61a4777d834c2b24fa987550cb13c570e1564f87ee32eceff3cb2d5b","src/hex.rs":"a2ba86cf47035b5d9cbf4adf8dc3e941d4e0a6ce1a61a29cbb14ea1fdabac6bb","src/json.rs":"75a788a46612c73bfd14af20fb48855dc8c930747c5255a288d2d09de25ea960","src/lib.rs":"a0e4a368a609f019434e7584f54448cf33ebf3e37e3fb1dd5537d300088184b1","src/serialize.rs":"7ddcc3c32843850e30d05b82a8cda8ae63ec0016e2b0bfbcc46a03ea3ea986e8"},"package":"dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"}
|
|
@ -0,0 +1,24 @@
|
|||
language: rust
|
||||
rust:
|
||||
- 1.0.0
|
||||
- stable
|
||||
- beta
|
||||
- nightly
|
||||
sudo: false
|
||||
before_script:
|
||||
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- cargo test --verbose
|
||||
- cargo doc --no-deps
|
||||
after_success:
|
||||
- travis-cargo --only nightly doc-upload
|
||||
env:
|
||||
global:
|
||||
secure: "kJnqqAXRl0C7Afx0c8Y3vA6TAEZsxlasu7eIZMdCbNS4N1+Rwh0jNTa2jy2D3CQCrzW5OCefnkpkPTu8mADrAjedM4p/9X5UXZi0sgg2lzCgfGwrRzitTnyPDkdYidiu4QeC/r0WPC8lYZKHkJXYhF8bZgchB9ypnZ6LAHCcDkA="
|
||||
|
||||
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
|
@ -0,0 +1,18 @@
|
|||
[package]
|
||||
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
authors = ["The Rust Project Developers"]
|
||||
license = "MIT/Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/rust-lang/rustc-serialize"
|
||||
homepage = "https://github.com/rust-lang/rustc-serialize"
|
||||
documentation = "https://doc.rust-lang.org/rustc-serialize"
|
||||
description = """
|
||||
Generic serialization/deserialization support corresponding to the
|
||||
`derive(RustcEncodable, RustcDecodable)` mode in the compiler. Also includes
|
||||
support for hex, base64, and json encoding and decoding.
|
||||
"""
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.3"
|
|
@ -0,0 +1,31 @@
|
|||
# rustc-serialize
|
||||
|
||||
> **NOTE**: This crate is deprecated in favor of [`serde`]. No new feature
|
||||
> development will happen in this crate, although bug fixes proposed through PRs
|
||||
> will still be merged. It is very highly recommended by the Rust Library Team
|
||||
> that you use [`serde`], not this crate.
|
||||
|
||||
[`serde`]: https://serde.rs
|
||||
|
||||
Serialization and deserialization support provided by the compiler in the form
|
||||
of `derive(RustcEncodable, RustcDecodable)`.
|
||||
|
||||
[![Linux Build Status](https://travis-ci.org/rust-lang-nursery/rustc-serialize.svg?branch=master)](https://travis-ci.org/rust-lang-nursery/rustc-serialize)
|
||||
[![Windows Build Status](https://ci.appveyor.com/api/projects/status/ka194de75aapwpft?svg=true)](https://ci.appveyor.com/project/alexcrichton/rustc-serialize)
|
||||
|
||||
[Documentation](https://doc.rust-lang.org/rustc-serialize)
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
rustc-serialize = "0.3"
|
||||
```
|
||||
|
||||
and this to your crate root:
|
||||
|
||||
```rust
|
||||
extern crate rustc_serialize;
|
||||
```
|
|
@ -0,0 +1,17 @@
|
|||
environment:
|
||||
matrix:
|
||||
- TARGET: x86_64-pc-windows-msvc
|
||||
- TARGET: i686-pc-windows-msvc
|
||||
- TARGET: i686-pc-windows-gnu
|
||||
install:
|
||||
- ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe"
|
||||
- rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust"
|
||||
- SET PATH=%PATH%;C:\Program Files (x86)\Rust\bin
|
||||
- SET PATH=%PATH%;C:\MinGW\bin
|
||||
- rustc -V
|
||||
- cargo -V
|
||||
|
||||
build: false
|
||||
|
||||
test_script:
|
||||
- cargo test --verbose
|
|
@ -0,0 +1,48 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_to_base64(b: &mut Bencher) {
|
||||
let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
|
||||
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
|
||||
b.iter(|| {
|
||||
s.as_bytes().to_base64(STANDARD);
|
||||
});
|
||||
b.bytes = s.len() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_from_base64(b: &mut Bencher) {
|
||||
let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
|
||||
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
|
||||
let sb = s.as_bytes().to_base64(STANDARD);
|
||||
b.iter(|| {
|
||||
sb.from_base64().unwrap();
|
||||
});
|
||||
b.bytes = sb.len() as u64;
|
||||
}
|
||||
|
||||
|
||||
#[bench]
|
||||
fn bench_to_base64_large(b: &mut Bencher) {
|
||||
let s: Vec<_> = (0..10000).map(|i| ((i as u32 * 12345) % 256) as u8).collect();
|
||||
b.iter(|| {
|
||||
s.to_base64(STANDARD);
|
||||
});
|
||||
b.bytes = s.len() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_from_base64_large(b: &mut Bencher) {
|
||||
let s: Vec<_> = (0..10000).map(|i| ((i as u32 * 12345) % 256) as u8).collect();
|
||||
let sb = s.to_base64(STANDARD);
|
||||
b.iter(|| {
|
||||
sb.from_base64().unwrap();
|
||||
});
|
||||
b.bytes = sb.len() as u64;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use test::Bencher;
|
||||
use rustc_serialize::hex::{FromHex, ToHex};
|
||||
|
||||
#[bench]
|
||||
fn bench_to_hex(b: &mut Bencher) {
|
||||
let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
|
||||
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
|
||||
b.iter(|| {
|
||||
s.as_bytes().to_hex();
|
||||
});
|
||||
b.bytes = s.len() as u64;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_from_hex(b: &mut Bencher) {
|
||||
let s = "イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム \
|
||||
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン";
|
||||
let sb = s.as_bytes().to_hex();
|
||||
b.iter(|| {
|
||||
sb.from_hex().unwrap();
|
||||
});
|
||||
b.bytes = sb.len() as u64;
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
extern crate rustc_serialize;
|
||||
|
||||
use std::string;
|
||||
use rustc_serialize::json::{Json, Parser};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn bench_streaming_small(b: &mut Bencher) {
|
||||
b.iter( || {
|
||||
let mut parser = Parser::new(
|
||||
r#"{
|
||||
"a": 1.0,
|
||||
"b": [
|
||||
true,
|
||||
"foo\nbar",
|
||||
{ "c": {"d": null} }
|
||||
]
|
||||
}"#.chars()
|
||||
);
|
||||
loop {
|
||||
match parser.next() {
|
||||
None => return,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn bench_small(b: &mut Bencher) {
|
||||
b.iter( || {
|
||||
let _ = Json::from_str(r#"{
|
||||
"a": 1.0,
|
||||
"b": [
|
||||
true,
|
||||
"foo\nbar",
|
||||
{ "c": {"d": null} }
|
||||
]
|
||||
}"#);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_decode_hex_escape(b: &mut Bencher) {
|
||||
let mut src = "\"".to_string();
|
||||
for _ in 0..10 {
|
||||
src.push_str("\\uF975\\uf9bc\\uF9A0\\uF9C4\\uF975\\uf9bc\\uF9A0\\uF9C4");
|
||||
}
|
||||
src.push_str("\"");
|
||||
b.iter( || {
|
||||
let _ = Json::from_str(&src);
|
||||
});
|
||||
}
|
||||
|
||||
fn big_json() -> string::String {
|
||||
let mut src = "[\n".to_string();
|
||||
for _ in 0..500 {
|
||||
src.push_str(r#"{ "a": true, "b": null, "c":3.1415, "d": "Hello world", "e": \
|
||||
[1,2,3]},"#);
|
||||
}
|
||||
src.push_str("{}]");
|
||||
return src;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_streaming_large(b: &mut Bencher) {
|
||||
let src = big_json();
|
||||
b.iter( || {
|
||||
let mut parser = Parser::new(src.chars());
|
||||
loop {
|
||||
match parser.next() {
|
||||
None => return,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
#[bench]
|
||||
fn bench_large(b: &mut Bencher) {
|
||||
let src = big_json();
|
||||
b.iter( || { let _ = Json::from_str(&src); });
|
||||
}
|
|
@ -0,0 +1,489 @@
|
|||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// ignore-lexer-test FIXME #15679
|
||||
|
||||
//! Base64 binary-to-text encoding
|
||||
|
||||
pub use self::FromBase64Error::*;
|
||||
pub use self::CharacterSet::*;
|
||||
|
||||
use std::fmt;
|
||||
use std::error;
|
||||
|
||||
/// Available encoding character sets
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum CharacterSet {
|
||||
/// The standard character set (uses `+` and `/`)
|
||||
Standard,
|
||||
/// The URL safe character set (uses `-` and `_`)
|
||||
UrlSafe
|
||||
}
|
||||
|
||||
/// Available newline types
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Newline {
|
||||
/// A linefeed (i.e. Unix-style newline)
|
||||
LF,
|
||||
/// A carriage return and a linefeed (i.e. Windows-style newline)
|
||||
CRLF
|
||||
}
|
||||
|
||||
/// Contains configuration parameters for `to_base64`.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Config {
|
||||
/// Character set to use
|
||||
pub char_set: CharacterSet,
|
||||
/// Newline to use
|
||||
pub newline: Newline,
|
||||
/// True to pad output with `=` characters
|
||||
pub pad: bool,
|
||||
/// `Some(len)` to wrap lines at `len`, `None` to disable line wrapping
|
||||
pub line_length: Option<usize>
|
||||
}
|
||||
|
||||
/// Configuration for RFC 4648 standard base64 encoding
|
||||
pub static STANDARD: Config =
|
||||
Config {char_set: Standard, newline: Newline::CRLF, pad: true, line_length: None};
|
||||
|
||||
/// Configuration for RFC 4648 base64url encoding
|
||||
pub static URL_SAFE: Config =
|
||||
Config {char_set: UrlSafe, newline: Newline::CRLF, pad: false, line_length: None};
|
||||
|
||||
/// Configuration for RFC 2045 MIME base64 encoding
|
||||
pub static MIME: Config =
|
||||
Config {char_set: Standard, newline: Newline::CRLF, pad: true, line_length: Some(76)};
|
||||
|
||||
static STANDARD_CHARS: &'static[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
|
||||
abcdefghijklmnopqrstuvwxyz\
|
||||
0123456789+/";
|
||||
|
||||
static URLSAFE_CHARS: &'static[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
|
||||
abcdefghijklmnopqrstuvwxyz\
|
||||
0123456789-_";
|
||||
|
||||
/// A trait for converting a value to base64 encoding.
|
||||
pub trait ToBase64 {
|
||||
/// Converts the value of `self` to a base64 value following the specified
|
||||
/// format configuration, returning the owned string.
|
||||
fn to_base64(&self, config: Config) -> String;
|
||||
}
|
||||
|
||||
impl ToBase64 for [u8] {
|
||||
/// Turn a vector of `u8` bytes into a base64 string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate rustc_serialize;
|
||||
/// use rustc_serialize::base64::{ToBase64, STANDARD};
|
||||
///
|
||||
/// fn main () {
|
||||
/// let str = [52,32].to_base64(STANDARD);
|
||||
/// println!("base 64 output: {:?}", str);
|
||||
/// }
|
||||
/// ```
|
||||
fn to_base64(&self, config: Config) -> String {
|
||||
let bytes = match config.char_set {
|
||||
Standard => STANDARD_CHARS,
|
||||
UrlSafe => URLSAFE_CHARS
|
||||
};
|
||||
|
||||
let len = self.len();
|
||||
let newline = match config.newline {
|
||||
Newline::LF => "\n",
|
||||
Newline::CRLF => "\r\n",
|
||||
};
|
||||
|
||||
// Preallocate memory.
|
||||
let mut prealloc_len = (len + 2) / 3 * 4;
|
||||
if let Some(line_length) = config.line_length {
|
||||
let num_lines = match prealloc_len {
|
||||
0 => 0,
|
||||
n => (n - 1) / line_length
|
||||
};
|
||||
prealloc_len += num_lines * newline.bytes().count();
|
||||
}
|
||||
|
||||
let mut out_bytes = vec![b'='; prealloc_len];
|
||||
|
||||
// Deal with padding bytes
|
||||
let mod_len = len % 3;
|
||||
|
||||
// Use iterators to reduce branching
|
||||
{
|
||||
let mut cur_length = 0;
|
||||
|
||||
let mut s_in = self[..len - mod_len].iter().map(|&x| x as u32);
|
||||
let mut s_out = out_bytes.iter_mut();
|
||||
|
||||
// Convenient shorthand
|
||||
let enc = |val| bytes[val as usize];
|
||||
let mut write = |val| *s_out.next().unwrap() = val;
|
||||
|
||||
// Iterate though blocks of 4
|
||||
while let (Some(first), Some(second), Some(third)) =
|
||||
(s_in.next(), s_in.next(), s_in.next()) {
|
||||
|
||||
// Line break if needed
|
||||
if let Some(line_length) = config.line_length {
|
||||
if cur_length >= line_length {
|
||||
for b in newline.bytes() { write(b) };
|
||||
cur_length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let n = first << 16 | second << 8 | third;
|
||||
|
||||
// This 24-bit number gets separated into four 6-bit numbers.
|
||||
write(enc((n >> 18) & 63));
|
||||
write(enc((n >> 12) & 63));
|
||||
write(enc((n >> 6 ) & 63));
|
||||
write(enc((n >> 0 ) & 63));
|
||||
|
||||
cur_length += 4;
|
||||
}
|
||||
|
||||
// Line break only needed if padding is required
|
||||
if mod_len != 0 {
|
||||
if let Some(line_length) = config.line_length {
|
||||
if cur_length >= line_length {
|
||||
for b in newline.bytes() { write(b) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Heh, would be cool if we knew this was exhaustive
|
||||
// (the dream of bounded integer types)
|
||||
match mod_len {
|
||||
0 => (),
|
||||
1 => {
|
||||
let n = (self[len-1] as u32) << 16;
|
||||
write(enc((n >> 18) & 63));
|
||||
write(enc((n >> 12) & 63));
|
||||
}
|
||||
2 => {
|
||||
let n = (self[len-2] as u32) << 16 |
|
||||
(self[len-1] as u32) << 8;
|
||||
write(enc((n >> 18) & 63));
|
||||
write(enc((n >> 12) & 63));
|
||||
write(enc((n >> 6 ) & 63));
|
||||
}
|
||||
_ => panic!("Algebra is broken, please alert the math police")
|
||||
}
|
||||
}
|
||||
|
||||
// We get padding for "free", so only have to drop it if unwanted.
|
||||
if !config.pad {
|
||||
while let Some(&b'=') = out_bytes.last() {
|
||||
out_bytes.pop();
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { String::from_utf8_unchecked(out_bytes) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + ToBase64> ToBase64 for &'a T {
|
||||
fn to_base64(&self, config: Config) -> String {
|
||||
(**self).to_base64(config)
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for converting from base64 encoded values.
|
||||
pub trait FromBase64 {
|
||||
/// Converts the value of `self`, interpreted as base64 encoded data, into
|
||||
/// an owned vector of bytes, returning the vector.
|
||||
fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error>;
|
||||
}
|
||||
|
||||
/// Errors that can occur when decoding a base64 encoded string
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FromBase64Error {
|
||||
/// The input contained a character not part of the base64 format
|
||||
InvalidBase64Byte(u8, usize),
|
||||
/// The input had an invalid length
|
||||
InvalidBase64Length,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FromBase64Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
InvalidBase64Byte(ch, idx) =>
|
||||
write!(f, "Invalid character '{}' at position {}", ch, idx),
|
||||
InvalidBase64Length => write!(f, "Invalid length"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for FromBase64Error {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
InvalidBase64Byte(_, _) => "invalid character",
|
||||
InvalidBase64Length => "invalid length",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FromBase64Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBase64 for str {
|
||||
/// Convert any base64 encoded string (literal, `@`, `&`, or `~`)
|
||||
/// to the byte values it encodes.
|
||||
///
|
||||
/// You can use the `String::from_utf8` function to turn a `Vec<u8>` into a
|
||||
/// string with characters corresponding to those values.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// This converts a string literal to base64 and back.
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate rustc_serialize;
|
||||
/// use rustc_serialize::base64::{ToBase64, FromBase64, STANDARD};
|
||||
///
|
||||
/// fn main () {
|
||||
/// let hello_str = b"Hello, World".to_base64(STANDARD);
|
||||
/// println!("base64 output: {}", hello_str);
|
||||
/// let res = hello_str.from_base64();
|
||||
/// if res.is_ok() {
|
||||
/// let opt_bytes = String::from_utf8(res.unwrap());
|
||||
/// if opt_bytes.is_ok() {
|
||||
/// println!("decoded from base64: {:?}", opt_bytes.unwrap());
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
|
||||
self.as_bytes().from_base64()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromBase64 for [u8] {
|
||||
fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
|
||||
let mut r = Vec::with_capacity(self.len());
|
||||
let mut buf: u32 = 0;
|
||||
let mut modulus = 0;
|
||||
|
||||
let mut it = self.iter();
|
||||
for byte in it.by_ref() {
|
||||
let code = DECODE_TABLE[*byte as usize];
|
||||
if code >= SPECIAL_CODES_START {
|
||||
match code {
|
||||
NEWLINE_CODE => continue,
|
||||
EQUALS_CODE => break,
|
||||
INVALID_CODE => return Err(InvalidBase64Byte(
|
||||
*byte, (byte as *const _ as usize) - self.as_ptr() as usize)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
buf = (buf | code as u32) << 6;
|
||||
modulus += 1;
|
||||
if modulus == 4 {
|
||||
modulus = 0;
|
||||
r.push((buf >> 22) as u8);
|
||||
r.push((buf >> 14) as u8);
|
||||
r.push((buf >> 6 ) as u8);
|
||||
}
|
||||
}
|
||||
|
||||
for byte in it {
|
||||
match *byte {
|
||||
b'=' | b'\r' | b'\n' => continue,
|
||||
_ => return Err(InvalidBase64Byte(
|
||||
*byte, (byte as *const _ as usize) - self.as_ptr() as usize)),
|
||||
}
|
||||
}
|
||||
|
||||
match modulus {
|
||||
2 => {
|
||||
r.push((buf >> 10) as u8);
|
||||
}
|
||||
3 => {
|
||||
r.push((buf >> 16) as u8);
|
||||
r.push((buf >> 8 ) as u8);
|
||||
}
|
||||
0 => (),
|
||||
_ => return Err(InvalidBase64Length),
|
||||
}
|
||||
|
||||
Ok(r)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + FromBase64> FromBase64 for &'a T {
|
||||
fn from_base64(&self) -> Result<Vec<u8>, FromBase64Error> {
|
||||
(**self).from_base64()
|
||||
}
|
||||
}
|
||||
|
||||
/// Base64 decoding lookup table, generated using:
|
||||
///
|
||||
/// ```
|
||||
/// let mut ch = 0u8;
|
||||
/// for ch in 0..255 {
|
||||
/// let mut ch = ch as u8;
|
||||
/// let code = match ch {
|
||||
/// b'A'...b'Z' => ch - 0x41,
|
||||
/// b'a'...b'z' => ch - 0x47,
|
||||
/// b'0'...b'9' => ch + 0x04,
|
||||
/// b'+' | b'-' => 0x3E,
|
||||
/// b'/' | b'_' => 0x3F,
|
||||
/// b'=' => 0xFE,
|
||||
/// b'\r' | b'\n' => 0xFD,
|
||||
/// _ => 0xFF,
|
||||
/// };
|
||||
/// print!("0x{:02X}, ", code);
|
||||
/// if ch % 16 == 15 { println!(""); }
|
||||
/// else if ch == 0xFF { break; }
|
||||
/// ch += 1;
|
||||
/// }
|
||||
/// println!("");
|
||||
/// ```
|
||||
const DECODE_TABLE: [u8; 256] = [
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF, 0xFD, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0x3E, 0xFF, 0x3F,
|
||||
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF,
|
||||
0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
|
||||
0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0x3F,
|
||||
0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
||||
0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
];
|
||||
const INVALID_CODE: u8 = 0xFF;
|
||||
const EQUALS_CODE: u8 = 0xFE;
|
||||
const NEWLINE_CODE: u8 = 0xFD;
|
||||
const SPECIAL_CODES_START: u8 = NEWLINE_CODE;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use base64::{Config, Newline, FromBase64, ToBase64, STANDARD, URL_SAFE};
|
||||
|
||||
#[test]
|
||||
fn test_to_base64_basic() {
|
||||
assert_eq!("".as_bytes().to_base64(STANDARD), "");
|
||||
assert_eq!("f".as_bytes().to_base64(STANDARD), "Zg==");
|
||||
assert_eq!("fo".as_bytes().to_base64(STANDARD), "Zm8=");
|
||||
assert_eq!("foo".as_bytes().to_base64(STANDARD), "Zm9v");
|
||||
assert_eq!("foob".as_bytes().to_base64(STANDARD), "Zm9vYg==");
|
||||
assert_eq!("fooba".as_bytes().to_base64(STANDARD), "Zm9vYmE=");
|
||||
assert_eq!("foobar".as_bytes().to_base64(STANDARD), "Zm9vYmFy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_base64_crlf_line_break() {
|
||||
assert!(![0; 1000].to_base64(Config {line_length: None, ..STANDARD})
|
||||
.contains("\r\n"));
|
||||
assert_eq!(b"foobar".to_base64(Config {line_length: Some(4),
|
||||
..STANDARD}),
|
||||
"Zm9v\r\nYmFy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_base64_lf_line_break() {
|
||||
assert!(![0; 1000].to_base64(Config {line_length: None,
|
||||
newline: Newline::LF,
|
||||
..STANDARD})
|
||||
.contains("\n"));
|
||||
assert_eq!(b"foobar".to_base64(Config {line_length: Some(4),
|
||||
newline: Newline::LF,
|
||||
..STANDARD}),
|
||||
"Zm9v\nYmFy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_base64_padding() {
|
||||
assert_eq!("f".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zg");
|
||||
assert_eq!("fo".as_bytes().to_base64(Config {pad: false, ..STANDARD}), "Zm8");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_base64_url_safe() {
|
||||
assert_eq!([251, 255].to_base64(URL_SAFE), "-_8");
|
||||
assert_eq!([251, 255].to_base64(STANDARD), "+/8=");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_to_base64_empty_line_length() {
|
||||
[].to_base64(Config {line_length: Some(72), ..STANDARD});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_base64_basic() {
|
||||
assert_eq!("".from_base64().unwrap(), b"");
|
||||
assert_eq!("Zg==".from_base64().unwrap(), b"f");
|
||||
assert_eq!("Zm8=".from_base64().unwrap(), b"fo");
|
||||
assert_eq!("Zm9v".from_base64().unwrap(), b"foo");
|
||||
assert_eq!("Zm9vYg==".from_base64().unwrap(), b"foob");
|
||||
assert_eq!("Zm9vYmE=".from_base64().unwrap(), b"fooba");
|
||||
assert_eq!("Zm9vYmFy".from_base64().unwrap(), b"foobar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_base64_bytes() {
|
||||
assert_eq!(b"Zm9vYmFy".from_base64().unwrap(), b"foobar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_base64_newlines() {
|
||||
assert_eq!("Zm9v\r\nYmFy".from_base64().unwrap(),
|
||||
b"foobar");
|
||||
assert_eq!("Zm9vYg==\r\n".from_base64().unwrap(),
|
||||
b"foob");
|
||||
assert_eq!("Zm9v\nYmFy".from_base64().unwrap(),
|
||||
b"foobar");
|
||||
assert_eq!("Zm9vYg==\n".from_base64().unwrap(),
|
||||
b"foob");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_base64_urlsafe() {
|
||||
assert_eq!("-_8".from_base64().unwrap(), "+/8=".from_base64().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_base64_invalid_char() {
|
||||
assert!("Zm$=".from_base64().is_err());
|
||||
assert!("Zg==$".from_base64().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_base64_invalid_padding() {
|
||||
assert!("Z===".from_base64().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_base64_random() {
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
for _ in 0..1000 {
|
||||
let times = thread_rng().gen_range(1, 100);
|
||||
let v = thread_rng().gen_iter::<u8>().take(times)
|
||||
.collect::<Vec<_>>();
|
||||
assert_eq!(v.to_base64(STANDARD)
|
||||
.from_base64()
|
||||
.unwrap(),
|
||||
v);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Implementations of serialization for structures found in libcollections
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use {Decodable, Encodable, Decoder, Encoder, cap_capacity};
|
||||
use std::collections::{LinkedList, VecDeque, BTreeMap, BTreeSet, HashMap, HashSet};
|
||||
|
||||
impl<
|
||||
T: Encodable
|
||||
> Encodable for LinkedList<T> {
|
||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
s.emit_seq(self.len(), |s| {
|
||||
for (i, e) in self.iter().enumerate() {
|
||||
try!(s.emit_seq_elt(i, |s| e.encode(s)));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Decodable> Decodable for LinkedList<T> {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<LinkedList<T>, D::Error> {
|
||||
d.read_seq(|d, len| {
|
||||
let mut list = LinkedList::new();
|
||||
for i in 0..len {
|
||||
list.push_back(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
|
||||
}
|
||||
Ok(list)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Encodable> Encodable for VecDeque<T> {
|
||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
s.emit_seq(self.len(), |s| {
|
||||
for (i, e) in self.iter().enumerate() {
|
||||
try!(s.emit_seq_elt(i, |s| e.encode(s)));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T:Decodable> Decodable for VecDeque<T> {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<VecDeque<T>, D::Error> {
|
||||
d.read_seq(|d, len| {
|
||||
let mut deque: VecDeque<T> = VecDeque::new();
|
||||
for i in 0..len {
|
||||
deque.push_back(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
|
||||
}
|
||||
Ok(deque)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
K: Encodable + Ord,
|
||||
V: Encodable
|
||||
> Encodable for BTreeMap<K, V> {
|
||||
fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
|
||||
e.emit_map(self.len(), |e| {
|
||||
let mut i = 0;
|
||||
for (key, val) in self.iter() {
|
||||
try!(e.emit_map_elt_key(i, |e| key.encode(e)));
|
||||
try!(e.emit_map_elt_val(i, |e| val.encode(e)));
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
K: Decodable + Ord,
|
||||
V: Decodable
|
||||
> Decodable for BTreeMap<K, V> {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<BTreeMap<K, V>, D::Error> {
|
||||
d.read_map(|d, len| {
|
||||
let mut map = BTreeMap::new();
|
||||
for i in 0..len {
|
||||
let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
|
||||
let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
|
||||
map.insert(key, val);
|
||||
}
|
||||
Ok(map)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
T: Encodable + Ord
|
||||
> Encodable for BTreeSet<T> {
|
||||
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||
s.emit_seq(self.len(), |s| {
|
||||
let mut i = 0;
|
||||
for e in self.iter() {
|
||||
try!(s.emit_seq_elt(i, |s| e.encode(s)));
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
T: Decodable + Ord
|
||||
> Decodable for BTreeSet<T> {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<BTreeSet<T>, D::Error> {
|
||||
d.read_seq(|d, len| {
|
||||
let mut set = BTreeSet::new();
|
||||
for i in 0..len {
|
||||
set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
|
||||
}
|
||||
Ok(set)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Encodable for HashMap<K, V>
|
||||
where K: Encodable + Hash + Eq,
|
||||
V: Encodable,
|
||||
{
|
||||
fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
|
||||
e.emit_map(self.len(), |e| {
|
||||
let mut i = 0;
|
||||
for (key, val) in self.iter() {
|
||||
try!(e.emit_map_elt_key(i, |e| key.encode(e)));
|
||||
try!(e.emit_map_elt_val(i, |e| val.encode(e)));
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> Decodable for HashMap<K, V>
|
||||
where K: Decodable + Hash + Eq,
|
||||
V: Decodable,
|
||||
{
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<HashMap<K, V>, D::Error> {
|
||||
d.read_map(|d, len| {
|
||||
let mut map = HashMap::with_capacity(cap_capacity::<(K, V)>(len));
|
||||
for i in 0..len {
|
||||
let key = try!(d.read_map_elt_key(i, |d| Decodable::decode(d)));
|
||||
let val = try!(d.read_map_elt_val(i, |d| Decodable::decode(d)));
|
||||
map.insert(key, val);
|
||||
}
|
||||
Ok(map)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Encodable for HashSet<T> where T: Encodable + Hash + Eq {
|
||||
fn encode<E: Encoder>(&self, s: &mut E) -> Result<(), E::Error> {
|
||||
s.emit_seq(self.len(), |s| {
|
||||
let mut i = 0;
|
||||
for e in self.iter() {
|
||||
try!(s.emit_seq_elt(i, |s| e.encode(s)));
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Decodable for HashSet<T> where T: Decodable + Hash + Eq, {
|
||||
fn decode<D: Decoder>(d: &mut D) -> Result<HashSet<T>, D::Error> {
|
||||
d.read_seq(|d, len| {
|
||||
let mut set = HashSet::with_capacity(cap_capacity::<T>(len));
|
||||
for i in 0..len {
|
||||
set.insert(try!(d.read_seq_elt(i, |d| Decodable::decode(d))));
|
||||
}
|
||||
Ok(set)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,221 @@
|
|||
// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
//
|
||||
// ignore-lexer-test FIXME #15679
|
||||
|
||||
//! Hex binary-to-text encoding
|
||||
|
||||
pub use self::FromHexError::*;
|
||||
|
||||
use std::fmt;
|
||||
use std::error;
|
||||
|
||||
/// A trait for converting a value to hexadecimal encoding
|
||||
pub trait ToHex {
|
||||
/// Converts the value of `self` to a hex value, returning the owned
|
||||
/// string.
|
||||
fn to_hex(&self) -> String;
|
||||
}
|
||||
|
||||
static CHARS: &'static[u8] = b"0123456789abcdef";
|
||||
|
||||
impl ToHex for [u8] {
|
||||
/// Turn a vector of `u8` bytes into a hexadecimal string.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate rustc_serialize;
|
||||
/// use rustc_serialize::hex::ToHex;
|
||||
///
|
||||
/// fn main () {
|
||||
/// let str = [52,32].to_hex();
|
||||
/// println!("{}", str);
|
||||
/// }
|
||||
/// ```
|
||||
fn to_hex(&self) -> String {
|
||||
let mut v = Vec::with_capacity(self.len() * 2);
|
||||
for &byte in self.iter() {
|
||||
v.push(CHARS[(byte >> 4) as usize]);
|
||||
v.push(CHARS[(byte & 0xf) as usize]);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
String::from_utf8_unchecked(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + ToHex> ToHex for &'a T {
|
||||
fn to_hex(&self) -> String {
|
||||
(**self).to_hex()
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for converting hexadecimal encoded values
|
||||
pub trait FromHex {
|
||||
/// Converts the value of `self`, interpreted as hexadecimal encoded data,
|
||||
/// into an owned vector of bytes, returning the vector.
|
||||
fn from_hex(&self) -> Result<Vec<u8>, FromHexError>;
|
||||
}
|
||||
|
||||
/// Errors that can occur when decoding a hex encoded string
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum FromHexError {
|
||||
/// The input contained a character not part of the hex format
|
||||
InvalidHexCharacter(char, usize),
|
||||
/// The input had an invalid length
|
||||
InvalidHexLength,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FromHexError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
InvalidHexCharacter(ch, idx) =>
|
||||
write!(f, "Invalid character '{}' at position {}", ch, idx),
|
||||
InvalidHexLength => write!(f, "Invalid input length"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for FromHexError {
|
||||
fn description(&self) -> &str {
|
||||
match *self {
|
||||
InvalidHexCharacter(_, _) => "invalid character",
|
||||
InvalidHexLength => "invalid length",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FromHexError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromHex for str {
|
||||
/// Convert any hexadecimal encoded string (literal, `@`, `&`, or `~`)
|
||||
/// to the byte values it encodes.
|
||||
///
|
||||
/// You can use the `String::from_utf8` function to turn a
|
||||
/// `Vec<u8>` into a string with characters corresponding to those values.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// This converts a string literal to hexadecimal and back.
|
||||
///
|
||||
/// ```rust
|
||||
/// extern crate rustc_serialize;
|
||||
/// use rustc_serialize::hex::{FromHex, ToHex};
|
||||
///
|
||||
/// fn main () {
|
||||
/// let hello_str = "Hello, World".as_bytes().to_hex();
|
||||
/// println!("{}", hello_str);
|
||||
/// let bytes = hello_str.from_hex().unwrap();
|
||||
/// println!("{:?}", bytes);
|
||||
/// let result_str = String::from_utf8(bytes).unwrap();
|
||||
/// println!("{}", result_str);
|
||||
/// }
|
||||
/// ```
|
||||
fn from_hex(&self) -> Result<Vec<u8>, FromHexError> {
|
||||
// This may be an overestimate if there is any whitespace
|
||||
let mut b = Vec::with_capacity(self.len() / 2);
|
||||
let mut modulus = 0;
|
||||
let mut buf = 0;
|
||||
|
||||
for (idx, byte) in self.bytes().enumerate() {
|
||||
buf <<= 4;
|
||||
|
||||
match byte {
|
||||
b'A'...b'F' => buf |= byte - b'A' + 10,
|
||||
b'a'...b'f' => buf |= byte - b'a' + 10,
|
||||
b'0'...b'9' => buf |= byte - b'0',
|
||||
b' '|b'\r'|b'\n'|b'\t' => {
|
||||
buf >>= 4;
|
||||
continue
|
||||
}
|
||||
_ => {
|
||||
let ch = self[idx..].chars().next().unwrap();
|
||||
return Err(InvalidHexCharacter(ch, idx))
|
||||
}
|
||||
}
|
||||
|
||||
modulus += 1;
|
||||
if modulus == 2 {
|
||||
modulus = 0;
|
||||
b.push(buf);
|
||||
}
|
||||
}
|
||||
|
||||
match modulus {
|
||||
0 => Ok(b.into_iter().collect()),
|
||||
_ => Err(InvalidHexLength),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + FromHex> FromHex for &'a T {
|
||||
fn from_hex(&self) -> Result<Vec<u8>, FromHexError> {
|
||||
(**self).from_hex()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex::{FromHex, ToHex};
|
||||
|
||||
#[test]
|
||||
pub fn test_to_hex() {
|
||||
assert_eq!("foobar".as_bytes().to_hex(), "666f6f626172");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_from_hex_okay() {
|
||||
assert_eq!("666f6f626172".from_hex().unwrap(),
|
||||
b"foobar");
|
||||
assert_eq!("666F6F626172".from_hex().unwrap(),
|
||||
b"foobar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_from_hex_odd_len() {
|
||||
assert!("666".from_hex().is_err());
|
||||
assert!("66 6".from_hex().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_from_hex_invalid_char() {
|
||||
assert!("66y6".from_hex().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_from_hex_ignores_whitespace() {
|
||||
assert_eq!("666f 6f6\r\n26172 ".from_hex().unwrap(),
|
||||
b"foobar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_to_hex_all_bytes() {
|
||||
for i in 0..256 {
|
||||
assert_eq!([i as u8].to_hex(), format!("{:02x}", i));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_from_hex_all_bytes() {
|
||||
for i in 0..256 {
|
||||
let ii: &[u8] = &[i as u8];
|
||||
assert_eq!(format!("{:02x}", i).from_hex().unwrap(),
|
||||
ii);
|
||||
assert_eq!(format!("{:02X}", i).from_hex().unwrap(),
|
||||
ii);
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Support code for encoding and decoding types.
|
||||
//!
|
||||
//! > **NOTE**: This crate is deprecated in favor of [`serde`]. No new feature
|
||||
//! > development will happen in this crate, although bug fixes proposed through
|
||||
//! > PRs will still be merged. It is very highly recommended by the Rust
|
||||
//! > Library Team that you use [`serde`], not this crate.
|
||||
//!
|
||||
//! [`serde`]: https://serde.rs
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! This crate is [on crates.io](https://crates.io/crates/rustc-serialize) and
|
||||
//! can be used by adding `rustc-serialize` to the dependencies in your
|
||||
//! project's `Cargo.toml`.
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! rustc-serialize = "0.3"
|
||||
//! ```
|
||||
//!
|
||||
//! and this to your crate root:
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate rustc_serialize;
|
||||
//! ```
|
||||
|
||||
#![cfg_attr(rustbuild, feature(staged_api, rustc_private))]
|
||||
#![cfg_attr(rustbuild, unstable(feature = "rustc_private", issue = "27812"))]
|
||||
|
||||
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
|
||||
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
|
||||
html_root_url = "https://doc.rust-lang.org/rustc-serialize/")]
|
||||
#![cfg_attr(test, deny(warnings))]
|
||||
#![allow(trivial_numeric_casts)]
|
||||
#![cfg_attr(rust_build, feature(staged_api))]
|
||||
#![cfg_attr(rust_build, staged_api)]
|
||||
#![cfg_attr(rust_build,
|
||||
unstable(feature = "rustc_private",
|
||||
reason = "use the crates.io `rustc-serialize` library instead"))]
|
||||
|
||||
#[cfg(test)] extern crate rand;
|
||||
|
||||
pub use self::serialize::{Decoder, Encoder, Decodable, Encodable,
|
||||
DecoderHelpers, EncoderHelpers};
|
||||
|
||||
|
||||
// Limit collections from allocating more than
|
||||
// 1 MB for calls to `with_capacity`.
|
||||
fn cap_capacity<T>(given_len: usize) -> usize {
|
||||
use std::cmp::min;
|
||||
use std::mem::size_of;
|
||||
const PRE_ALLOCATE_CAP: usize = 0x100000;
|
||||
|
||||
match size_of::<T>() {
|
||||
0 => min(given_len, PRE_ALLOCATE_CAP),
|
||||
n => min(given_len, PRE_ALLOCATE_CAP / n)
|
||||
}
|
||||
}
|
||||
|
||||
mod serialize;
|
||||
mod collection_impls;
|
||||
|
||||
pub mod base64;
|
||||
pub mod hex;
|
||||
pub mod json;
|
||||
|
||||
mod rustc_serialize {
|
||||
pub use serialize::*;
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1 +0,0 @@
|
|||
{"files":{".travis.yml":"f73b83dfd8c84e2af27d780e93441a071507c8c2d981a73844558fe72717448e","Cargo.toml":"a97493cb1e3f170d30a5f8d92ae65185103083baf408eb5c0bd8f64a7ffdf007","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","README.md":"d150ab564033cacab1e6e806b13e78bb241818e950ffa4bf2623e6bd3bc64fb3","benchmark/benchmark.rs":"5ee2d5f68f6fa93f24e3828c9e8e269c22ce3ea96c0804def61b0b3026ad021f","build.rs":"96e9308d3a7a23a4c222892be8f29f45b13bffaed751f2e5b0b247230b65f360","src/buffer/mod.rs":"bcdc3db1c97b36c04c4445c4a7fba768be7a8b16e0f8bc7bab86ac06256a9750","src/common.rs":"f165881b718867b7801044bd29fa7f4322ebfc63d45d462d4a703253f8812a3e","src/d2s.rs":"35e0982e83d8c46f4304148d0454957b62be5f17480ad417d893521d578ad86e","src/d2s_full_table.rs":"7f7e475c54ae69d834574603bde9dcbe9f0d7cb09cfc3cda025319c903996bf8","src/d2s_small_table.rs":"f1bc5b328be636f100315ba818545e7d3c125cacec8487ed12f59f50d70b945e","src/digit_table.rs":"02351ca54cb8cb3679f635115dd094f32fd91750e9f66103c1ee9ec3db507072","src/f2s.rs":"2a59cc3ea57244ef4e926d98284a33363fb67452e20f24141395312173698e32","src/lib.rs":"268d75e445101f1edc0ced93852941ab9b150f1faea642490a03e8a613a33589","src/mulshift128.rs":"b539411c08b7cde489b876addc346a061e3594ed707fe577a3cdff6d26b0e1f8","src/pretty/exponent.rs":"15fd163fdb81573d331f24fda37f5403931512ffb08715a2695f0a0256b69b84","src/pretty/mantissa.rs":"7b0ea97069ee597f3bc0c8f2f3354c75be93d01c6a8135104ae82cd83df318e0","src/pretty/mod.rs":"f691267e66ce6f13d6db17ca6b93065e50a669e73916cbd22c8bfb3e33ad85fe","tests/d2s_table_test.rs":"57b541dc08c54c83979f18a20f8895daac3ad32628b140c66b09f42bce7f2691","tests/d2s_test.rs":"9abea253a30d96815688a34f20e2b3eff40c9d8b9a1209d7e927ba5a63efd74a","tests/exhaustive.rs":"0e01491936cb6b5ae68d92e50b6f7cebb26362774e860df103695ecc1f71fa7b","tests/f2s_test.rs":"7fa9dd515ed42947f570243a6d0656b6e2861c1399f679d96317dc109018d59b","tests/macros/mod.rs":"45eed20e9a3d8d9b673f504e8194f762223346adec46a6fbf1e0717eaeee85bc"},"package":"fd0568787116e13c652377b6846f5931454a363a8fdf8ae50463ee40935b278b"}
|
|
@ -1,13 +0,0 @@
|
|||
language: rust
|
||||
|
||||
rust:
|
||||
- nightly
|
||||
- beta
|
||||
- stable
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- rust: 1.15.0
|
||||
script:
|
||||
- cargo build
|
||||
- cargo build --features small
|
|
@ -1,39 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "ryu"
|
||||
version = "0.2.4"
|
||||
authors = ["David Tolnay <dtolnay@gmail.com>"]
|
||||
build = "build.rs"
|
||||
description = "Fast floating point to string conversion"
|
||||
documentation = "https://docs.rs/ryu"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/dtolnay/ryu"
|
||||
|
||||
[[example]]
|
||||
name = "benchmark"
|
||||
path = "benchmark/benchmark.rs"
|
||||
[dependencies.no-panic]
|
||||
version = "0.1"
|
||||
optional = true
|
||||
[dev-dependencies.num_cpus]
|
||||
version = "1.8"
|
||||
|
||||
[dev-dependencies.rand]
|
||||
version = "0.5"
|
||||
|
||||
[features]
|
||||
small = []
|
||||
[badges.travis-ci]
|
||||
repository = "dtolnay/ryu"
|
|
@ -1,201 +0,0 @@
|
|||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -1,74 +0,0 @@
|
|||
# Ryū
|
||||
|
||||
[![Build Status](https://api.travis-ci.org/dtolnay/ryu.svg?branch=master)](https://travis-ci.org/dtolnay/ryu)
|
||||
[![Latest Version](https://img.shields.io/crates/v/ryu.svg)](https://crates.io/crates/ryu)
|
||||
[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/ryu)
|
||||
[![Rustc Version 1.15+](https://img.shields.io/badge/rustc-1.15+-lightgray.svg)](https://blog.rust-lang.org/2017/02/02/Rust-1.15.html)
|
||||
|
||||
Pure Rust implementation of Ryū, an algorithm to quickly convert floating point
|
||||
numbers to decimal strings.
|
||||
|
||||
The PLDI'18 paper [*Ryū: fast float-to-string conversion*][paper] by Ulf Adams
|
||||
includes a complete correctness proof of the algorithm. The paper is available
|
||||
under the creative commons CC-BY-SA license.
|
||||
|
||||
This Rust implementation is a line-by-line port of Ulf Adams' implementation in
|
||||
C, [https://github.com/ulfjack/ryu][upstream]. The `ryu::raw` module exposes
|
||||
exactly the API and formatting of the C implementation as unsafe pure Rust
|
||||
functions. There is additionally a safe API as demonstrated in the example code
|
||||
below. The safe API uses the same underlying Ryū algorithm but diverges from the
|
||||
formatting of the C implementation to produce more human-readable output, for
|
||||
example `0.3` rather than `3E-1`.
|
||||
|
||||
*Requirements: this crate supports any compiler version back to rustc 1.15; it
|
||||
uses nothing from the Rust standard library so is usable from no_std crates.*
|
||||
|
||||
[paper]: https://dl.acm.org/citation.cfm?id=3192369
|
||||
[upstream]: https://github.com/ulfjack/ryu/tree/4ffc2b759e4b0a431b35dbfbfd6e0e85fdd15a69
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
ryu = "0.2"
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
```rust
|
||||
extern crate ryu;
|
||||
|
||||
fn main() {
|
||||
let mut buffer = ryu::Buffer::new();
|
||||
let printed = buffer.format(1.234);
|
||||
assert_eq!(printed, "1.234");
|
||||
}
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
You can run upstream's benchmarks with:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/ulfjack/ryu c-ryu
|
||||
$ cd c-ryu
|
||||
$ bazel run -c opt //ryu/benchmark
|
||||
```
|
||||
|
||||
And our benchmarks with:
|
||||
|
||||
```console
|
||||
$ git clone https://github.com/ulfjack/ryu rust-ryu
|
||||
$ cd rust-ryu
|
||||
$ cargo run --example benchmark --release
|
||||
```
|
||||
|
||||
These benchmarks measure the average time to print a 32-bit float and average
|
||||
time to print a 64-bit float, where the inputs are distributed as uniform random
|
||||
bit patterns 32 and 64 bits wide.
|
||||
|
||||
The upstream C code, the unsafe direct Rust port, and the safe pretty Rust API
|
||||
all perform the same, taking around 21 nanoseconds to format a 32-bit float and
|
||||
31 nanoseconds to format a 64-bit float.
|
||||
|
||||
## License
|
||||
|
||||
Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
|
@ -1,86 +0,0 @@
|
|||
extern crate rand;
|
||||
extern crate ryu;
|
||||
|
||||
use rand::{Rng, SeedableRng};
|
||||
|
||||
const SAMPLES: usize = 10000;
|
||||
const ITERATIONS: usize = 1000;
|
||||
|
||||
struct MeanAndVariance {
|
||||
n: i64,
|
||||
mean: f64,
|
||||
m2: f64,
|
||||
}
|
||||
|
||||
impl MeanAndVariance {
|
||||
fn new() -> Self {
|
||||
MeanAndVariance {
|
||||
n: 0,
|
||||
mean: 0.0,
|
||||
m2: 0.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, x: f64) {
|
||||
self.n += 1;
|
||||
let d = x - self.mean;
|
||||
self.mean += d / self.n as f64;
|
||||
let d2 = x - self.mean;
|
||||
self.m2 += d * d2;
|
||||
}
|
||||
|
||||
fn variance(&self) -> f64 {
|
||||
self.m2 / (self.n - 1) as f64
|
||||
}
|
||||
|
||||
fn stddev(&self) -> f64 {
|
||||
self.variance().sqrt()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! benchmark {
|
||||
($name:ident, $ty:ident) => {
|
||||
fn $name() -> usize {
|
||||
let mut rng = rand::prng::XorShiftRng::from_seed([123u8; 16]);
|
||||
let mut mv = MeanAndVariance::new();
|
||||
let mut throwaway = 0;
|
||||
for _ in 0..SAMPLES {
|
||||
let f = loop {
|
||||
let f = $ty::from_bits(rng.gen());
|
||||
if f.is_finite() {
|
||||
break f;
|
||||
}
|
||||
};
|
||||
|
||||
let t1 = std::time::SystemTime::now();
|
||||
for _ in 0..ITERATIONS {
|
||||
throwaway += ryu::Buffer::new().format(f).len();
|
||||
}
|
||||
let duration = t1.elapsed().unwrap();
|
||||
let nanos = duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64;
|
||||
mv.update(nanos as f64 / ITERATIONS as f64);
|
||||
}
|
||||
println!(
|
||||
"{:12} {:8.3} {:8.3}",
|
||||
concat!(stringify!($name), ":"),
|
||||
mv.mean,
|
||||
mv.stddev(),
|
||||
);
|
||||
throwaway
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
benchmark!(pretty32, f32);
|
||||
benchmark!(pretty64, f64);
|
||||
|
||||
fn main() {
|
||||
println!("{:>20}{:>9}", "Average", "Stddev");
|
||||
let mut throwaway = 0;
|
||||
throwaway += pretty32();
|
||||
throwaway += pretty64();
|
||||
if std::env::var_os("ryu-benchmark").is_some() {
|
||||
// Prevent the compiler from optimizing the code away.
|
||||
println!("{}", throwaway);
|
||||
}
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
use std::env;
|
||||
use std::process::Command;
|
||||
use std::str::{self, FromStr};
|
||||
|
||||
// The rustc-cfg strings below are *not* public API. Please let us know by
|
||||
// opening a GitHub issue if your build environment requires some way to enable
|
||||
// these cfgs other than by executing our build script.
|
||||
fn main() {
|
||||
let minor = match rustc_minor_version() {
|
||||
Some(minor) => minor,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// 128-bit integers stabilized in Rust 1.26:
|
||||
// https://blog.rust-lang.org/2018/05/10/Rust-1.26.html
|
||||
if minor >= 26 {
|
||||
println!("cargo:rustc-cfg=integer128");
|
||||
}
|
||||
|
||||
// #[must_use] on functions stabilized in Rust 1.27:
|
||||
// https://blog.rust-lang.org/2018/06/21/Rust-1.27.html
|
||||
if minor >= 27 {
|
||||
println!("cargo:rustc-cfg=must_use_return");
|
||||
}
|
||||
}
|
||||
|
||||
fn rustc_minor_version() -> Option<u32> {
|
||||
let rustc = match env::var_os("RUSTC") {
|
||||
Some(rustc) => rustc,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
let output = match Command::new(rustc).arg("--version").output() {
|
||||
Ok(output) => output,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let version = match str::from_utf8(&output.stdout) {
|
||||
Ok(version) => version,
|
||||
Err(_) => return None,
|
||||
};
|
||||
|
||||
let mut pieces = version.split('.');
|
||||
if pieces.next() != Some("rustc 1") {
|
||||
return None;
|
||||
}
|
||||
|
||||
let next = match pieces.next() {
|
||||
Some(next) => next,
|
||||
None => return None,
|
||||
};
|
||||
|
||||
u32::from_str(next).ok()
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
use core::{mem, slice, str};
|
||||
|
||||
use pretty;
|
||||
|
||||
#[cfg(feature = "no-panic")]
|
||||
use no_panic::no_panic;
|
||||
|
||||
/// Safe API for formatting floating point numbers to text.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut buffer = ryu::Buffer::new();
|
||||
/// let printed = buffer.format(1.234);
|
||||
/// assert_eq!(printed, "1.234");
|
||||
/// ```
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Buffer {
|
||||
bytes: [u8; 24],
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
/// This is a cheap operation; you don't need to worry about reusing buffers
|
||||
/// for efficiency.
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
pub fn new() -> Self {
|
||||
Buffer {
|
||||
bytes: unsafe { mem::uninitialized() },
|
||||
}
|
||||
}
|
||||
|
||||
/// Print a floating point number into this buffer and return a reference to
|
||||
/// its string representation within the buffer.
|
||||
///
|
||||
/// # Special cases
|
||||
///
|
||||
/// This function **does not** check for NaN or infinity. If the input
|
||||
/// number is not a finite float, the printed representation will be some
|
||||
/// correctly formatted but unspecified numerical value.
|
||||
///
|
||||
/// Please check [`is_finite`] yourself before calling this function, or
|
||||
/// check [`is_nan`] and [`is_infinite`] and handle those cases yourself.
|
||||
///
|
||||
/// [`is_finite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_finite
|
||||
/// [`is_nan`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_nan
|
||||
/// [`is_infinite`]: https://doc.rust-lang.org/std/primitive.f64.html#method.is_infinite
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
pub fn format<F: Float>(&mut self, f: F) -> &str {
|
||||
unsafe {
|
||||
let n = f.write_to_ryu_buffer(&mut self.bytes[0]);
|
||||
debug_assert!(n <= self.bytes.len());
|
||||
let slice = slice::from_raw_parts(&self.bytes[0], n);
|
||||
str::from_utf8_unchecked(slice)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Buffer {
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
fn default() -> Self {
|
||||
Buffer::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// A floating point number, f32 or f64, that can be written into a
|
||||
/// [`ryu::Buffer`][Buffer].
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented for types outside of the
|
||||
/// `ryu` crate.
|
||||
pub trait Float: Sealed {
|
||||
// Not public API.
|
||||
#[doc(hidden)]
|
||||
unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize;
|
||||
}
|
||||
|
||||
impl Float for f32 {
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
|
||||
pretty::f2s_buffered_n(self, result)
|
||||
}
|
||||
}
|
||||
|
||||
impl Float for f64 {
|
||||
#[inline]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
unsafe fn write_to_ryu_buffer(self, result: *mut u8) -> usize {
|
||||
pretty::d2s_buffered_n(self, result)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Sealed {}
|
||||
impl Sealed for f32 {}
|
||||
impl Sealed for f64 {}
|
|
@ -1,72 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
use core::ptr;
|
||||
|
||||
// Returns e == 0 ? 1 : ceil(log_2(5^e)).
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn pow5bits(e: i32) -> u32 {
|
||||
// This approximation works up to the point that the multiplication overflows at e = 3529.
|
||||
// If the multiplication were done in 64 bits, it would fail at 5^4004 which is just greater
|
||||
// than 2^9297.
|
||||
debug_assert!(e >= 0);
|
||||
debug_assert!(e <= 3528);
|
||||
((e as u32 * 1217359) >> 19) + 1
|
||||
}
|
||||
|
||||
// Returns floor(log_10(2^e)).
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn log10_pow2(e: i32) -> i32 {
|
||||
// The first value this approximation fails for is 2^1651 which is just greater than 10^297.
|
||||
debug_assert!(e >= 0);
|
||||
debug_assert!(e <= 1650);
|
||||
((e as u32 * 78913) >> 18) as i32
|
||||
}
|
||||
|
||||
// Returns floor(log_10(5^e)).
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn log10_pow5(e: i32) -> i32 {
|
||||
// The first value this approximation fails for is 5^2621 which is just greater than 10^1832.
|
||||
debug_assert!(e >= 0);
|
||||
debug_assert!(e <= 2620);
|
||||
((e as u32 * 732923) >> 20) as i32
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn copy_special_str(
|
||||
result: *mut u8,
|
||||
sign: bool,
|
||||
exponent: bool,
|
||||
mantissa: bool,
|
||||
) -> usize {
|
||||
if mantissa {
|
||||
ptr::copy_nonoverlapping(b"NaN".as_ptr(), result, 3);
|
||||
return 3;
|
||||
}
|
||||
if sign {
|
||||
*result = b'-';
|
||||
}
|
||||
if exponent {
|
||||
ptr::copy_nonoverlapping(b"Infinity".as_ptr(), result.offset(sign as isize), 8);
|
||||
return sign as usize + 8;
|
||||
}
|
||||
ptr::copy_nonoverlapping(b"0E0".as_ptr(), result.offset(sign as isize), 3);
|
||||
sign as usize + 3
|
||||
}
|
|
@ -1,553 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
use core::{mem, ptr};
|
||||
|
||||
use common::*;
|
||||
#[cfg(not(feature = "small"))]
|
||||
use d2s_full_table::*;
|
||||
#[cfg(feature = "small")]
|
||||
use d2s_small_table::*;
|
||||
use digit_table::*;
|
||||
#[cfg(not(integer128))]
|
||||
use mulshift128::*;
|
||||
|
||||
#[cfg(feature = "no-panic")]
|
||||
use no_panic::no_panic;
|
||||
|
||||
pub const DOUBLE_MANTISSA_BITS: u32 = 52;
|
||||
pub const DOUBLE_EXPONENT_BITS: u32 = 11;
|
||||
|
||||
const DOUBLE_POW5_INV_BITCOUNT: i32 = 122;
|
||||
const DOUBLE_POW5_BITCOUNT: i32 = 121;
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn pow5_factor(mut value: u64) -> u32 {
|
||||
let mut count = 0u32;
|
||||
loop {
|
||||
debug_assert!(value != 0);
|
||||
let q = value / 5;
|
||||
let r = value % 5;
|
||||
if r != 0 {
|
||||
break;
|
||||
}
|
||||
value = q;
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
// Returns true if value is divisible by 5^p.
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn multiple_of_power_of_5(value: u64, p: u32) -> bool {
|
||||
// I tried a case distinction on p, but there was no performance difference.
|
||||
pow5_factor(value) >= p
|
||||
}
|
||||
|
||||
// Returns true if value is divisible by 2^p.
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn multiple_of_power_of_2(value: u64, p: u32) -> bool {
|
||||
// return __builtin_ctzll(value) >= p;
|
||||
(value & ((1u64 << p) - 1)) == 0
|
||||
}
|
||||
|
||||
#[cfg(integer128)]
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn mul_shift(m: u64, mul: &(u64, u64), j: u32) -> u64 {
|
||||
let b0 = m as u128 * mul.0 as u128;
|
||||
let b2 = m as u128 * mul.1 as u128;
|
||||
(((b0 >> 64) + b2) >> (j - 64)) as u64
|
||||
}
|
||||
|
||||
#[cfg(integer128)]
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn mul_shift_all(
|
||||
m: u64,
|
||||
mul: &(u64, u64),
|
||||
j: u32,
|
||||
vp: &mut u64,
|
||||
vm: &mut u64,
|
||||
mm_shift: u32,
|
||||
) -> u64 {
|
||||
*vp = mul_shift(4 * m + 2, mul, j);
|
||||
*vm = mul_shift(4 * m - 1 - mm_shift as u64, mul, j);
|
||||
mul_shift(4 * m, mul, j)
|
||||
}
|
||||
|
||||
#[cfg(not(integer128))]
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn mul_shift_all(
|
||||
mut m: u64,
|
||||
mul: &(u64, u64),
|
||||
j: u32,
|
||||
vp: &mut u64,
|
||||
vm: &mut u64,
|
||||
mm_shift: u32,
|
||||
) -> u64 {
|
||||
m <<= 1;
|
||||
// m is maximum 55 bits
|
||||
let (lo, tmp) = umul128(m, mul.0);
|
||||
let (mut mid, mut hi) = umul128(m, mul.1);
|
||||
mid = mid.wrapping_add(tmp);
|
||||
hi = hi.wrapping_add((mid < tmp) as u64); // overflow into hi
|
||||
|
||||
let lo2 = lo.wrapping_add(mul.0);
|
||||
let mid2 = mid.wrapping_add(mul.1).wrapping_add((lo2 < lo) as u64);
|
||||
let hi2 = hi.wrapping_add((mid2 < mid) as u64);
|
||||
*vp = shiftright128(mid2, hi2, j - 64 - 1);
|
||||
|
||||
if mm_shift == 1 {
|
||||
let lo3 = lo.wrapping_sub(mul.0);
|
||||
let mid3 = mid.wrapping_sub(mul.1).wrapping_sub((lo3 > lo) as u64);
|
||||
let hi3 = hi.wrapping_sub((mid3 > mid) as u64);
|
||||
*vm = shiftright128(mid3, hi3, j - 64 - 1);
|
||||
} else {
|
||||
let lo3 = lo + lo;
|
||||
let mid3 = mid.wrapping_add(mid).wrapping_add((lo3 < lo) as u64);
|
||||
let hi3 = hi.wrapping_add(hi).wrapping_add((mid3 < mid) as u64);
|
||||
let lo4 = lo3.wrapping_sub(mul.0);
|
||||
let mid4 = mid3.wrapping_sub(mul.1).wrapping_sub((lo4 > lo3) as u64);
|
||||
let hi4 = hi3.wrapping_sub((mid4 > mid3) as u64);
|
||||
*vm = shiftright128(mid4, hi4, j - 64);
|
||||
}
|
||||
|
||||
shiftright128(mid, hi, j - 64 - 1)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn decimal_length(v: u64) -> u32 {
|
||||
// This is slightly faster than a loop.
|
||||
// The average output length is 16.38 digits, so we check high-to-low.
|
||||
// Function precondition: v is not an 18, 19, or 20-digit number.
|
||||
// (17 digits are sufficient for round-tripping.)
|
||||
debug_assert!(v < 100000000000000000);
|
||||
|
||||
if v >= 10000000000000000 {
|
||||
17
|
||||
} else if v >= 1000000000000000 {
|
||||
16
|
||||
} else if v >= 100000000000000 {
|
||||
15
|
||||
} else if v >= 10000000000000 {
|
||||
14
|
||||
} else if v >= 1000000000000 {
|
||||
13
|
||||
} else if v >= 100000000000 {
|
||||
12
|
||||
} else if v >= 10000000000 {
|
||||
11
|
||||
} else if v >= 1000000000 {
|
||||
10
|
||||
} else if v >= 100000000 {
|
||||
9
|
||||
} else if v >= 10000000 {
|
||||
8
|
||||
} else if v >= 1000000 {
|
||||
7
|
||||
} else if v >= 100000 {
|
||||
6
|
||||
} else if v >= 10000 {
|
||||
5
|
||||
} else if v >= 1000 {
|
||||
4
|
||||
} else if v >= 100 {
|
||||
3
|
||||
} else if v >= 10 {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
// A floating decimal representing m * 10^e.
|
||||
pub struct FloatingDecimal64 {
|
||||
pub mantissa: u64,
|
||||
pub exponent: i32,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn d2d(ieee_mantissa: u64, ieee_exponent: u32) -> FloatingDecimal64 {
|
||||
let bias = (1u32 << (DOUBLE_EXPONENT_BITS - 1)) - 1;
|
||||
|
||||
let (e2, m2) = if ieee_exponent == 0 {
|
||||
(
|
||||
// We subtract 2 so that the bounds computation has 2 additional bits.
|
||||
1 - bias as i32 - DOUBLE_MANTISSA_BITS as i32 - 2,
|
||||
ieee_mantissa,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
ieee_exponent as i32 - bias as i32 - DOUBLE_MANTISSA_BITS as i32 - 2,
|
||||
(1u64 << DOUBLE_MANTISSA_BITS) | ieee_mantissa,
|
||||
)
|
||||
};
|
||||
let even = (m2 & 1) == 0;
|
||||
let accept_bounds = even;
|
||||
|
||||
// Step 2: Determine the interval of legal decimal representations.
|
||||
let mv = 4 * m2;
|
||||
// Implicit bool -> int conversion. True is 1, false is 0.
|
||||
let mm_shift = (ieee_mantissa != 0 || ieee_exponent <= 1) as u32;
|
||||
// We would compute mp and mm like this:
|
||||
// uint64_t mp = 4 * m2 + 2;
|
||||
// uint64_t mm = mv - 1 - mm_shift;
|
||||
|
||||
// Step 3: Convert to a decimal power base using 128-bit arithmetic.
|
||||
let mut vr: u64;
|
||||
let mut vp: u64 = unsafe { mem::uninitialized() };
|
||||
let mut vm: u64 = unsafe { mem::uninitialized() };
|
||||
let e10: i32;
|
||||
let mut vm_is_trailing_zeros = false;
|
||||
let mut vr_is_trailing_zeros = false;
|
||||
if e2 >= 0 {
|
||||
// I tried special-casing q == 0, but there was no effect on performance.
|
||||
// This expression is slightly faster than max(0, log10_pow2(e2) - 1).
|
||||
let q = (log10_pow2(e2) - (e2 > 3) as i32) as u32;
|
||||
e10 = q as i32;
|
||||
let k = DOUBLE_POW5_INV_BITCOUNT + pow5bits(q as i32) as i32 - 1;
|
||||
let i = -e2 + q as i32 + k;
|
||||
vr = mul_shift_all(
|
||||
m2,
|
||||
#[cfg(feature = "small")]
|
||||
unsafe {
|
||||
&compute_inv_pow5(q)
|
||||
},
|
||||
#[cfg(not(feature = "small"))]
|
||||
unsafe {
|
||||
debug_assert!(q < DOUBLE_POW5_INV_SPLIT.len() as u32);
|
||||
DOUBLE_POW5_INV_SPLIT.get_unchecked(q as usize)
|
||||
},
|
||||
i as u32,
|
||||
&mut vp,
|
||||
&mut vm,
|
||||
mm_shift,
|
||||
);
|
||||
if q <= 21 {
|
||||
// This should use q <= 22, but I think 21 is also safe. Smaller values
|
||||
// may still be safe, but it's more difficult to reason about them.
|
||||
// Only one of mp, mv, and mm can be a multiple of 5, if any.
|
||||
if mv % 5 == 0 {
|
||||
vr_is_trailing_zeros = multiple_of_power_of_5(mv, q);
|
||||
} else if accept_bounds {
|
||||
// Same as min(e2 + (~mm & 1), pow5_factor(mm)) >= q
|
||||
// <=> e2 + (~mm & 1) >= q && pow5_factor(mm) >= q
|
||||
// <=> true && pow5_factor(mm) >= q, since e2 >= q.
|
||||
vm_is_trailing_zeros = multiple_of_power_of_5(mv - 1 - mm_shift as u64, q);
|
||||
} else {
|
||||
// Same as min(e2 + 1, pow5_factor(mp)) >= q.
|
||||
vp -= multiple_of_power_of_5(mv + 2, q) as u64;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// This expression is slightly faster than max(0, log10_pow5(-e2) - 1).
|
||||
let q = (log10_pow5(-e2) - (-e2 > 1) as i32) as u32;
|
||||
e10 = q as i32 + e2;
|
||||
let i = -e2 - q as i32;
|
||||
let k = pow5bits(i) as i32 - DOUBLE_POW5_BITCOUNT;
|
||||
let j = q as i32 - k;
|
||||
vr = mul_shift_all(
|
||||
m2,
|
||||
#[cfg(feature = "small")]
|
||||
unsafe {
|
||||
&compute_pow5(i as u32)
|
||||
},
|
||||
#[cfg(not(feature = "small"))]
|
||||
unsafe {
|
||||
debug_assert!(i < DOUBLE_POW5_SPLIT.len() as i32);
|
||||
DOUBLE_POW5_SPLIT.get_unchecked(i as usize)
|
||||
},
|
||||
j as u32,
|
||||
&mut vp,
|
||||
&mut vm,
|
||||
mm_shift,
|
||||
);
|
||||
if q <= 1 {
|
||||
// {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
|
||||
// mv = 4 * m2, so it always has at least two trailing 0 bits.
|
||||
vr_is_trailing_zeros = true;
|
||||
if accept_bounds {
|
||||
// mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
|
||||
vm_is_trailing_zeros = mm_shift == 1;
|
||||
} else {
|
||||
// mp = mv + 2, so it always has at least one trailing 0 bit.
|
||||
vp -= 1;
|
||||
}
|
||||
} else if q < 63 {
|
||||
// TODO(ulfjack): Use a tighter bound here.
|
||||
// We need to compute min(ntz(mv), pow5_factor(mv) - e2) >= q - 1
|
||||
// <=> ntz(mv) >= q - 1 && pow5_factor(mv) - e2 >= q - 1
|
||||
// <=> ntz(mv) >= q - 1 (e2 is negative and -e2 >= q)
|
||||
// <=> (mv & ((1 << (q - 1)) - 1)) == 0
|
||||
// We also need to make sure that the left shift does not overflow.
|
||||
vr_is_trailing_zeros = multiple_of_power_of_2(mv, q - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Find the shortest decimal representation in the interval of legal representations.
|
||||
let mut removed = 0u32;
|
||||
let mut last_removed_digit = 0u8;
|
||||
// On average, we remove ~2 digits.
|
||||
let output = if vm_is_trailing_zeros || vr_is_trailing_zeros {
|
||||
// General case, which happens rarely (~0.7%).
|
||||
while vp / 10 > vm / 10 {
|
||||
vm_is_trailing_zeros &= vm - (vm / 10) * 10 == 0;
|
||||
vr_is_trailing_zeros &= last_removed_digit == 0;
|
||||
last_removed_digit = (vr % 10) as u8;
|
||||
vr /= 10;
|
||||
vp /= 10;
|
||||
vm /= 10;
|
||||
removed += 1;
|
||||
}
|
||||
if vm_is_trailing_zeros {
|
||||
while vm % 10 == 0 {
|
||||
vr_is_trailing_zeros &= last_removed_digit == 0;
|
||||
last_removed_digit = (vr % 10) as u8;
|
||||
vr /= 10;
|
||||
vp /= 10;
|
||||
vm /= 10;
|
||||
removed += 1;
|
||||
}
|
||||
}
|
||||
if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
|
||||
// Round even if the exact number is .....50..0.
|
||||
last_removed_digit = 4;
|
||||
}
|
||||
// We need to take vr + 1 if vr is outside bounds or we need to round up.
|
||||
vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5)
|
||||
as u64
|
||||
} else {
|
||||
// Specialized for the common case (~99.3%). Percentages below are relative to this.
|
||||
let mut round_up = false;
|
||||
// Optimization: remove two digits at a time (~86.2%).
|
||||
if vp / 100 > vm / 100 {
|
||||
round_up = vr % 100 >= 50;
|
||||
vr /= 100;
|
||||
vp /= 100;
|
||||
vm /= 100;
|
||||
removed += 2;
|
||||
}
|
||||
// Loop iterations below (approximately), without optimization above:
|
||||
// 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02%
|
||||
// Loop iterations below (approximately), with optimization above:
|
||||
// 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02%
|
||||
while vp / 10 > vm / 10 {
|
||||
round_up = vr % 10 >= 5;
|
||||
vr /= 10;
|
||||
vp /= 10;
|
||||
vm /= 10;
|
||||
removed += 1;
|
||||
}
|
||||
// We need to take vr + 1 if vr is outside bounds or we need to round up.
|
||||
vr + (vr == vm || round_up) as u64
|
||||
};
|
||||
let exp = e10 + removed as i32;
|
||||
|
||||
FloatingDecimal64 {
|
||||
exponent: exp,
|
||||
mantissa: output,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
unsafe fn to_chars(v: FloatingDecimal64, sign: bool, result: *mut u8) -> usize {
|
||||
// Step 5: Print the decimal representation.
|
||||
let mut index = 0isize;
|
||||
if sign {
|
||||
*result.offset(index) = b'-';
|
||||
index += 1;
|
||||
}
|
||||
|
||||
let mut output = v.mantissa;
|
||||
let olength = decimal_length(output);
|
||||
|
||||
// Print the decimal digits.
|
||||
// The following code is equivalent to:
|
||||
// for (uint32_t i = 0; i < olength - 1; ++i) {
|
||||
// const uint32_t c = output % 10; output /= 10;
|
||||
// result[index + olength - i] = (char) ('0' + c);
|
||||
// }
|
||||
// result[index] = '0' + output % 10;
|
||||
|
||||
let mut i = 0isize;
|
||||
// We prefer 32-bit operations, even on 64-bit platforms.
|
||||
// We have at most 17 digits, and uint32_t can store 9 digits.
|
||||
// If output doesn't fit into uint32_t, we cut off 8 digits,
|
||||
// so the rest will fit into uint32_t.
|
||||
if (output >> 32) != 0 {
|
||||
// Expensive 64-bit division.
|
||||
let mut output2 = (output - 100000000 * (output / 100000000)) as u32;
|
||||
output /= 100000000;
|
||||
|
||||
let c = output2 % 10000;
|
||||
output2 /= 10000;
|
||||
let d = output2 % 10000;
|
||||
let c0 = (c % 100) << 1;
|
||||
let c1 = (c / 100) << 1;
|
||||
let d0 = (d % 100) << 1;
|
||||
let d1 = (d / 100) << 1;
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(c0 as usize),
|
||||
result.offset(index + olength as isize - i - 1),
|
||||
2,
|
||||
);
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(c1 as usize),
|
||||
result.offset(index + olength as isize - i - 3),
|
||||
2,
|
||||
);
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(d0 as usize),
|
||||
result.offset(index + olength as isize - i - 5),
|
||||
2,
|
||||
);
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(d1 as usize),
|
||||
result.offset(index + olength as isize - i - 7),
|
||||
2,
|
||||
);
|
||||
i += 8;
|
||||
}
|
||||
let mut output2 = output as u32;
|
||||
while output2 >= 10000 {
|
||||
let c = (output2 - 10000 * (output2 / 10000)) as u32;
|
||||
output2 /= 10000;
|
||||
let c0 = (c % 100) << 1;
|
||||
let c1 = (c / 100) << 1;
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(c0 as usize),
|
||||
result.offset(index + olength as isize - i - 1),
|
||||
2,
|
||||
);
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(c1 as usize),
|
||||
result.offset(index + olength as isize - i - 3),
|
||||
2,
|
||||
);
|
||||
i += 4;
|
||||
}
|
||||
if output2 >= 100 {
|
||||
let c = ((output2 % 100) << 1) as u32;
|
||||
output2 /= 100;
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(c as usize),
|
||||
result.offset(index + olength as isize - i - 1),
|
||||
2,
|
||||
);
|
||||
i += 2;
|
||||
}
|
||||
if output2 >= 10 {
|
||||
let c = (output2 << 1) as u32;
|
||||
// We can't use memcpy here: the decimal dot goes between these two digits.
|
||||
*result.offset(index + olength as isize - i) = *DIGIT_TABLE.get_unchecked(c as usize + 1);
|
||||
*result.offset(index) = *DIGIT_TABLE.get_unchecked(c as usize);
|
||||
} else {
|
||||
*result.offset(index) = b'0' + output2 as u8;
|
||||
}
|
||||
|
||||
// Print decimal point if needed.
|
||||
if olength > 1 {
|
||||
*result.offset(index + 1) = b'.';
|
||||
index += olength as isize + 1;
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// Print the exponent.
|
||||
*result.offset(index) = b'E';
|
||||
index += 1;
|
||||
let mut exp = v.exponent as i32 + olength as i32 - 1;
|
||||
if exp < 0 {
|
||||
*result.offset(index) = b'-';
|
||||
index += 1;
|
||||
exp = -exp;
|
||||
}
|
||||
|
||||
if exp >= 100 {
|
||||
let c = exp % 10;
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked((2 * (exp / 10)) as usize),
|
||||
result.offset(index),
|
||||
2,
|
||||
);
|
||||
*result.offset(index + 2) = b'0' + c as u8;
|
||||
index += 3;
|
||||
} else if exp >= 10 {
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked((2 * exp) as usize),
|
||||
result.offset(index),
|
||||
2,
|
||||
);
|
||||
index += 2;
|
||||
} else {
|
||||
*result.offset(index) = b'0' + exp as u8;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
debug_assert!(index <= 24);
|
||||
index as usize
|
||||
}
|
||||
|
||||
/// Print f64 to the given buffer and return number of bytes written.
|
||||
///
|
||||
/// At most 24 bytes will be written.
|
||||
///
|
||||
/// ## Special cases
|
||||
///
|
||||
/// This function represents any NaN as `NaN`, positive infinity as `Infinity`,
|
||||
/// and negative infinity as `-Infinity`.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// The `result` pointer argument must point to sufficiently many writable bytes
|
||||
/// to hold Ryū's representation of `f`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let f = 1.234f64;
|
||||
///
|
||||
/// unsafe {
|
||||
/// let mut buffer: [u8; 24] = std::mem::uninitialized();
|
||||
/// let n = ryu::raw::d2s_buffered_n(f, &mut buffer[0]);
|
||||
/// let s = std::str::from_utf8_unchecked(&buffer[..n]);
|
||||
/// assert_eq!(s, "1.234E0");
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg_attr(must_use_return, must_use)]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
pub unsafe fn d2s_buffered_n(f: f64, result: *mut u8) -> usize {
|
||||
// Step 1: Decode the floating-point number, and unify normalized and subnormal cases.
|
||||
let bits = mem::transmute::<f64, u64>(f);
|
||||
|
||||
// Decode bits into sign, mantissa, and exponent.
|
||||
let ieee_sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0;
|
||||
let ieee_mantissa = bits & ((1u64 << DOUBLE_MANTISSA_BITS) - 1);
|
||||
let ieee_exponent =
|
||||
(bits >> DOUBLE_MANTISSA_BITS) as u32 & ((1u32 << DOUBLE_EXPONENT_BITS) - 1);
|
||||
// Case distinction; exit early for the easy cases.
|
||||
if ieee_exponent == ((1u32 << DOUBLE_EXPONENT_BITS) - 1)
|
||||
|| (ieee_exponent == 0 && ieee_mantissa == 0)
|
||||
{
|
||||
return copy_special_str(result, ieee_sign, ieee_exponent != 0, ieee_mantissa != 0);
|
||||
}
|
||||
|
||||
let v = d2d(ieee_mantissa, ieee_exponent);
|
||||
to_chars(v, ieee_sign, result)
|
||||
}
|
|
@ -1,643 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
pub static DOUBLE_POW5_INV_SPLIT: [(u64, u64); 292] = [
|
||||
(1, 288230376151711744),
|
||||
(3689348814741910324, 230584300921369395),
|
||||
(2951479051793528259, 184467440737095516),
|
||||
(17118578500402463900, 147573952589676412),
|
||||
(12632330341676300947, 236118324143482260),
|
||||
(10105864273341040758, 188894659314785808),
|
||||
(15463389048156653253, 151115727451828646),
|
||||
(17362724847566824558, 241785163922925834),
|
||||
(17579528692795369969, 193428131138340667),
|
||||
(6684925324752475329, 154742504910672534),
|
||||
(18074578149087781173, 247588007857076054),
|
||||
(18149011334012135262, 198070406285660843),
|
||||
(3451162622983977240, 158456325028528675),
|
||||
(5521860196774363583, 253530120045645880),
|
||||
(4417488157419490867, 202824096036516704),
|
||||
(7223339340677503017, 162259276829213363),
|
||||
(7867994130342094503, 259614842926741381),
|
||||
(2605046489531765280, 207691874341393105),
|
||||
(2084037191625412224, 166153499473114484),
|
||||
(10713157136084480204, 265845599156983174),
|
||||
(12259874523609494487, 212676479325586539),
|
||||
(13497248433629505913, 170141183460469231),
|
||||
(14216899864323388813, 272225893536750770),
|
||||
(11373519891458711051, 217780714829400616),
|
||||
(5409467098425058518, 174224571863520493),
|
||||
(4965798542738183305, 278759314981632789),
|
||||
(7661987648932456967, 223007451985306231),
|
||||
(2440241304404055250, 178405961588244985),
|
||||
(3904386087046488400, 285449538541191976),
|
||||
(17880904128604832013, 228359630832953580),
|
||||
(14304723302883865611, 182687704666362864),
|
||||
(15133127457049002812, 146150163733090291),
|
||||
(16834306301794583852, 233840261972944466),
|
||||
(9778096226693756759, 187072209578355573),
|
||||
(15201174610838826053, 149657767662684458),
|
||||
(2185786488890659746, 239452428260295134),
|
||||
(5437978005854438120, 191561942608236107),
|
||||
(15418428848909281466, 153249554086588885),
|
||||
(6222742084545298729, 245199286538542217),
|
||||
(16046240111861969953, 196159429230833773),
|
||||
(1768945645263844993, 156927543384667019),
|
||||
(10209010661905972635, 251084069415467230),
|
||||
(8167208529524778108, 200867255532373784),
|
||||
(10223115638361732810, 160693804425899027),
|
||||
(1599589762411131202, 257110087081438444),
|
||||
(4969020624670815285, 205688069665150755),
|
||||
(3975216499736652228, 164550455732120604),
|
||||
(13739044029062464211, 263280729171392966),
|
||||
(7301886408508061046, 210624583337114373),
|
||||
(13220206756290269483, 168499666669691498),
|
||||
(17462981995322520850, 269599466671506397),
|
||||
(6591687966774196033, 215679573337205118),
|
||||
(12652048002903177473, 172543658669764094),
|
||||
(9175230360419352987, 276069853871622551),
|
||||
(3650835473593572067, 220855883097298041),
|
||||
(17678063637842498946, 176684706477838432),
|
||||
(13527506561580357021, 282695530364541492),
|
||||
(3443307619780464970, 226156424291633194),
|
||||
(6443994910566282300, 180925139433306555),
|
||||
(5155195928453025840, 144740111546645244),
|
||||
(15627011115008661990, 231584178474632390),
|
||||
(12501608892006929592, 185267342779705912),
|
||||
(2622589484121723027, 148213874223764730),
|
||||
(4196143174594756843, 237142198758023568),
|
||||
(10735612169159626121, 189713759006418854),
|
||||
(12277838550069611220, 151771007205135083),
|
||||
(15955192865369467629, 242833611528216133),
|
||||
(1696107848069843133, 194266889222572907),
|
||||
(12424932722681605476, 155413511378058325),
|
||||
(1433148282581017146, 248661618204893321),
|
||||
(15903913885032455010, 198929294563914656),
|
||||
(9033782293284053685, 159143435651131725),
|
||||
(14454051669254485895, 254629497041810760),
|
||||
(11563241335403588716, 203703597633448608),
|
||||
(16629290697806691620, 162962878106758886),
|
||||
(781423413297334329, 260740604970814219),
|
||||
(4314487545379777786, 208592483976651375),
|
||||
(3451590036303822229, 166873987181321100),
|
||||
(5522544058086115566, 266998379490113760),
|
||||
(4418035246468892453, 213598703592091008),
|
||||
(10913125826658934609, 170878962873672806),
|
||||
(10082303693170474728, 273406340597876490),
|
||||
(8065842954536379782, 218725072478301192),
|
||||
(17520720807854834795, 174980057982640953),
|
||||
(5897060404116273733, 279968092772225526),
|
||||
(1028299508551108663, 223974474217780421),
|
||||
(15580034865808528224, 179179579374224336),
|
||||
(17549358155809824511, 286687326998758938),
|
||||
(2971440080422128639, 229349861599007151),
|
||||
(17134547323305344204, 183479889279205720),
|
||||
(13707637858644275364, 146783911423364576),
|
||||
(14553522944347019935, 234854258277383322),
|
||||
(4264120725993795302, 187883406621906658),
|
||||
(10789994210278856888, 150306725297525326),
|
||||
(9885293106962350374, 240490760476040522),
|
||||
(529536856086059653, 192392608380832418),
|
||||
(7802327114352668369, 153914086704665934),
|
||||
(1415676938738538420, 246262538727465495),
|
||||
(1132541550990830736, 197010030981972396),
|
||||
(15663428499760305882, 157608024785577916),
|
||||
(17682787970132668764, 252172839656924666),
|
||||
(10456881561364224688, 201738271725539733),
|
||||
(15744202878575200397, 161390617380431786),
|
||||
(17812026976236499989, 258224987808690858),
|
||||
(3181575136763469022, 206579990246952687),
|
||||
(13613306553636506187, 165263992197562149),
|
||||
(10713244041592678929, 264422387516099439),
|
||||
(12259944048016053467, 211537910012879551),
|
||||
(6118606423670932450, 169230328010303641),
|
||||
(2411072648389671274, 270768524816485826),
|
||||
(16686253377679378312, 216614819853188660),
|
||||
(13349002702143502650, 173291855882550928),
|
||||
(17669055508687693916, 277266969412081485),
|
||||
(14135244406950155133, 221813575529665188),
|
||||
(240149081334393137, 177450860423732151),
|
||||
(11452284974360759988, 283921376677971441),
|
||||
(5472479164746697667, 227137101342377153),
|
||||
(11756680961281178780, 181709681073901722),
|
||||
(2026647139541122378, 145367744859121378),
|
||||
(18000030682233437097, 232588391774594204),
|
||||
(18089373360528660001, 186070713419675363),
|
||||
(3403452244197197031, 148856570735740291),
|
||||
(16513570034941246220, 238170513177184465),
|
||||
(13210856027952996976, 190536410541747572),
|
||||
(3189987192878576934, 152429128433398058),
|
||||
(1414630693863812771, 243886605493436893),
|
||||
(8510402184574870864, 195109284394749514),
|
||||
(10497670562401807014, 156087427515799611),
|
||||
(9417575270359070576, 249739884025279378),
|
||||
(14912757845771077107, 199791907220223502),
|
||||
(4551508647133041040, 159833525776178802),
|
||||
(10971762650154775986, 255733641241886083),
|
||||
(16156107749607641435, 204586912993508866),
|
||||
(9235537384944202825, 163669530394807093),
|
||||
(11087511001168814197, 261871248631691349),
|
||||
(12559357615676961681, 209496998905353079),
|
||||
(13736834907283479668, 167597599124282463),
|
||||
(18289587036911657145, 268156158598851941),
|
||||
(10942320814787415393, 214524926879081553),
|
||||
(16132554281313752961, 171619941503265242),
|
||||
(11054691591134363444, 274591906405224388),
|
||||
(16222450902391311402, 219673525124179510),
|
||||
(12977960721913049122, 175738820099343608),
|
||||
(17075388340318968271, 281182112158949773),
|
||||
(2592264228029443648, 224945689727159819),
|
||||
(5763160197165465241, 179956551781727855),
|
||||
(9221056315464744386, 287930482850764568),
|
||||
(14755542681855616155, 230344386280611654),
|
||||
(15493782960226403247, 184275509024489323),
|
||||
(1326979923955391628, 147420407219591459),
|
||||
(9501865507812447252, 235872651551346334),
|
||||
(11290841220991868125, 188698121241077067),
|
||||
(1653975347309673853, 150958496992861654),
|
||||
(10025058185179298811, 241533595188578646),
|
||||
(4330697733401528726, 193226876150862917),
|
||||
(14532604630946953951, 154581500920690333),
|
||||
(1116074521063664381, 247330401473104534),
|
||||
(4582208431592841828, 197864321178483627),
|
||||
(14733813189500004432, 158291456942786901),
|
||||
(16195403473716186445, 253266331108459042),
|
||||
(5577625149489128510, 202613064886767234),
|
||||
(8151448934333213131, 162090451909413787),
|
||||
(16731667109675051333, 259344723055062059),
|
||||
(17074682502481951390, 207475778444049647),
|
||||
(6281048372501740465, 165980622755239718),
|
||||
(6360328581260874421, 265568996408383549),
|
||||
(8777611679750609860, 212455197126706839),
|
||||
(10711438158542398211, 169964157701365471),
|
||||
(9759603424184016492, 271942652322184754),
|
||||
(11497031554089123517, 217554121857747803),
|
||||
(16576322872755119460, 174043297486198242),
|
||||
(11764721337440549842, 278469275977917188),
|
||||
(16790474699436260520, 222775420782333750),
|
||||
(13432379759549008416, 178220336625867000),
|
||||
(3045063541568861850, 285152538601387201),
|
||||
(17193446092222730773, 228122030881109760),
|
||||
(13754756873778184618, 182497624704887808),
|
||||
(18382503128506368341, 145998099763910246),
|
||||
(3586563302416817083, 233596959622256395),
|
||||
(2869250641933453667, 186877567697805116),
|
||||
(17052795772514404226, 149502054158244092),
|
||||
(12527077977055405469, 239203286653190548),
|
||||
(17400360011128145022, 191362629322552438),
|
||||
(2852241564676785048, 153090103458041951),
|
||||
(15631632947708587046, 244944165532867121),
|
||||
(8815957543424959314, 195955332426293697),
|
||||
(18120812478965698421, 156764265941034957),
|
||||
(14235904707377476180, 250822825505655932),
|
||||
(4010026136418160298, 200658260404524746),
|
||||
(17965416168102169531, 160526608323619796),
|
||||
(2919224165770098987, 256842573317791675),
|
||||
(2335379332616079190, 205474058654233340),
|
||||
(1868303466092863352, 164379246923386672),
|
||||
(6678634360490491686, 263006795077418675),
|
||||
(5342907488392393349, 210405436061934940),
|
||||
(4274325990713914679, 168324348849547952),
|
||||
(10528270399884173809, 269318958159276723),
|
||||
(15801313949391159694, 215455166527421378),
|
||||
(1573004715287196786, 172364133221937103),
|
||||
(17274202803427156150, 275782613155099364),
|
||||
(17508711057483635243, 220626090524079491),
|
||||
(10317620031244997871, 176500872419263593),
|
||||
(12818843235250086271, 282401395870821749),
|
||||
(13944423402941979340, 225921116696657399),
|
||||
(14844887537095493795, 180736893357325919),
|
||||
(15565258844418305359, 144589514685860735),
|
||||
(6457670077359736959, 231343223497377177),
|
||||
(16234182506113520537, 185074578797901741),
|
||||
(9297997190148906106, 148059663038321393),
|
||||
(11187446689496339446, 236895460861314229),
|
||||
(12639306166338981880, 189516368689051383),
|
||||
(17490142562555006151, 151613094951241106),
|
||||
(2158786396894637579, 242580951921985771),
|
||||
(16484424376483351356, 194064761537588616),
|
||||
(9498190686444770762, 155251809230070893),
|
||||
(11507756283569722895, 248402894768113429),
|
||||
(12895553841597688639, 198722315814490743),
|
||||
(17695140702761971558, 158977852651592594),
|
||||
(17244178680193423523, 254364564242548151),
|
||||
(10105994129412828495, 203491651394038521),
|
||||
(4395446488788352473, 162793321115230817),
|
||||
(10722063196803274280, 260469313784369307),
|
||||
(1198952927958798777, 208375451027495446),
|
||||
(15716557601334680315, 166700360821996356),
|
||||
(17767794532651667857, 266720577315194170),
|
||||
(14214235626121334286, 213376461852155336),
|
||||
(7682039686155157106, 170701169481724269),
|
||||
(1223217053622520399, 273121871170758831),
|
||||
(15735968901865657612, 218497496936607064),
|
||||
(16278123936234436413, 174797997549285651),
|
||||
(219556594781725998, 279676796078857043),
|
||||
(7554342905309201445, 223741436863085634),
|
||||
(9732823138989271479, 178993149490468507),
|
||||
(815121763415193074, 286389039184749612),
|
||||
(11720143854957885429, 229111231347799689),
|
||||
(13065463898708218666, 183288985078239751),
|
||||
(6763022304224664610, 146631188062591801),
|
||||
(3442138057275642729, 234609900900146882),
|
||||
(13821756890046245153, 187687920720117505),
|
||||
(11057405512036996122, 150150336576094004),
|
||||
(6623802375033462826, 240240538521750407),
|
||||
(16367088344252501231, 192192430817400325),
|
||||
(13093670675402000985, 153753944653920260),
|
||||
(2503129006933649959, 246006311446272417),
|
||||
(13070549649772650937, 196805049157017933),
|
||||
(17835137349301941396, 157444039325614346),
|
||||
(2710778055689733971, 251910462920982955),
|
||||
(2168622444551787177, 201528370336786364),
|
||||
(5424246770383340065, 161222696269429091),
|
||||
(1300097203129523457, 257956314031086546),
|
||||
(15797473021471260058, 206365051224869236),
|
||||
(8948629602435097724, 165092040979895389),
|
||||
(3249760919670425388, 264147265567832623),
|
||||
(9978506365220160957, 211317812454266098),
|
||||
(15361502721659949412, 169054249963412878),
|
||||
(2442311466204457120, 270486799941460606),
|
||||
(16711244431931206989, 216389439953168484),
|
||||
(17058344360286875914, 173111551962534787),
|
||||
(12535955717491360170, 276978483140055660),
|
||||
(10028764573993088136, 221582786512044528),
|
||||
(15401709288678291155, 177266229209635622),
|
||||
(9885339602917624555, 283625966735416996),
|
||||
(4218922867592189321, 226900773388333597),
|
||||
(14443184738299482427, 181520618710666877),
|
||||
(4175850161155765295, 145216494968533502),
|
||||
(10370709072591134795, 232346391949653603),
|
||||
(15675264887556728482, 185877113559722882),
|
||||
(5161514280561562140, 148701690847778306),
|
||||
(879725219414678777, 237922705356445290),
|
||||
(703780175531743021, 190338164285156232),
|
||||
(11631070584651125387, 152270531428124985),
|
||||
(162968861732249003, 243632850284999977),
|
||||
(11198421533611530172, 194906280227999981),
|
||||
(5269388412147313814, 155925024182399985),
|
||||
(8431021459435702103, 249480038691839976),
|
||||
(3055468352806651359, 199584030953471981),
|
||||
(17201769941212962380, 159667224762777584),
|
||||
(16454785461715008838, 255467559620444135),
|
||||
(13163828369372007071, 204374047696355308),
|
||||
(17909760324981426303, 163499238157084246),
|
||||
(2830174816776909822, 261598781051334795),
|
||||
(2264139853421527858, 209279024841067836),
|
||||
(16568707141704863579, 167423219872854268),
|
||||
(4373838538276319787, 267877151796566830),
|
||||
(3499070830621055830, 214301721437253464),
|
||||
(6488605479238754987, 171441377149802771),
|
||||
(3003071137298187333, 274306203439684434),
|
||||
(6091805724580460189, 219444962751747547),
|
||||
(15941491023890099121, 175555970201398037),
|
||||
(10748990379256517301, 280889552322236860),
|
||||
(8599192303405213841, 224711641857789488),
|
||||
(14258051472207991719, 179769313486231590),
|
||||
];
|
||||
|
||||
pub static DOUBLE_POW5_SPLIT: [(u64, u64); 326] = [
|
||||
(0, 72057594037927936),
|
||||
(0, 90071992547409920),
|
||||
(0, 112589990684262400),
|
||||
(0, 140737488355328000),
|
||||
(0, 87960930222080000),
|
||||
(0, 109951162777600000),
|
||||
(0, 137438953472000000),
|
||||
(0, 85899345920000000),
|
||||
(0, 107374182400000000),
|
||||
(0, 134217728000000000),
|
||||
(0, 83886080000000000),
|
||||
(0, 104857600000000000),
|
||||
(0, 131072000000000000),
|
||||
(0, 81920000000000000),
|
||||
(0, 102400000000000000),
|
||||
(0, 128000000000000000),
|
||||
(0, 80000000000000000),
|
||||
(0, 100000000000000000),
|
||||
(0, 125000000000000000),
|
||||
(0, 78125000000000000),
|
||||
(0, 97656250000000000),
|
||||
(0, 122070312500000000),
|
||||
(0, 76293945312500000),
|
||||
(0, 95367431640625000),
|
||||
(0, 119209289550781250),
|
||||
(4611686018427387904, 74505805969238281),
|
||||
(10376293541461622784, 93132257461547851),
|
||||
(8358680908399640576, 116415321826934814),
|
||||
(612489549322387456, 72759576141834259),
|
||||
(14600669991935148032, 90949470177292823),
|
||||
(13639151471491547136, 113686837721616029),
|
||||
(3213881284082270208, 142108547152020037),
|
||||
(4314518811765112832, 88817841970012523),
|
||||
(781462496279003136, 111022302462515654),
|
||||
(10200200157203529728, 138777878078144567),
|
||||
(13292654125893287936, 86736173798840354),
|
||||
(7392445620511834112, 108420217248550443),
|
||||
(4628871007212404736, 135525271560688054),
|
||||
(16728102434789916672, 84703294725430033),
|
||||
(7075069988205232128, 105879118406787542),
|
||||
(18067209522111315968, 132348898008484427),
|
||||
(8986162942105878528, 82718061255302767),
|
||||
(6621017659204960256, 103397576569128459),
|
||||
(3664586055578812416, 129246970711410574),
|
||||
(16125424340018921472, 80779356694631608),
|
||||
(1710036351314100224, 100974195868289511),
|
||||
(15972603494424788992, 126217744835361888),
|
||||
(9982877184015493120, 78886090522101180),
|
||||
(12478596480019366400, 98607613152626475),
|
||||
(10986559581596820096, 123259516440783094),
|
||||
(2254913720070624656, 77037197775489434),
|
||||
(12042014186943056628, 96296497219361792),
|
||||
(15052517733678820785, 120370621524202240),
|
||||
(9407823583549262990, 75231638452626400),
|
||||
(11759779479436578738, 94039548065783000),
|
||||
(14699724349295723422, 117549435082228750),
|
||||
(4575641699882439235, 73468396926392969),
|
||||
(10331238143280436948, 91835496157991211),
|
||||
(8302361660673158281, 114794370197489014),
|
||||
(1154580038986672043, 143492962746861268),
|
||||
(9944984561221445835, 89683101716788292),
|
||||
(12431230701526807293, 112103877145985365),
|
||||
(1703980321626345405, 140129846432481707),
|
||||
(17205888765512323542, 87581154020301066),
|
||||
(12283988920035628619, 109476442525376333),
|
||||
(1519928094762372062, 136845553156720417),
|
||||
(12479170105294952299, 85528470722950260),
|
||||
(15598962631618690374, 106910588403687825),
|
||||
(5663645234241199255, 133638235504609782),
|
||||
(17374836326682913246, 83523897190381113),
|
||||
(7883487353071477846, 104404871487976392),
|
||||
(9854359191339347308, 130506089359970490),
|
||||
(10770660513014479971, 81566305849981556),
|
||||
(13463325641268099964, 101957882312476945),
|
||||
(2994098996302961243, 127447352890596182),
|
||||
(15706369927971514489, 79654595556622613),
|
||||
(5797904354682229399, 99568244445778267),
|
||||
(2635694424925398845, 124460305557222834),
|
||||
(6258995034005762182, 77787690973264271),
|
||||
(3212057774079814824, 97234613716580339),
|
||||
(17850130272881932242, 121543267145725423),
|
||||
(18073860448192289507, 75964541966078389),
|
||||
(8757267504958198172, 94955677457597987),
|
||||
(6334898362770359811, 118694596821997484),
|
||||
(13182683513586250689, 74184123013748427),
|
||||
(11866668373555425458, 92730153767185534),
|
||||
(5609963430089506015, 115912692208981918),
|
||||
(17341285199088104971, 72445432630613698),
|
||||
(12453234462005355406, 90556790788267123),
|
||||
(10954857059079306353, 113195988485333904),
|
||||
(13693571323849132942, 141494985606667380),
|
||||
(17781854114260483896, 88434366004167112),
|
||||
(3780573569116053255, 110542957505208891),
|
||||
(114030942967678664, 138178696881511114),
|
||||
(4682955357782187069, 86361685550944446),
|
||||
(15077066234082509644, 107952106938680557),
|
||||
(5011274737320973344, 134940133673350697),
|
||||
(14661261756894078100, 84337583545844185),
|
||||
(4491519140835433913, 105421979432305232),
|
||||
(5614398926044292391, 131777474290381540),
|
||||
(12732371365632458552, 82360921431488462),
|
||||
(6692092170185797382, 102951151789360578),
|
||||
(17588487249587022536, 128688939736700722),
|
||||
(15604490549419276989, 80430587335437951),
|
||||
(14893927168346708332, 100538234169297439),
|
||||
(14005722942005997511, 125672792711621799),
|
||||
(15671105866394830300, 78545495444763624),
|
||||
(1142138259283986260, 98181869305954531),
|
||||
(15262730879387146537, 122727336632443163),
|
||||
(7233363790403272633, 76704585395276977),
|
||||
(13653390756431478696, 95880731744096221),
|
||||
(3231680390257184658, 119850914680120277),
|
||||
(4325643253124434363, 74906821675075173),
|
||||
(10018740084832930858, 93633527093843966),
|
||||
(3300053069186387764, 117041908867304958),
|
||||
(15897591223523656064, 73151193042065598),
|
||||
(10648616992549794273, 91438991302581998),
|
||||
(4087399203832467033, 114298739128227498),
|
||||
(14332621041645359599, 142873423910284372),
|
||||
(18181260187883125557, 89295889943927732),
|
||||
(4279831161144355331, 111619862429909666),
|
||||
(14573160988285219972, 139524828037387082),
|
||||
(13719911636105650386, 87203017523366926),
|
||||
(7926517508277287175, 109003771904208658),
|
||||
(684774848491833161, 136254714880260823),
|
||||
(7345513307948477581, 85159196800163014),
|
||||
(18405263671790372785, 106448996000203767),
|
||||
(18394893571310578077, 133061245000254709),
|
||||
(13802651491282805250, 83163278125159193),
|
||||
(3418256308821342851, 103954097656448992),
|
||||
(4272820386026678563, 129942622070561240),
|
||||
(2670512741266674102, 81214138794100775),
|
||||
(17173198981865506339, 101517673492625968),
|
||||
(3019754653622331308, 126897091865782461),
|
||||
(4193189667727651020, 79310682416114038),
|
||||
(14464859121514339583, 99138353020142547),
|
||||
(13469387883465536574, 123922941275178184),
|
||||
(8418367427165960359, 77451838296986365),
|
||||
(15134645302384838353, 96814797871232956),
|
||||
(471562554271496325, 121018497339041196),
|
||||
(9518098633274461011, 75636560836900747),
|
||||
(7285937273165688360, 94545701046125934),
|
||||
(18330793628311886258, 118182126307657417),
|
||||
(4539216990053847055, 73863828942285886),
|
||||
(14897393274422084627, 92329786177857357),
|
||||
(4786683537745442072, 115412232722321697),
|
||||
(14520892257159371055, 72132645451451060),
|
||||
(18151115321449213818, 90165806814313825),
|
||||
(8853836096529353561, 112707258517892282),
|
||||
(1843923083806916143, 140884073147365353),
|
||||
(12681666973447792349, 88052545717103345),
|
||||
(2017025661527576725, 110065682146379182),
|
||||
(11744654113764246714, 137582102682973977),
|
||||
(422879793461572340, 85988814176858736),
|
||||
(528599741826965425, 107486017721073420),
|
||||
(660749677283706782, 134357522151341775),
|
||||
(7330497575943398595, 83973451344588609),
|
||||
(13774807988356636147, 104966814180735761),
|
||||
(3383451930163631472, 131208517725919702),
|
||||
(15949715511634433382, 82005323578699813),
|
||||
(6102086334260878016, 102506654473374767),
|
||||
(3015921899398709616, 128133318091718459),
|
||||
(18025852251620051174, 80083323807324036),
|
||||
(4085571240815512351, 100104154759155046),
|
||||
(14330336087874166247, 125130193448943807),
|
||||
(15873989082562435760, 78206370905589879),
|
||||
(15230800334775656796, 97757963631987349),
|
||||
(5203442363187407284, 122197454539984187),
|
||||
(946308467778435600, 76373409087490117),
|
||||
(5794571603150432404, 95466761359362646),
|
||||
(16466586540792816313, 119333451699203307),
|
||||
(7985773578781816244, 74583407312002067),
|
||||
(5370530955049882401, 93229259140002584),
|
||||
(6713163693812353001, 116536573925003230),
|
||||
(18030785363914884337, 72835358703127018),
|
||||
(13315109668038829614, 91044198378908773),
|
||||
(2808829029766373305, 113805247973635967),
|
||||
(17346094342490130344, 142256559967044958),
|
||||
(6229622945628943561, 88910349979403099),
|
||||
(3175342663608791547, 111137937474253874),
|
||||
(13192550366365765242, 138922421842817342),
|
||||
(3633657960551215372, 86826513651760839),
|
||||
(18377130505971182927, 108533142064701048),
|
||||
(4524669058754427043, 135666427580876311),
|
||||
(9745447189362598758, 84791517238047694),
|
||||
(2958436949848472639, 105989396547559618),
|
||||
(12921418224165366607, 132486745684449522),
|
||||
(12687572408530742033, 82804216052780951),
|
||||
(11247779492236039638, 103505270065976189),
|
||||
(224666310012885835, 129381587582470237),
|
||||
(2446259452971747599, 80863492239043898),
|
||||
(12281196353069460307, 101079365298804872),
|
||||
(15351495441336825384, 126349206623506090),
|
||||
(14206370669262903769, 78968254139691306),
|
||||
(8534591299723853903, 98710317674614133),
|
||||
(15279925143082205283, 123387897093267666),
|
||||
(14161639232853766206, 77117435683292291),
|
||||
(13090363022639819853, 96396794604115364),
|
||||
(16362953778299774816, 120495993255144205),
|
||||
(12532689120651053212, 75309995784465128),
|
||||
(15665861400813816515, 94137494730581410),
|
||||
(10358954714162494836, 117671868413226763),
|
||||
(4168503687137865320, 73544917758266727),
|
||||
(598943590494943747, 91931147197833409),
|
||||
(5360365506546067587, 114913933997291761),
|
||||
(11312142901609972388, 143642417496614701),
|
||||
(9375932322719926695, 89776510935384188),
|
||||
(11719915403399908368, 112220638669230235),
|
||||
(10038208235822497557, 140275798336537794),
|
||||
(10885566165816448877, 87672373960336121),
|
||||
(18218643725697949000, 109590467450420151),
|
||||
(18161618638695048346, 136988084313025189),
|
||||
(13656854658398099168, 85617552695640743),
|
||||
(12459382304570236056, 107021940869550929),
|
||||
(1739169825430631358, 133777426086938662),
|
||||
(14922039196176308311, 83610891304336663),
|
||||
(14040862976792997485, 104513614130420829),
|
||||
(3716020665709083144, 130642017663026037),
|
||||
(4628355925281870917, 81651261039391273),
|
||||
(10397130925029726550, 102064076299239091),
|
||||
(8384727637859770284, 127580095374048864),
|
||||
(5240454773662356427, 79737559608780540),
|
||||
(6550568467077945534, 99671949510975675),
|
||||
(3576524565420044014, 124589936888719594),
|
||||
(6847013871814915412, 77868710555449746),
|
||||
(17782139376623420074, 97335888194312182),
|
||||
(13004302183924499284, 121669860242890228),
|
||||
(17351060901807587860, 76043662651806392),
|
||||
(3242082053549933210, 95054578314757991),
|
||||
(17887660622219580224, 118818222893447488),
|
||||
(11179787888887237640, 74261389308404680),
|
||||
(13974734861109047050, 92826736635505850),
|
||||
(8245046539531533005, 116033420794382313),
|
||||
(16682369133275677888, 72520887996488945),
|
||||
(7017903361312433648, 90651109995611182),
|
||||
(17995751238495317868, 113313887494513977),
|
||||
(8659630992836983623, 141642359368142472),
|
||||
(5412269370523114764, 88526474605089045),
|
||||
(11377022731581281359, 110658093256361306),
|
||||
(4997906377621825891, 138322616570451633),
|
||||
(14652906532082110942, 86451635356532270),
|
||||
(9092761128247862869, 108064544195665338),
|
||||
(2142579373455052779, 135080680244581673),
|
||||
(12868327154477877747, 84425425152863545),
|
||||
(2250350887815183471, 105531781441079432),
|
||||
(2812938609768979339, 131914726801349290),
|
||||
(6369772649532999991, 82446704250843306),
|
||||
(17185587848771025797, 103058380313554132),
|
||||
(3035240737254230630, 128822975391942666),
|
||||
(6508711479211282048, 80514359619964166),
|
||||
(17359261385868878368, 100642949524955207),
|
||||
(17087390713908710056, 125803686906194009),
|
||||
(3762090168551861929, 78627304316371256),
|
||||
(4702612710689827411, 98284130395464070),
|
||||
(15101637925217060072, 122855162994330087),
|
||||
(16356052730901744401, 76784476871456304),
|
||||
(1998321839917628885, 95980596089320381),
|
||||
(7109588318324424010, 119975745111650476),
|
||||
(13666864735807540814, 74984840694781547),
|
||||
(12471894901332038114, 93731050868476934),
|
||||
(6366496589810271835, 117163813585596168),
|
||||
(3979060368631419896, 73227383490997605),
|
||||
(9585511479216662775, 91534229363747006),
|
||||
(2758517312166052660, 114417786704683758),
|
||||
(12671518677062341634, 143022233380854697),
|
||||
(1002170145522881665, 89388895863034186),
|
||||
(10476084718758377889, 111736119828792732),
|
||||
(13095105898447972362, 139670149785990915),
|
||||
(5878598177316288774, 87293843616244322),
|
||||
(16571619758500136775, 109117304520305402),
|
||||
(11491152661270395161, 136396630650381753),
|
||||
(264441385652915120, 85247894156488596),
|
||||
(330551732066143900, 106559867695610745),
|
||||
(5024875683510067779, 133199834619513431),
|
||||
(10058076329834874218, 83249896637195894),
|
||||
(3349223375438816964, 104062370796494868),
|
||||
(4186529219298521205, 130077963495618585),
|
||||
(14145795808130045513, 81298727184761615),
|
||||
(13070558741735168987, 101623408980952019),
|
||||
(11726512408741573330, 127029261226190024),
|
||||
(7329070255463483331, 79393288266368765),
|
||||
(13773023837756742068, 99241610332960956),
|
||||
(17216279797195927585, 124052012916201195),
|
||||
(8454331864033760789, 77532508072625747),
|
||||
(5956228811614813082, 96915635090782184),
|
||||
(7445286014518516353, 121144543863477730),
|
||||
(9264989777501460624, 75715339914673581),
|
||||
(16192923240304213684, 94644174893341976),
|
||||
(1794409976670715490, 118305218616677471),
|
||||
(8039035263060279037, 73940761635423419),
|
||||
(5437108060397960892, 92425952044279274),
|
||||
(16019757112352226923, 115532440055349092),
|
||||
(788976158365366019, 72207775034593183),
|
||||
(14821278253238871236, 90259718793241478),
|
||||
(9303225779693813237, 112824648491551848),
|
||||
(11629032224617266546, 141030810614439810),
|
||||
(11879831158813179495, 88144256634024881),
|
||||
(1014730893234310657, 110180320792531102),
|
||||
(10491785653397664129, 137725400990663877),
|
||||
(8863209042587234033, 86078375619164923),
|
||||
(6467325284806654637, 107597969523956154),
|
||||
(17307528642863094104, 134497461904945192),
|
||||
(10817205401789433815, 84060913690590745),
|
||||
(18133192770664180173, 105076142113238431),
|
||||
(18054804944902837312, 131345177641548039),
|
||||
(18201782118205355176, 82090736025967524),
|
||||
(4305483574047142354, 102613420032459406),
|
||||
(14605226504413703751, 128266775040574257),
|
||||
(2210737537617482988, 80166734400358911),
|
||||
(16598479977304017447, 100208418000448638),
|
||||
(11524727934775246001, 125260522500560798),
|
||||
(2591268940807140847, 78287826562850499),
|
||||
(17074144231291089770, 97859783203563123),
|
||||
(16730994270686474309, 122324729004453904),
|
||||
(10456871419179046443, 76452955627783690),
|
||||
(3847717237119032246, 95566194534729613),
|
||||
(9421332564826178211, 119457743168412016),
|
||||
(5888332853016361382, 74661089480257510),
|
||||
(16583788103125227536, 93326361850321887),
|
||||
(16118049110479146516, 116657952312902359),
|
||||
(16991309721690548428, 72911220195563974),
|
||||
(12015765115258409727, 91139025244454968),
|
||||
(15019706394073012159, 113923781555568710),
|
||||
(9551260955736489391, 142404726944460888),
|
||||
(5969538097335305869, 89002954340288055),
|
||||
(2850236603241744433, 111253692925360069),
|
||||
];
|
|
@ -1,206 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
use common::*;
|
||||
#[cfg(not(integer128))]
|
||||
use mulshift128::*;
|
||||
|
||||
pub static DOUBLE_POW5_TABLE: [u64; 26] = [
|
||||
1,
|
||||
5,
|
||||
25,
|
||||
125,
|
||||
625,
|
||||
3125,
|
||||
15625,
|
||||
78125,
|
||||
390625,
|
||||
1953125,
|
||||
9765625,
|
||||
48828125,
|
||||
244140625,
|
||||
1220703125,
|
||||
6103515625,
|
||||
30517578125,
|
||||
152587890625,
|
||||
762939453125,
|
||||
3814697265625,
|
||||
19073486328125,
|
||||
95367431640625,
|
||||
476837158203125,
|
||||
2384185791015625,
|
||||
11920928955078125,
|
||||
59604644775390625,
|
||||
298023223876953125,
|
||||
];
|
||||
|
||||
pub static DOUBLE_POW5_SPLIT2: [(u64, u64); 13] = [
|
||||
(0, 72057594037927936),
|
||||
(10376293541461622784, 93132257461547851),
|
||||
(15052517733678820785, 120370621524202240),
|
||||
(6258995034005762182, 77787690973264271),
|
||||
(14893927168346708332, 100538234169297439),
|
||||
(4272820386026678563, 129942622070561240),
|
||||
(7330497575943398595, 83973451344588609),
|
||||
(18377130505971182927, 108533142064701048),
|
||||
(10038208235822497557, 140275798336537794),
|
||||
(7017903361312433648, 90651109995611182),
|
||||
(6366496589810271835, 117163813585596168),
|
||||
(9264989777501460624, 75715339914673581),
|
||||
(17074144231291089770, 97859783203563123),
|
||||
];
|
||||
|
||||
// Unfortunately, the results are sometimes off by one. We use an additional
|
||||
// lookup table to store those cases and adjust the result.
|
||||
pub static POW5_OFFSETS: [u32; 13] = [
|
||||
0x00000000, 0x00000000, 0x00000000, 0x033c55be, 0x03db77d8, 0x0265ffb2, 0x00000800, 0x01a8ff56,
|
||||
0x00000000, 0x0037a200, 0x00004000, 0x03fffffc, 0x00003ffe,
|
||||
];
|
||||
|
||||
pub static DOUBLE_POW5_INV_SPLIT2: [(u64, u64); 13] = [
|
||||
(1, 288230376151711744),
|
||||
(7661987648932456967, 223007451985306231),
|
||||
(12652048002903177473, 172543658669764094),
|
||||
(5522544058086115566, 266998379490113760),
|
||||
(3181575136763469022, 206579990246952687),
|
||||
(4551508647133041040, 159833525776178802),
|
||||
(1116074521063664381, 247330401473104534),
|
||||
(17400360011128145022, 191362629322552438),
|
||||
(9297997190148906106, 148059663038321393),
|
||||
(11720143854957885429, 229111231347799689),
|
||||
(15401709288678291155, 177266229209635622),
|
||||
(3003071137298187333, 274306203439684434),
|
||||
(17516772882021341108, 212234145163966538),
|
||||
];
|
||||
|
||||
pub static POW5_INV_OFFSETS: [u32; 20] = [
|
||||
0x51505404, 0x55054514, 0x45555545, 0x05511411, 0x00505010, 0x00000004, 0x00000000, 0x00000000,
|
||||
0x55555040, 0x00505051, 0x00050040, 0x55554000, 0x51659559, 0x00001000, 0x15000010, 0x55455555,
|
||||
0x41404051, 0x00001010, 0x00000014, 0x00000000,
|
||||
];
|
||||
|
||||
// Computes 5^i in the form required by Ryu.
|
||||
#[cfg(integer128)]
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn compute_pow5(i: u32) -> (u64, u64) {
|
||||
let base = i / DOUBLE_POW5_TABLE.len() as u32;
|
||||
let base2 = base * DOUBLE_POW5_TABLE.len() as u32;
|
||||
let offset = i - base2;
|
||||
debug_assert!(base < DOUBLE_POW5_SPLIT2.len() as u32);
|
||||
let mul = *DOUBLE_POW5_SPLIT2.get_unchecked(base as usize);
|
||||
if offset == 0 {
|
||||
return mul;
|
||||
}
|
||||
debug_assert!(offset < DOUBLE_POW5_TABLE.len() as u32);
|
||||
let m = *DOUBLE_POW5_TABLE.get_unchecked(offset as usize);
|
||||
let b0 = m as u128 * mul.0 as u128;
|
||||
let b2 = m as u128 * mul.1 as u128;
|
||||
let delta = pow5bits(i as i32) - pow5bits(base2 as i32);
|
||||
debug_assert!(base < POW5_OFFSETS.len() as u32);
|
||||
let shifted_sum = (b0 >> delta)
|
||||
+ (b2 << (64 - delta))
|
||||
+ ((*POW5_OFFSETS.get_unchecked(base as usize) >> offset) & 1) as u128;
|
||||
(shifted_sum as u64, (shifted_sum >> 64) as u64)
|
||||
}
|
||||
|
||||
// Computes 5^-i in the form required by Ryu.
|
||||
#[cfg(integer128)]
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn compute_inv_pow5(i: u32) -> (u64, u64) {
|
||||
let base = (i + DOUBLE_POW5_TABLE.len() as u32 - 1) / DOUBLE_POW5_TABLE.len() as u32;
|
||||
let base2 = base * DOUBLE_POW5_TABLE.len() as u32;
|
||||
let offset = base2 - i;
|
||||
debug_assert!(base < DOUBLE_POW5_INV_SPLIT2.len() as u32);
|
||||
let mul = *DOUBLE_POW5_INV_SPLIT2.get_unchecked(base as usize); // 1/5^base2
|
||||
if offset == 0 {
|
||||
return mul;
|
||||
}
|
||||
debug_assert!(offset < DOUBLE_POW5_TABLE.len() as u32);
|
||||
let m = *DOUBLE_POW5_TABLE.get_unchecked(offset as usize); // 5^offset
|
||||
let b0 = m as u128 * (mul.0 - 1) as u128;
|
||||
let b2 = m as u128 * mul.1 as u128; // 1/5^base2 * 5^offset = 1/5^(base2-offset) = 1/5^i
|
||||
let delta = pow5bits(base2 as i32) - pow5bits(i as i32);
|
||||
debug_assert!(base < POW5_INV_OFFSETS.len() as u32);
|
||||
let shifted_sum = ((b0 >> delta) + (b2 << (64 - delta)))
|
||||
+ 1
|
||||
+ ((*POW5_INV_OFFSETS.get_unchecked((i / 16) as usize) >> ((i % 16) << 1)) & 3) as u128;
|
||||
(shifted_sum as u64, (shifted_sum >> 64) as u64)
|
||||
}
|
||||
|
||||
// Computes 5^i in the form required by Ryu, and stores it in the given pointer.
|
||||
#[cfg(not(integer128))]
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn compute_pow5(i: u32) -> (u64, u64) {
|
||||
let base = i / DOUBLE_POW5_TABLE.len() as u32;
|
||||
let base2 = base * DOUBLE_POW5_TABLE.len() as u32;
|
||||
let offset = i - base2;
|
||||
debug_assert!(base < DOUBLE_POW5_SPLIT2.len() as u32);
|
||||
let mul = *DOUBLE_POW5_SPLIT2.get_unchecked(base as usize);
|
||||
if offset == 0 {
|
||||
return mul;
|
||||
}
|
||||
debug_assert!(offset < DOUBLE_POW5_TABLE.len() as u32);
|
||||
let m = *DOUBLE_POW5_TABLE.get_unchecked(offset as usize);
|
||||
let (low1, mut high1) = umul128(m, mul.1);
|
||||
let (low0, high0) = umul128(m, mul.0);
|
||||
let sum = high0 + low1;
|
||||
if sum < high0 {
|
||||
high1 += 1; // overflow into high1
|
||||
}
|
||||
// high1 | sum | low0
|
||||
let delta = pow5bits(i as i32) - pow5bits(base2 as i32);
|
||||
debug_assert!(base < POW5_OFFSETS.len() as u32);
|
||||
(
|
||||
shiftright128(low0, sum, delta)
|
||||
+ ((*POW5_OFFSETS.get_unchecked(base as usize) >> offset) & 1) as u64,
|
||||
shiftright128(sum, high1, delta),
|
||||
)
|
||||
}
|
||||
|
||||
// Computes 5^-i in the form required by Ryu, and stores it in the given pointer.
|
||||
#[cfg(not(integer128))]
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn compute_inv_pow5(i: u32) -> (u64, u64) {
|
||||
let base = (i + DOUBLE_POW5_TABLE.len() as u32 - 1) / DOUBLE_POW5_TABLE.len() as u32;
|
||||
let base2 = base * DOUBLE_POW5_TABLE.len() as u32;
|
||||
let offset = base2 - i;
|
||||
debug_assert!(base < DOUBLE_POW5_INV_SPLIT2.len() as u32);
|
||||
let mul = *DOUBLE_POW5_INV_SPLIT2.get_unchecked(base as usize); // 1/5^base2
|
||||
if offset == 0 {
|
||||
return mul;
|
||||
}
|
||||
debug_assert!(offset < DOUBLE_POW5_TABLE.len() as u32);
|
||||
let m = *DOUBLE_POW5_TABLE.get_unchecked(offset as usize);
|
||||
let (low1, mut high1) = umul128(m, mul.1);
|
||||
let (low0, high0) = umul128(m, mul.0 - 1);
|
||||
let sum = high0 + low1;
|
||||
if sum < high0 {
|
||||
high1 += 1; // overflow into high1
|
||||
}
|
||||
// high1 | sum | low0
|
||||
let delta = pow5bits(base2 as i32) - pow5bits(i as i32);
|
||||
debug_assert!(base < POW5_INV_OFFSETS.len() as u32);
|
||||
(
|
||||
shiftright128(low0, sum, delta)
|
||||
+ 1
|
||||
+ ((*POW5_INV_OFFSETS.get_unchecked((i / 16) as usize) >> ((i % 16) << 1)) & 3) as u64,
|
||||
shiftright128(sum, high1, delta),
|
||||
)
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
// A table of all two-digit numbers. This is used to speed up decimal digit
|
||||
// generation by copying pairs of digits into the final output.
|
||||
pub static DIGIT_TABLE: [u8; 200] = *b"\
|
||||
0001020304050607080910111213141516171819\
|
||||
2021222324252627282930313233343536373839\
|
||||
4041424344454647484950515253545556575859\
|
||||
6061626364656667686970717273747576777879\
|
||||
8081828384858687888990919293949596979899";
|
|
@ -1,494 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
use core::{mem, ptr};
|
||||
|
||||
use common::*;
|
||||
use digit_table::*;
|
||||
|
||||
#[cfg(feature = "no-panic")]
|
||||
use no_panic::no_panic;
|
||||
|
||||
pub const FLOAT_MANTISSA_BITS: u32 = 23;
|
||||
pub const FLOAT_EXPONENT_BITS: u32 = 8;
|
||||
|
||||
const FLOAT_POW5_INV_BITCOUNT: i32 = 59;
|
||||
const FLOAT_POW5_BITCOUNT: i32 = 61;
|
||||
|
||||
// This table is generated by PrintFloatLookupTable.
|
||||
static FLOAT_POW5_INV_SPLIT: [u64; 32] = [
|
||||
576460752303423489,
|
||||
461168601842738791,
|
||||
368934881474191033,
|
||||
295147905179352826,
|
||||
472236648286964522,
|
||||
377789318629571618,
|
||||
302231454903657294,
|
||||
483570327845851670,
|
||||
386856262276681336,
|
||||
309485009821345069,
|
||||
495176015714152110,
|
||||
396140812571321688,
|
||||
316912650057057351,
|
||||
507060240091291761,
|
||||
405648192073033409,
|
||||
324518553658426727,
|
||||
519229685853482763,
|
||||
415383748682786211,
|
||||
332306998946228969,
|
||||
531691198313966350,
|
||||
425352958651173080,
|
||||
340282366920938464,
|
||||
544451787073501542,
|
||||
435561429658801234,
|
||||
348449143727040987,
|
||||
557518629963265579,
|
||||
446014903970612463,
|
||||
356811923176489971,
|
||||
570899077082383953,
|
||||
456719261665907162,
|
||||
365375409332725730,
|
||||
1 << 63,
|
||||
];
|
||||
|
||||
static FLOAT_POW5_SPLIT: [u64; 47] = [
|
||||
1152921504606846976,
|
||||
1441151880758558720,
|
||||
1801439850948198400,
|
||||
2251799813685248000,
|
||||
1407374883553280000,
|
||||
1759218604441600000,
|
||||
2199023255552000000,
|
||||
1374389534720000000,
|
||||
1717986918400000000,
|
||||
2147483648000000000,
|
||||
1342177280000000000,
|
||||
1677721600000000000,
|
||||
2097152000000000000,
|
||||
1310720000000000000,
|
||||
1638400000000000000,
|
||||
2048000000000000000,
|
||||
1280000000000000000,
|
||||
1600000000000000000,
|
||||
2000000000000000000,
|
||||
1250000000000000000,
|
||||
1562500000000000000,
|
||||
1953125000000000000,
|
||||
1220703125000000000,
|
||||
1525878906250000000,
|
||||
1907348632812500000,
|
||||
1192092895507812500,
|
||||
1490116119384765625,
|
||||
1862645149230957031,
|
||||
1164153218269348144,
|
||||
1455191522836685180,
|
||||
1818989403545856475,
|
||||
2273736754432320594,
|
||||
1421085471520200371,
|
||||
1776356839400250464,
|
||||
2220446049250313080,
|
||||
1387778780781445675,
|
||||
1734723475976807094,
|
||||
2168404344971008868,
|
||||
1355252715606880542,
|
||||
1694065894508600678,
|
||||
2117582368135750847,
|
||||
1323488980084844279,
|
||||
1654361225106055349,
|
||||
2067951531382569187,
|
||||
1292469707114105741,
|
||||
1615587133892632177,
|
||||
2019483917365790221,
|
||||
];
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn pow5_factor(mut value: u32) -> u32 {
|
||||
let mut count = 0u32;
|
||||
loop {
|
||||
debug_assert!(value != 0);
|
||||
let q = value / 5;
|
||||
let r = value % 5;
|
||||
if r != 0 {
|
||||
break;
|
||||
}
|
||||
value = q;
|
||||
count += 1;
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
// Returns true if value is divisible by 5^p.
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn multiple_of_power_of_5(value: u32, p: u32) -> bool {
|
||||
pow5_factor(value) >= p
|
||||
}
|
||||
|
||||
// Returns true if value is divisible by 2^p.
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn multiple_of_power_of_2(value: u32, p: u32) -> bool {
|
||||
// return __builtin_ctz(value) >= p;
|
||||
(value & ((1u32 << p) - 1)) == 0
|
||||
}
|
||||
|
||||
// It seems to be slightly faster to avoid uint128_t here, although the
|
||||
// generated code for uint128_t looks slightly nicer.
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn mul_shift(m: u32, factor: u64, shift: i32) -> u32 {
|
||||
debug_assert!(shift > 32);
|
||||
|
||||
// The casts here help MSVC to avoid calls to the __allmul library
|
||||
// function.
|
||||
let factor_lo = factor as u32;
|
||||
let factor_hi = (factor >> 32) as u32;
|
||||
let bits0 = m as u64 * factor_lo as u64;
|
||||
let bits1 = m as u64 * factor_hi as u64;
|
||||
|
||||
let sum = (bits0 >> 32) + bits1;
|
||||
let shifted_sum = sum >> (shift - 32);
|
||||
debug_assert!(shifted_sum <= u32::max_value() as u64);
|
||||
shifted_sum as u32
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn mul_pow5_inv_div_pow2(m: u32, q: u32, j: i32) -> u32 {
|
||||
debug_assert!(q < FLOAT_POW5_INV_SPLIT.len() as u32);
|
||||
unsafe { mul_shift(m, *FLOAT_POW5_INV_SPLIT.get_unchecked(q as usize), j) }
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
fn mul_pow5_div_pow2(m: u32, i: u32, j: i32) -> u32 {
|
||||
debug_assert!(i < FLOAT_POW5_SPLIT.len() as u32);
|
||||
unsafe { mul_shift(m, *FLOAT_POW5_SPLIT.get_unchecked(i as usize), j) }
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn decimal_length(v: u32) -> u32 {
|
||||
// Function precondition: v is not a 10-digit number.
|
||||
// (9 digits are sufficient for round-tripping.)
|
||||
debug_assert!(v < 1000000000);
|
||||
|
||||
if v >= 100000000 {
|
||||
9
|
||||
} else if v >= 10000000 {
|
||||
8
|
||||
} else if v >= 1000000 {
|
||||
7
|
||||
} else if v >= 100000 {
|
||||
6
|
||||
} else if v >= 10000 {
|
||||
5
|
||||
} else if v >= 1000 {
|
||||
4
|
||||
} else if v >= 100 {
|
||||
3
|
||||
} else if v >= 10 {
|
||||
2
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
|
||||
// A floating decimal representing m * 10^e.
|
||||
pub struct FloatingDecimal32 {
|
||||
pub mantissa: u32,
|
||||
pub exponent: i32,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn f2d(ieee_mantissa: u32, ieee_exponent: u32) -> FloatingDecimal32 {
|
||||
let bias = (1u32 << (FLOAT_EXPONENT_BITS - 1)) - 1;
|
||||
|
||||
let (e2, m2) = if ieee_exponent == 0 {
|
||||
(
|
||||
// We subtract 2 so that the bounds computation has 2 additional bits.
|
||||
1 - bias as i32 - FLOAT_MANTISSA_BITS as i32 - 2,
|
||||
ieee_mantissa,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
ieee_exponent as i32 - bias as i32 - FLOAT_MANTISSA_BITS as i32 - 2,
|
||||
(1u32 << FLOAT_MANTISSA_BITS) | ieee_mantissa,
|
||||
)
|
||||
};
|
||||
let even = (m2 & 1) == 0;
|
||||
let accept_bounds = even;
|
||||
|
||||
// Step 2: Determine the interval of legal decimal representations.
|
||||
let mv = 4 * m2;
|
||||
let mp = 4 * m2 + 2;
|
||||
// Implicit bool -> int conversion. True is 1, false is 0.
|
||||
let mm_shift = (ieee_mantissa != 0 || ieee_exponent <= 1) as u32;
|
||||
let mm = 4 * m2 - 1 - mm_shift;
|
||||
|
||||
// Step 3: Convert to a decimal power base using 64-bit arithmetic.
|
||||
let mut vr: u32;
|
||||
let mut vp: u32;
|
||||
let mut vm: u32;
|
||||
let e10: i32;
|
||||
let mut vm_is_trailing_zeros = false;
|
||||
let mut vr_is_trailing_zeros = false;
|
||||
let mut last_removed_digit = 0u8;
|
||||
if e2 >= 0 {
|
||||
let q = log10_pow2(e2) as u32;
|
||||
e10 = q as i32;
|
||||
let k = FLOAT_POW5_INV_BITCOUNT + pow5bits(q as i32) as i32 - 1;
|
||||
let i = -e2 + q as i32 + k;
|
||||
vr = mul_pow5_inv_div_pow2(mv, q, i);
|
||||
vp = mul_pow5_inv_div_pow2(mp, q, i);
|
||||
vm = mul_pow5_inv_div_pow2(mm, q, i);
|
||||
if q != 0 && (vp - 1) / 10 <= vm / 10 {
|
||||
// We need to know one removed digit even if we are not going to loop below. We could use
|
||||
// q = X - 1 above, except that would require 33 bits for the result, and we've found that
|
||||
// 32-bit arithmetic is faster even on 64-bit machines.
|
||||
let l = FLOAT_POW5_INV_BITCOUNT + pow5bits(q as i32 - 1) as i32 - 1;
|
||||
last_removed_digit =
|
||||
(mul_pow5_inv_div_pow2(mv, q - 1, -e2 + q as i32 - 1 + l) % 10) as u8;
|
||||
}
|
||||
if q <= 9 {
|
||||
// The largest power of 5 that fits in 24 bits is 5^10, but q <= 9 seems to be safe as well.
|
||||
// Only one of mp, mv, and mm can be a multiple of 5, if any.
|
||||
if mv % 5 == 0 {
|
||||
vr_is_trailing_zeros = multiple_of_power_of_5(mv, q);
|
||||
} else if accept_bounds {
|
||||
vm_is_trailing_zeros = multiple_of_power_of_5(mm, q);
|
||||
} else {
|
||||
vp -= multiple_of_power_of_5(mp, q) as u32;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let q = log10_pow5(-e2) as u32;
|
||||
e10 = q as i32 + e2;
|
||||
let i = -e2 - q as i32;
|
||||
let k = pow5bits(i) as i32 - FLOAT_POW5_BITCOUNT;
|
||||
let mut j = q as i32 - k;
|
||||
vr = mul_pow5_div_pow2(mv, i as u32, j);
|
||||
vp = mul_pow5_div_pow2(mp, i as u32, j);
|
||||
vm = mul_pow5_div_pow2(mm, i as u32, j);
|
||||
if q != 0 && (vp - 1) / 10 <= vm / 10 {
|
||||
j = q as i32 - 1 - (pow5bits(i + 1) as i32 - FLOAT_POW5_BITCOUNT);
|
||||
last_removed_digit = (mul_pow5_div_pow2(mv, (i + 1) as u32, j) % 10) as u8;
|
||||
}
|
||||
if q <= 1 {
|
||||
// {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits.
|
||||
// mv = 4 * m2, so it always has at least two trailing 0 bits.
|
||||
vr_is_trailing_zeros = true;
|
||||
if accept_bounds {
|
||||
// mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit iff mm_shift == 1.
|
||||
vm_is_trailing_zeros = mm_shift == 1;
|
||||
} else {
|
||||
// mp = mv + 2, so it always has at least one trailing 0 bit.
|
||||
vp -= 1;
|
||||
}
|
||||
} else if q < 31 {
|
||||
// TODO(ulfjack): Use a tighter bound here.
|
||||
vr_is_trailing_zeros = multiple_of_power_of_2(mv, q - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: Find the shortest decimal representation in the interval of legal representations.
|
||||
let mut removed = 0u32;
|
||||
let output = if vm_is_trailing_zeros || vr_is_trailing_zeros {
|
||||
// General case, which happens rarely (~4.0%).
|
||||
while vp / 10 > vm / 10 {
|
||||
vm_is_trailing_zeros &= vm - (vm / 10) * 10 == 0;
|
||||
vr_is_trailing_zeros &= last_removed_digit == 0;
|
||||
last_removed_digit = (vr % 10) as u8;
|
||||
vr /= 10;
|
||||
vp /= 10;
|
||||
vm /= 10;
|
||||
removed += 1;
|
||||
}
|
||||
if vm_is_trailing_zeros {
|
||||
while vm % 10 == 0 {
|
||||
vr_is_trailing_zeros &= last_removed_digit == 0;
|
||||
last_removed_digit = (vr % 10) as u8;
|
||||
vr /= 10;
|
||||
vp /= 10;
|
||||
vm /= 10;
|
||||
removed += 1;
|
||||
}
|
||||
}
|
||||
if vr_is_trailing_zeros && last_removed_digit == 5 && vr % 2 == 0 {
|
||||
// Round even if the exact number is .....50..0.
|
||||
last_removed_digit = 4;
|
||||
}
|
||||
// We need to take vr + 1 if vr is outside bounds or we need to round up.
|
||||
vr + ((vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5)
|
||||
as u32
|
||||
} else {
|
||||
// Specialized for the common case (~96.0%). Percentages below are relative to this.
|
||||
// Loop iterations below (approximately):
|
||||
// 0: 13.6%, 1: 70.7%, 2: 14.1%, 3: 1.39%, 4: 0.14%, 5+: 0.01%
|
||||
while vp / 10 > vm / 10 {
|
||||
last_removed_digit = (vr % 10) as u8;
|
||||
vr /= 10;
|
||||
vp /= 10;
|
||||
vm /= 10;
|
||||
removed += 1;
|
||||
}
|
||||
// We need to take vr + 1 if vr is outside bounds or we need to round up.
|
||||
vr + (vr == vm || last_removed_digit >= 5) as u32
|
||||
};
|
||||
let exp = e10 + removed as i32;
|
||||
|
||||
FloatingDecimal32 {
|
||||
exponent: exp,
|
||||
mantissa: output,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
unsafe fn to_chars(v: FloatingDecimal32, sign: bool, result: *mut u8) -> usize {
|
||||
// Step 5: Print the decimal representation.
|
||||
let mut index = 0isize;
|
||||
if sign {
|
||||
*result.offset(index) = b'-';
|
||||
index += 1;
|
||||
}
|
||||
|
||||
let mut output = v.mantissa;
|
||||
let olength = decimal_length(output);
|
||||
|
||||
// Print the decimal digits.
|
||||
// The following code is equivalent to:
|
||||
// for (uint32_t i = 0; i < olength - 1; ++i) {
|
||||
// const uint32_t c = output % 10; output /= 10;
|
||||
// result[index + olength - i] = (char) ('0' + c);
|
||||
// }
|
||||
// result[index] = '0' + output % 10;
|
||||
let mut i = 0isize;
|
||||
while output >= 10000 {
|
||||
let c = output - 10000 * (output / 10000);
|
||||
output /= 10000;
|
||||
let c0 = (c % 100) << 1;
|
||||
let c1 = (c / 100) << 1;
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(c0 as usize),
|
||||
result.offset(index + olength as isize - i - 1),
|
||||
2,
|
||||
);
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(c1 as usize),
|
||||
result.offset(index + olength as isize - i - 3),
|
||||
2,
|
||||
);
|
||||
i += 4;
|
||||
}
|
||||
if output >= 100 {
|
||||
let c = (output % 100) << 1;
|
||||
output /= 100;
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked(c as usize),
|
||||
result.offset(index + olength as isize - i - 1),
|
||||
2,
|
||||
);
|
||||
i += 2;
|
||||
}
|
||||
if output >= 10 {
|
||||
let c = output << 1;
|
||||
// We can't use memcpy here: the decimal dot goes between these two digits.
|
||||
*result.offset(index + olength as isize - i) = *DIGIT_TABLE.get_unchecked(c as usize + 1);
|
||||
*result.offset(index) = *DIGIT_TABLE.get_unchecked(c as usize);
|
||||
} else {
|
||||
*result.offset(index) = b'0' + output as u8;
|
||||
}
|
||||
|
||||
// Print decimal point if needed.
|
||||
if olength > 1 {
|
||||
*result.offset(index + 1) = b'.';
|
||||
index += olength as isize + 1;
|
||||
} else {
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// Print the exponent.
|
||||
*result.offset(index) = b'E';
|
||||
index += 1;
|
||||
let mut exp = v.exponent + olength as i32 - 1;
|
||||
if exp < 0 {
|
||||
*result.offset(index) = b'-';
|
||||
index += 1;
|
||||
exp = -exp;
|
||||
}
|
||||
|
||||
if exp >= 10 {
|
||||
ptr::copy_nonoverlapping(
|
||||
DIGIT_TABLE.get_unchecked((2 * exp) as usize),
|
||||
result.offset(index),
|
||||
2,
|
||||
);
|
||||
index += 2;
|
||||
} else {
|
||||
*result.offset(index) = b'0' + exp as u8;
|
||||
index += 1;
|
||||
}
|
||||
|
||||
debug_assert!(index <= 15);
|
||||
index as usize
|
||||
}
|
||||
|
||||
/// Print f32 to the given buffer and return number of bytes written.
|
||||
///
|
||||
/// At most 15 bytes will be written.
|
||||
///
|
||||
/// ## Special cases
|
||||
///
|
||||
/// This function represents any NaN as `NaN`, positive infinity as `Infinity`,
|
||||
/// and negative infinity as `-Infinity`.
|
||||
///
|
||||
/// ## Safety
|
||||
///
|
||||
/// The `result` pointer argument must point to sufficiently many writable bytes
|
||||
/// to hold Ryū's representation of `f`.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```rust
|
||||
/// let f = 1.234f32;
|
||||
///
|
||||
/// unsafe {
|
||||
/// let mut buffer: [u8; 15] = std::mem::uninitialized();
|
||||
/// let n = ryu::raw::f2s_buffered_n(f, &mut buffer[0]);
|
||||
/// let s = std::str::from_utf8_unchecked(&buffer[..n]);
|
||||
/// assert_eq!(s, "1.234E0");
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg_attr(must_use_return, must_use)]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
pub unsafe fn f2s_buffered_n(f: f32, result: *mut u8) -> usize {
|
||||
// Step 1: Decode the floating-point number, and unify normalized and subnormal cases.
|
||||
let bits = mem::transmute::<f32, u32>(f);
|
||||
|
||||
// Decode bits into sign, mantissa, and exponent.
|
||||
let ieee_sign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0;
|
||||
let ieee_mantissa = bits & ((1u32 << FLOAT_MANTISSA_BITS) - 1);
|
||||
let ieee_exponent =
|
||||
((bits >> FLOAT_MANTISSA_BITS) & ((1u32 << FLOAT_EXPONENT_BITS) - 1)) as u32;
|
||||
|
||||
// Case distinction; exit early for the easy cases.
|
||||
if ieee_exponent == ((1u32 << FLOAT_EXPONENT_BITS) - 1)
|
||||
|| (ieee_exponent == 0 && ieee_mantissa == 0)
|
||||
{
|
||||
return copy_special_str(result, ieee_sign, ieee_exponent != 0, ieee_mantissa != 0);
|
||||
}
|
||||
|
||||
let v = f2d(ieee_mantissa, ieee_exponent);
|
||||
to_chars(v, ieee_sign, result)
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
//! Pure Rust implementation of Ryū, an algorithm to quickly convert floating
|
||||
//! point numbers to decimal strings.
|
||||
//!
|
||||
//! The PLDI'18 paper [*Ryū: fast float-to-string conversion*][paper] by Ulf
|
||||
//! Adams includes a complete correctness proof of the algorithm. The paper is
|
||||
//! available under the creative commons CC-BY-SA license.
|
||||
//!
|
||||
//! This Rust implementation is a line-by-line port of Ulf Adams' implementation
|
||||
//! in C, [https://github.com/ulfjack/ryu][upstream]. The [`ryu::raw`][raw]
|
||||
//! module exposes exactly the API and formatting of the C implementation as
|
||||
//! unsafe pure Rust functions. There is additionally a safe API as demonstrated
|
||||
//! in the example code below. The safe API uses the same underlying Ryū
|
||||
//! algorithm but diverges from the formatting of the C implementation to
|
||||
//! produce more human-readable output, for example `0.3` rather than `3E-1`.
|
||||
//!
|
||||
//! [paper]: https://dl.acm.org/citation.cfm?id=3192369
|
||||
//! [upstream]: https://github.com/ulfjack/ryu
|
||||
//! [raw]: raw/index.html
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate ryu;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! let mut buffer = ryu::Buffer::new();
|
||||
//! let printed = buffer.format(1.234);
|
||||
//! assert_eq!(printed, "1.234");
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
#![no_std]
|
||||
#![doc(html_root_url = "https://docs.rs/ryu/0.2.4")]
|
||||
#![cfg_attr(feature = "no-panic", feature(use_extern_macros))]
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
allow(
|
||||
cast_lossless,
|
||||
cyclomatic_complexity,
|
||||
many_single_char_names,
|
||||
needless_pass_by_value,
|
||||
unreadable_literal,
|
||||
)
|
||||
)]
|
||||
|
||||
#[cfg(feature = "no-panic")]
|
||||
extern crate no_panic;
|
||||
|
||||
mod buffer;
|
||||
mod common;
|
||||
mod d2s;
|
||||
#[cfg(not(feature = "small"))]
|
||||
mod d2s_full_table;
|
||||
#[cfg(feature = "small")]
|
||||
mod d2s_small_table;
|
||||
mod digit_table;
|
||||
mod f2s;
|
||||
#[cfg(not(integer128))]
|
||||
mod mulshift128;
|
||||
mod pretty;
|
||||
|
||||
pub use buffer::{Buffer, Float};
|
||||
|
||||
/// Unsafe functions that exactly mirror the API of the C implementation of Ryū.
|
||||
pub mod raw {
|
||||
pub use d2s::d2s_buffered_n;
|
||||
pub use f2s::f2s_buffered_n;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
// Returns (lo, hi).
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn umul128(a: u64, b: u64) -> (u64, u64) {
|
||||
let a_lo = a as u32;
|
||||
let a_hi = (a >> 32) as u32;
|
||||
let b_lo = b as u32;
|
||||
let b_hi = (b >> 32) as u32;
|
||||
|
||||
let b00 = a_lo as u64 * b_lo as u64;
|
||||
let b01 = a_lo as u64 * b_hi as u64;
|
||||
let b10 = a_hi as u64 * b_lo as u64;
|
||||
let b11 = a_hi as u64 * b_hi as u64;
|
||||
|
||||
let b00_lo = b00 as u32;
|
||||
let b00_hi = (b00 >> 32) as u32;
|
||||
|
||||
let mid1 = b10 + b00_hi as u64;
|
||||
let mid1_lo = mid1 as u32;
|
||||
let mid1_hi = (mid1 >> 32) as u32;
|
||||
|
||||
let mid2 = b01 + mid1_lo as u64;
|
||||
let mid2_lo = mid2 as u32;
|
||||
let mid2_hi = (mid2 >> 32) as u32;
|
||||
|
||||
let p_hi = b11 + mid1_hi as u64 + mid2_hi as u64;
|
||||
let p_lo = ((mid2_lo as u64) << 32) + b00_lo as u64;
|
||||
|
||||
(p_lo, p_hi)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub fn shiftright128(lo: u64, hi: u64, dist: u32) -> u64 {
|
||||
// We don't need to handle the case dist >= 64 here (see above).
|
||||
debug_assert!(dist > 0);
|
||||
debug_assert!(dist < 64);
|
||||
(hi << (64 - dist)) | (lo >> dist)
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
use core::ptr;
|
||||
|
||||
use digit_table::*;
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn write_exponent3(mut k: isize, mut result: *mut u8) -> usize {
|
||||
let sign = k < 0;
|
||||
if sign {
|
||||
*result = b'-';
|
||||
result = result.offset(1);
|
||||
k = -k;
|
||||
}
|
||||
|
||||
debug_assert!(k < 1000);
|
||||
if k >= 100 {
|
||||
*result = b'0' + (k / 100) as u8;
|
||||
k %= 100;
|
||||
let d = DIGIT_TABLE.get_unchecked(k as usize * 2);
|
||||
ptr::copy_nonoverlapping(d, result.offset(1), 2);
|
||||
sign as usize + 3
|
||||
} else if k >= 10 {
|
||||
let d = DIGIT_TABLE.get_unchecked(k as usize * 2);
|
||||
ptr::copy_nonoverlapping(d, result, 2);
|
||||
sign as usize + 2
|
||||
} else {
|
||||
*result = b'0' + k as u8;
|
||||
sign as usize + 1
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn write_exponent2(mut k: isize, mut result: *mut u8) -> usize {
|
||||
let sign = k < 0;
|
||||
if sign {
|
||||
*result = b'-';
|
||||
result = result.offset(1);
|
||||
k = -k;
|
||||
}
|
||||
|
||||
debug_assert!(k < 100);
|
||||
if k >= 10 {
|
||||
let d = DIGIT_TABLE.get_unchecked(k as usize * 2);
|
||||
ptr::copy_nonoverlapping(d, result, 2);
|
||||
sign as usize + 2
|
||||
} else {
|
||||
*result = b'0' + k as u8;
|
||||
sign as usize + 1
|
||||
}
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
use core::ptr;
|
||||
|
||||
use digit_table::*;
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn write_mantissa_long(mut output: u64, mut result: *mut u8) {
|
||||
if (output >> 32) != 0 {
|
||||
// One expensive 64-bit division.
|
||||
let mut output2 = (output - 100_000_000 * (output / 100_000_000)) as u32;
|
||||
output /= 100_000_000;
|
||||
|
||||
let c = output2 % 10_000;
|
||||
output2 /= 10_000;
|
||||
let d = output2 % 10_000;
|
||||
let c0 = (c % 100) << 1;
|
||||
let c1 = (c / 100) << 1;
|
||||
let d0 = (d % 100) << 1;
|
||||
let d1 = (d / 100) << 1;
|
||||
ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c0 as usize), result.offset(-2), 2);
|
||||
ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c1 as usize), result.offset(-4), 2);
|
||||
ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(d0 as usize), result.offset(-6), 2);
|
||||
ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(d1 as usize), result.offset(-8), 2);
|
||||
result = result.offset(-8);
|
||||
}
|
||||
write_mantissa(output as u32, result);
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "no-panic", inline)]
|
||||
pub unsafe fn write_mantissa(mut output: u32, mut result: *mut u8) {
|
||||
while output >= 10_000 {
|
||||
let c = (output - 10_000 * (output / 10_000)) as u32;
|
||||
output /= 10_000;
|
||||
let c0 = (c % 100) << 1;
|
||||
let c1 = (c / 100) << 1;
|
||||
ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c0 as usize), result.offset(-2), 2);
|
||||
ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c1 as usize), result.offset(-4), 2);
|
||||
result = result.offset(-4);
|
||||
}
|
||||
if output >= 100 {
|
||||
let c = ((output % 100) << 1) as u32;
|
||||
output /= 100;
|
||||
ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c as usize), result.offset(-2), 2);
|
||||
result = result.offset(-2);
|
||||
}
|
||||
if output >= 10 {
|
||||
let c = (output << 1) as u32;
|
||||
ptr::copy_nonoverlapping(DIGIT_TABLE.get_unchecked(c as usize), result.offset(-2), 2);
|
||||
} else {
|
||||
*result.offset(-1) = b'0' + output as u8;
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
mod exponent;
|
||||
mod mantissa;
|
||||
|
||||
use core::{mem, ptr};
|
||||
|
||||
use self::exponent::*;
|
||||
use self::mantissa::*;
|
||||
use d2s;
|
||||
use d2s::*;
|
||||
use f2s;
|
||||
use f2s::*;
|
||||
|
||||
#[cfg(feature = "no-panic")]
|
||||
use no_panic::no_panic;
|
||||
|
||||
#[cfg_attr(must_use_return, must_use)]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
pub unsafe fn d2s_buffered_n(f: f64, result: *mut u8) -> usize {
|
||||
let bits = mem::transmute::<f64, u64>(f);
|
||||
let sign = ((bits >> (DOUBLE_MANTISSA_BITS + DOUBLE_EXPONENT_BITS)) & 1) != 0;
|
||||
let ieee_mantissa = bits & ((1u64 << DOUBLE_MANTISSA_BITS) - 1);
|
||||
let ieee_exponent =
|
||||
(bits >> DOUBLE_MANTISSA_BITS) as u32 & ((1u32 << DOUBLE_EXPONENT_BITS) - 1);
|
||||
|
||||
let mut index = 0isize;
|
||||
if sign {
|
||||
*result = b'-';
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if ieee_exponent == 0 && ieee_mantissa == 0 {
|
||||
ptr::copy_nonoverlapping(b"0.0".as_ptr(), result.offset(index), 3);
|
||||
return sign as usize + 3;
|
||||
}
|
||||
|
||||
let v = d2d(ieee_mantissa, ieee_exponent);
|
||||
|
||||
let length = d2s::decimal_length(v.mantissa) as isize;
|
||||
let k = v.exponent as isize;
|
||||
let kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||
debug_assert!(k >= -324);
|
||||
|
||||
if 0 <= k && kk <= 16 {
|
||||
// 1234e7 -> 12340000000.0
|
||||
write_mantissa_long(v.mantissa, result.offset(index + length));
|
||||
for i in length..kk {
|
||||
*result.offset(index + i) = b'0';
|
||||
}
|
||||
*result.offset(index + kk) = b'.';
|
||||
*result.offset(index + kk + 1) = b'0';
|
||||
index as usize + kk as usize + 2
|
||||
} else if 0 < kk && kk <= 16 {
|
||||
// 1234e-2 -> 12.34
|
||||
write_mantissa_long(v.mantissa, result.offset(index + length + 1));
|
||||
ptr::copy(result.offset(index + 1), result.offset(index), kk as usize);
|
||||
*result.offset(index + kk) = b'.';
|
||||
index as usize + length as usize + 1
|
||||
} else if -5 < kk && kk <= 0 {
|
||||
// 1234e-6 -> 0.001234
|
||||
*result.offset(index) = b'0';
|
||||
*result.offset(index + 1) = b'.';
|
||||
let offset = 2 - kk;
|
||||
for i in 2..offset {
|
||||
*result.offset(index + i) = b'0';
|
||||
}
|
||||
write_mantissa_long(v.mantissa, result.offset(index + length + offset));
|
||||
index as usize + length as usize + offset as usize
|
||||
} else if length == 1 {
|
||||
// 1e30
|
||||
*result.offset(index) = b'0' + v.mantissa as u8;
|
||||
*result.offset(index + 1) = b'e';
|
||||
index as usize + 2 + write_exponent3(kk - 1, result.offset(index + 2))
|
||||
} else {
|
||||
// 1234e30 -> 1.234e33
|
||||
write_mantissa_long(v.mantissa, result.offset(index + length + 1));
|
||||
*result.offset(index) = *result.offset(index + 1);
|
||||
*result.offset(index + 1) = b'.';
|
||||
*result.offset(index + length + 1) = b'e';
|
||||
index as usize
|
||||
+ length as usize
|
||||
+ 2
|
||||
+ write_exponent3(kk - 1, result.offset(index + length + 2))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(must_use_return, must_use)]
|
||||
#[cfg_attr(feature = "no-panic", no_panic)]
|
||||
pub unsafe fn f2s_buffered_n(f: f32, result: *mut u8) -> usize {
|
||||
let bits = mem::transmute::<f32, u32>(f);
|
||||
let sign = ((bits >> (FLOAT_MANTISSA_BITS + FLOAT_EXPONENT_BITS)) & 1) != 0;
|
||||
let ieee_mantissa = bits & ((1u32 << FLOAT_MANTISSA_BITS) - 1);
|
||||
let ieee_exponent =
|
||||
((bits >> FLOAT_MANTISSA_BITS) & ((1u32 << FLOAT_EXPONENT_BITS) - 1)) as u32;
|
||||
|
||||
let mut index = 0isize;
|
||||
if sign {
|
||||
*result = b'-';
|
||||
index += 1;
|
||||
}
|
||||
|
||||
if ieee_exponent == 0 && ieee_mantissa == 0 {
|
||||
ptr::copy_nonoverlapping(b"0.0".as_ptr(), result.offset(index), 3);
|
||||
return sign as usize + 3;
|
||||
}
|
||||
|
||||
let v = f2d(ieee_mantissa, ieee_exponent);
|
||||
|
||||
let length = f2s::decimal_length(v.mantissa) as isize;
|
||||
let k = v.exponent as isize;
|
||||
let kk = length + k; // 10^(kk-1) <= v < 10^kk
|
||||
debug_assert!(k >= -45);
|
||||
|
||||
if 0 <= k && kk <= 13 {
|
||||
// 1234e7 -> 12340000000.0
|
||||
write_mantissa(v.mantissa, result.offset(index + length));
|
||||
for i in length..kk {
|
||||
*result.offset(index + i) = b'0';
|
||||
}
|
||||
*result.offset(index + kk) = b'.';
|
||||
*result.offset(index + kk + 1) = b'0';
|
||||
index as usize + kk as usize + 2
|
||||
} else if 0 < kk && kk <= 13 {
|
||||
// 1234e-2 -> 12.34
|
||||
write_mantissa(v.mantissa, result.offset(index + length + 1));
|
||||
ptr::copy(result.offset(index + 1), result.offset(index), kk as usize);
|
||||
*result.offset(index + kk) = b'.';
|
||||
index as usize + length as usize + 1
|
||||
} else if -6 < kk && kk <= 0 {
|
||||
// 1234e-6 -> 0.001234
|
||||
*result.offset(index) = b'0';
|
||||
*result.offset(index + 1) = b'.';
|
||||
let offset = 2 - kk;
|
||||
for i in 2..offset {
|
||||
*result.offset(index + i) = b'0';
|
||||
}
|
||||
write_mantissa(v.mantissa, result.offset(index + length + offset));
|
||||
index as usize + length as usize + offset as usize
|
||||
} else if length == 1 {
|
||||
// 1e30
|
||||
*result.offset(index) = b'0' + v.mantissa as u8;
|
||||
*result.offset(index + 1) = b'e';
|
||||
index as usize + 2 + write_exponent2(kk - 1, result.offset(index + 2))
|
||||
} else {
|
||||
// 1234e30 -> 1.234e33
|
||||
write_mantissa(v.mantissa, result.offset(index + length + 1));
|
||||
*result.offset(index) = *result.offset(index + 1);
|
||||
*result.offset(index + 1) = b'.';
|
||||
*result.offset(index + length + 1) = b'e';
|
||||
index as usize
|
||||
+ length as usize
|
||||
+ 2
|
||||
+ write_exponent2(kk - 1, result.offset(index + length + 2))
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
extern crate core;
|
||||
|
||||
#[path = "../src/common.rs"]
|
||||
mod common;
|
||||
|
||||
#[path = "../src/d2s_full_table.rs"]
|
||||
mod d2s_full_table;
|
||||
|
||||
#[path = "../src/d2s_small_table.rs"]
|
||||
mod d2s_small_table;
|
||||
|
||||
#[path = "../src/mulshift128.rs"]
|
||||
mod mulshift128;
|
||||
|
||||
use d2s_full_table::*;
|
||||
use d2s_small_table::*;
|
||||
|
||||
#[test]
|
||||
fn test_compute_pow5() {
|
||||
for (i, entry) in DOUBLE_POW5_SPLIT.iter().enumerate() {
|
||||
assert_eq!(*entry, unsafe { compute_pow5(i as u32) }, "entry {}", i);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compute_inv_pow5() {
|
||||
for (i, entry) in DOUBLE_POW5_INV_SPLIT.iter().enumerate() {
|
||||
assert_eq!(*entry, unsafe { compute_inv_pow5(i as u32) }, "entry {}", i);
|
||||
}
|
||||
}
|
|
@ -1,154 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
extern crate rand;
|
||||
extern crate ryu;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
use std::{f64, str};
|
||||
|
||||
fn print(f: f64) -> String {
|
||||
let mut bytes = [0u8; 24];
|
||||
let n = unsafe { ryu::raw::d2s_buffered_n(f, &mut bytes[0]) };
|
||||
let s = str::from_utf8(&bytes[..n]).unwrap();
|
||||
s.to_owned()
|
||||
}
|
||||
|
||||
fn pretty(f: f64) -> String {
|
||||
ryu::Buffer::new().format(f).to_owned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ryu() {
|
||||
check!(3E-1, 0.3);
|
||||
check!(1.234E15, 1234000000000000.0);
|
||||
check!(1.234E16, 1.234e16);
|
||||
check!(2.71828E0, 2.71828);
|
||||
check!(1.1E128, 1.1e128);
|
||||
check!(1.1E-64, 1.1e-64);
|
||||
check!(2.718281828459045E0, 2.718281828459045);
|
||||
check!(5E-324, 5e-324);
|
||||
check!(1.7976931348623157E308, 1.7976931348623157e308);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random() {
|
||||
let mut bytes = [0u8; 24];
|
||||
let mut buffer = ryu::Buffer::new();
|
||||
for _ in 0..1000000 {
|
||||
let f = rand::random();
|
||||
let n = unsafe { ryu::raw::d2s_buffered_n(f, &mut bytes[0]) };
|
||||
assert_eq!(f, str::from_utf8(&bytes[..n]).unwrap().parse().unwrap());
|
||||
assert_eq!(f, buffer.format(f).parse().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_finite() {
|
||||
for i in 0u64..1 << 23 {
|
||||
let f = f64::from_bits((((1 << 11) - 1) << 52) + (i << 29));
|
||||
assert!(!f.is_finite(), "f={}", f);
|
||||
ryu::Buffer::new().format(f);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
check!(0E0, 0.0);
|
||||
check!(-0E0, -0.0);
|
||||
check!(1E0, 1.0);
|
||||
check!(-1E0, -1.0);
|
||||
assert_eq!(print(f64::NAN), "NaN");
|
||||
assert_eq!(print(f64::INFINITY), "Infinity");
|
||||
assert_eq!(print(f64::NEG_INFINITY), "-Infinity");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_to_subnormal() {
|
||||
check!(2.2250738585072014E-308, 2.2250738585072014e-308);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_and_max() {
|
||||
assert_eq!(f64::from_bits(0x7fefffffffffffff), 1.7976931348623157e308);
|
||||
check!(1.7976931348623157E308, 1.7976931348623157e308);
|
||||
assert_eq!(f64::from_bits(1), 5e-324);
|
||||
check!(5E-324, 5e-324);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lots_of_trailing_zeros() {
|
||||
check!(2.9802322387695312E-8, 2.9802322387695312e-8);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regression() {
|
||||
check!(-2.109808898695963E16, -2.109808898695963e16);
|
||||
check!(4.940656E-318, 4.940656e-318);
|
||||
check!(1.18575755E-316, 1.18575755e-316);
|
||||
check!(2.989102097996E-312, 2.989102097996e-312);
|
||||
check!(9.0608011534336E15, 9060801153433600.0);
|
||||
check!(4.708356024711512E18, 4.708356024711512e18);
|
||||
check!(9.409340012568248E18, 9.409340012568248e18);
|
||||
check!(1.2345678E0, 1.2345678);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_looks_like_pow5() {
|
||||
// These numbers have a mantissa that is a multiple of the largest power of
|
||||
// 5 that fits, and an exponent that causes the computation for q to result
|
||||
// in 22, which is a corner case for Ryu.
|
||||
assert_eq!(f64::from_bits(0x4830F0CF064DD592), 5.764607523034235e39);
|
||||
check!(5.764607523034235E39, 5.764607523034235e39);
|
||||
assert_eq!(f64::from_bits(0x4840F0CF064DD592), 1.152921504606847e40);
|
||||
check!(1.152921504606847E40, 1.152921504606847e40);
|
||||
assert_eq!(f64::from_bits(0x4850F0CF064DD592), 2.305843009213694e40);
|
||||
check!(2.305843009213694E40, 2.305843009213694e40);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_output_length() {
|
||||
check!(1E0, 1.0); // already tested in Basic
|
||||
check!(1.2E0, 1.2);
|
||||
check!(1.23E0, 1.23);
|
||||
check!(1.234E0, 1.234);
|
||||
check!(1.2345E0, 1.2345);
|
||||
check!(1.23456E0, 1.23456);
|
||||
check!(1.234567E0, 1.234567);
|
||||
check!(1.2345678E0, 1.2345678); // already tested in Regression
|
||||
check!(1.23456789E0, 1.23456789);
|
||||
check!(1.234567895E0, 1.234567895); // 1.234567890 would be trimmed
|
||||
check!(1.2345678901E0, 1.2345678901);
|
||||
check!(1.23456789012E0, 1.23456789012);
|
||||
check!(1.234567890123E0, 1.234567890123);
|
||||
check!(1.2345678901234E0, 1.2345678901234);
|
||||
check!(1.23456789012345E0, 1.23456789012345);
|
||||
check!(1.234567890123456E0, 1.234567890123456);
|
||||
check!(1.2345678901234567E0, 1.2345678901234567);
|
||||
|
||||
// Test 32-bit chunking
|
||||
check!(4.294967294E0, 4.294967294); // 2^32 - 2
|
||||
check!(4.294967295E0, 4.294967295); // 2^32 - 1
|
||||
check!(4.294967296E0, 4.294967296); // 2^32
|
||||
check!(4.294967297E0, 4.294967297); // 2^32 + 1
|
||||
check!(4.294967298E0, 4.294967298); // 2^32 + 2
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#![cfg(exhaustive)]
|
||||
|
||||
extern crate num_cpus;
|
||||
extern crate ryu;
|
||||
|
||||
use std::str;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::thread;
|
||||
|
||||
#[test]
|
||||
fn test_exhaustive() {
|
||||
const BATCH_SIZE: u32 = 1_000_000;
|
||||
let counter = Arc::new(AtomicUsize::new(0));
|
||||
let finished = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
let mut workers = Vec::new();
|
||||
for _ in 0..num_cpus::get() {
|
||||
let counter = counter.clone();
|
||||
let finished = finished.clone();
|
||||
workers.push(thread::spawn(move || loop {
|
||||
let batch = counter.fetch_add(1, Ordering::SeqCst) as u32;
|
||||
if batch > u32::max_value() / BATCH_SIZE {
|
||||
return;
|
||||
}
|
||||
|
||||
let min = batch * BATCH_SIZE;
|
||||
let max = if batch == u32::max_value() / BATCH_SIZE {
|
||||
u32::max_value()
|
||||
} else {
|
||||
min + BATCH_SIZE - 1
|
||||
};
|
||||
|
||||
let mut bytes = [0u8; 24];
|
||||
let mut buffer = ryu::Buffer::new();
|
||||
for u in min..=max {
|
||||
let f = f32::from_bits(u);
|
||||
if !f.is_finite() {
|
||||
continue;
|
||||
}
|
||||
let n = unsafe { ryu::raw::f2s_buffered_n(f, &mut bytes[0]) };
|
||||
assert_eq!(Ok(Ok(f)), str::from_utf8(&bytes[..n]).map(str::parse));
|
||||
assert_eq!(Ok(f), buffer.format(f).parse());
|
||||
}
|
||||
|
||||
let increment = (max - min + 1) as usize;
|
||||
let update = finished.fetch_add(increment, Ordering::SeqCst);
|
||||
println!("{}", update + increment);
|
||||
}));
|
||||
}
|
||||
|
||||
for w in workers {
|
||||
w.join().unwrap();
|
||||
}
|
||||
}
|
|
@ -1,183 +0,0 @@
|
|||
// Translated from C to Rust. The original C code can be found at
|
||||
// https://github.com/ulfjack/ryu and carries the following license:
|
||||
//
|
||||
// Copyright 2018 Ulf Adams
|
||||
//
|
||||
// The contents of this file may be used under the terms of the Apache License,
|
||||
// Version 2.0.
|
||||
//
|
||||
// (See accompanying file LICENSE-Apache or copy at
|
||||
// http://www.apache.org/licenses/LICENSE-2.0)
|
||||
//
|
||||
// Alternatively, the contents of this file may be used under the terms of
|
||||
// the Boost Software License, Version 1.0.
|
||||
// (See accompanying file LICENSE-Boost or copy at
|
||||
// https://www.boost.org/LICENSE_1_0.txt)
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, this software
|
||||
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
// KIND, either express or implied.
|
||||
|
||||
extern crate rand;
|
||||
extern crate ryu;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
use std::{f32, str};
|
||||
|
||||
fn print(f: f32) -> String {
|
||||
let mut bytes = [0u8; 24];
|
||||
let n = unsafe { ryu::raw::f2s_buffered_n(f, &mut bytes[0]) };
|
||||
let s = str::from_utf8(&bytes[..n]).unwrap();
|
||||
s.to_owned()
|
||||
}
|
||||
|
||||
fn pretty(f: f32) -> String {
|
||||
ryu::Buffer::new().format(f).to_owned()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ryu() {
|
||||
check!(3E-1, 0.3);
|
||||
check!(1.234E12, 1234000000000.0);
|
||||
check!(1.234E13, 1.234e13);
|
||||
check!(2.71828E0, 2.71828);
|
||||
check!(1.1E32, 1.1e32);
|
||||
check!(1.1E-32, 1.1e-32);
|
||||
check!(2.7182817E0, 2.7182817);
|
||||
check!(1E-45, 1e-45);
|
||||
check!(3.4028235E38, 3.4028235e38);
|
||||
check!(-1.234E-3, -0.001234);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_random() {
|
||||
let mut bytes = [0u8; 24];
|
||||
let mut buffer = ryu::Buffer::new();
|
||||
for _ in 0..1000000 {
|
||||
let f = rand::random();
|
||||
let n = unsafe { ryu::raw::f2s_buffered_n(f, &mut bytes[0]) };
|
||||
assert_eq!(f, str::from_utf8(&bytes[..n]).unwrap().parse().unwrap());
|
||||
assert_eq!(f, buffer.format(f).parse().unwrap());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_non_finite() {
|
||||
for i in 0u32..1 << 23 {
|
||||
let f = f32::from_bits((((1 << 8) - 1) << 23) + i);
|
||||
assert!(!f.is_finite(), "f={}", f);
|
||||
ryu::Buffer::new().format(f);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
check!(0E0, 0.0);
|
||||
check!(-0E0, -0.0);
|
||||
check!(1E0, 1.0);
|
||||
check!(-1E0, -1.0);
|
||||
assert_eq!(print(f32::NAN), "NaN");
|
||||
assert_eq!(print(f32::INFINITY), "Infinity");
|
||||
assert_eq!(print(f32::NEG_INFINITY), "-Infinity");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch_to_subnormal() {
|
||||
check!(1.1754944E-38, 1.1754944e-38);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_min_and_max() {
|
||||
assert_eq!(f32::from_bits(0x7f7fffff), 3.4028235e38);
|
||||
check!(3.4028235E38, 3.4028235e38);
|
||||
assert_eq!(f32::from_bits(1), 1e-45);
|
||||
check!(1E-45, 1e-45);
|
||||
}
|
||||
|
||||
// Check that we return the exact boundary if it is the shortest
|
||||
// representation, but only if the original floating point number is even.
|
||||
#[test]
|
||||
fn test_boundary_round_even() {
|
||||
check!(3.355445E7, 33554450.0);
|
||||
check!(9E9, 9000000000.0);
|
||||
check!(3.436672E10, 34366720000.0);
|
||||
}
|
||||
|
||||
// If the exact value is exactly halfway between two shortest representations,
|
||||
// then we round to even. It seems like this only makes a difference if the
|
||||
// last two digits are ...2|5 or ...7|5, and we cut off the 5.
|
||||
#[test]
|
||||
fn test_exact_value_round_even() {
|
||||
check!(3.0540412E5, 305404.12);
|
||||
check!(8.0990312E3, 8099.0312);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lots_of_trailing_zeros() {
|
||||
// Pattern for the first test: 00111001100000000000000000000000
|
||||
check!(2.4414062E-4, 0.00024414062);
|
||||
check!(2.4414062E-3, 0.0024414062);
|
||||
check!(4.3945312E-3, 0.0043945312);
|
||||
check!(6.3476562E-3, 0.0063476562);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_regression() {
|
||||
check!(4.7223665E21, 4.7223665e21);
|
||||
check!(8.388608E6, 8388608.0);
|
||||
check!(1.6777216E7, 16777216.0);
|
||||
check!(3.3554436E7, 33554436.0);
|
||||
check!(6.7131496E7, 67131496.0);
|
||||
check!(1.9310392E-38, 1.9310392e-38);
|
||||
check!(-2.47E-43, -2.47e-43);
|
||||
check!(1.993244E-38, 1.993244e-38);
|
||||
check!(4.1039004E3, 4103.9004);
|
||||
check!(5.3399997E9, 5339999700.0);
|
||||
check!(6.0898E-39, 6.0898e-39);
|
||||
check!(1.0310042E-3, 0.0010310042);
|
||||
check!(2.882326E17, 2.882326e17);
|
||||
check!(7.038531E-26, 7.038531e-26);
|
||||
check!(9.223404E17, 9.223404e17);
|
||||
check!(6.710887E7, 67108870.0);
|
||||
check!(1E-44, 1e-44);
|
||||
check!(2.816025E14, 2.816025e14);
|
||||
check!(9.223372E18, 9.223372e18);
|
||||
check!(1.5846086E29, 1.5846086e29);
|
||||
check!(1.1811161E19, 1.1811161e19);
|
||||
check!(5.368709E18, 5.368709e18);
|
||||
check!(4.6143166E18, 4.6143166e18);
|
||||
check!(7.812537E-3, 0.007812537);
|
||||
check!(1E-45, 1e-45);
|
||||
check!(1.18697725E20, 1.18697725e20);
|
||||
check!(1.00014165E-36, 1.00014165e-36);
|
||||
check!(2E2, 200.0);
|
||||
check!(3.3554432E7, 33554432.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_looks_like_pow5() {
|
||||
// These numbers have a mantissa that is the largest power of 5 that fits,
|
||||
// and an exponent that causes the computation for q to result in 10, which
|
||||
// is a corner case for Ryu.
|
||||
assert_eq!(f32::from_bits(0x5D1502F9), 6.7108864e17);
|
||||
check!(6.7108864E17, 6.7108864e17);
|
||||
assert_eq!(f32::from_bits(0x5D9502F9), 1.3421773e18);
|
||||
check!(1.3421773E18, 1.3421773e18);
|
||||
assert_eq!(f32::from_bits(0x5E1502F9), 2.6843546e18);
|
||||
check!(2.6843546E18, 2.6843546e18);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_output_length() {
|
||||
check!(1E0, 1.0); // already tested in Basic
|
||||
check!(1.2E0, 1.2);
|
||||
check!(1.23E0, 1.23);
|
||||
check!(1.234E0, 1.234);
|
||||
check!(1.2345E0, 1.2345);
|
||||
check!(1.23456E0, 1.23456);
|
||||
check!(1.234567E0, 1.234567);
|
||||
check!(1.2345678E0, 1.2345678);
|
||||
check!(1.23456735E-36, 1.23456735e-36);
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
macro_rules! check {
|
||||
($ryu:tt, $pretty:tt) => {
|
||||
assert_eq!($ryu, $pretty);
|
||||
assert_eq!(print($ryu), stringify!($ryu));
|
||||
assert_eq!(pretty($pretty), stringify!($pretty));
|
||||
};
|
||||
(-$ryu:tt, -$pretty:tt) => {
|
||||
assert_eq!(-$ryu, -$pretty);
|
||||
assert_eq!(print(-$ryu), concat!("-", stringify!($ryu)));
|
||||
assert_eq!(pretty(-$pretty), concat!("-", stringify!($pretty)));
|
||||
};
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"files":{"Cargo.toml":"0265066945da4da5957f3446cef6f2a52e81254be3a9c4e678cc2c1e1160118b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"6485b8ed310d3f0340bf1ad1f47645069ce4069dcc6bb46c7d5c6faf41de1fdb","README.md":"c4d8690cd2aa2db200ecdf2f2113fc8e3f4ccc88f3dc96da22189f45136932d0","src/de.rs":"42d7aa8cf85baa1b3851180e32af883c11ca2889df6f4eb306670f360af46d3e","src/error.rs":"8ac6d4c861891c133b3359b2a14fccb8a6d463a4b2c13f0d658884c1972186ce","src/iter.rs":"b5c77c5482e45bed1e63bd36a0f991a08f732811546a163228be48303e7f1b4d","src/lib.rs":"698af0adc583d8b5a22f510bb33fbdb35552dd6e0775e038f7ed50f6680580d8","src/macros.rs":"0d9850832f53c8aca337395b2536f1cdaf2d2d2699adc09c9a2001544106a4fc","src/map.rs":"724205f934003c879fb3e48a84b21c54e273e968beec97b85617042911ca88d7","src/number.rs":"47bf5416ca4f8299e98f008bd1bcf7e7311f00d0ce282536a17310fa73788def","src/read.rs":"832bc530dd35ee24df59c1cf648cfbbffc4c2e72e86104b0e186582ae0de545a","src/ser.rs":"d2050d362c23e988141e6c5d08590152b3412ee3464e1a3026b83dda7de0c985","src/value/de.rs":"b25d9192f179f84c0ecb2226216ff663c39ebdde524179f7118718f3202243cf","src/value/from.rs":"13a6c7b0b327f23c2fd899c8390e8ddfd8907bfcfc362e80834b9ae7ddb698e9","src/value/index.rs":"b9a8cc6f37e2a079a5e4ab1bf25fa9ff076c514870e0756a0c88af3ffa0f51a8","src/value/mod.rs":"8d0ac56fcd12aee1c645afdfe0df62b10527cab21d16284fe9f0bd6768457002","src/value/partial_eq.rs":"5924e245408afc8075e2bb9dda479c938c6c8cdd3c18b54697aa2b33fffda57b","src/value/ser.rs":"4bbc3ad0f646464f1e9b9a2c2e48df799d02ef658eacadc35971b897cfe7cd02"},"package":"44dd2cfde475037451fa99b7e5df77aa3cfd1536575fa8e7a538ab36dcde49ae"}
|
|
@ -1,54 +0,0 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g. crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
name = "serde_json"
|
||||
version = "1.0.26"
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
description = "A JSON serialization file format"
|
||||
documentation = "http://docs.serde.rs/serde_json/"
|
||||
readme = "README.md"
|
||||
keywords = ["json", "serde", "serialization"]
|
||||
categories = ["encoding"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/serde-rs/json"
|
||||
[dependencies.indexmap]
|
||||
version = "1.0"
|
||||
optional = true
|
||||
|
||||
[dependencies.itoa]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.ryu]
|
||||
version = "0.2"
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1.0.60"
|
||||
[dev-dependencies.compiletest_rs]
|
||||
version = "0.3"
|
||||
|
||||
[dev-dependencies.serde_bytes]
|
||||
version = "0.10"
|
||||
|
||||
[dev-dependencies.serde_derive]
|
||||
version = "1.0"
|
||||
|
||||
[features]
|
||||
arbitrary_precision = []
|
||||
default = []
|
||||
preserve_order = ["indexmap"]
|
||||
[badges.appveyor]
|
||||
repository = "serde-rs/json"
|
||||
|
||||
[badges.travis-ci]
|
||||
repository = "serde-rs/json"
|
|
@ -1,365 +0,0 @@
|
|||
# Serde JSON   [![Build Status]][travis] [![Latest Version]][crates.io] [![Rustc Version 1.15+]][rustc]
|
||||
|
||||
[Build Status]: https://api.travis-ci.org/serde-rs/json.svg?branch=master
|
||||
[travis]: https://travis-ci.org/serde-rs/json
|
||||
[Latest Version]: https://img.shields.io/crates/v/serde_json.svg
|
||||
[crates.io]: https://crates.io/crates/serde\_json
|
||||
[Rustc Version 1.15+]: https://img.shields.io/badge/rustc-1.15+-lightgray.svg
|
||||
[rustc]: https://blog.rust-lang.org/2017/02/02/Rust-1.15.html
|
||||
|
||||
**Serde is a framework for *ser*ializing and *de*serializing Rust data structures efficiently and generically.**
|
||||
|
||||
---
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
serde_json = "1.0"
|
||||
```
|
||||
|
||||
You may be looking for:
|
||||
|
||||
- [JSON API documentation](https://docs.serde.rs/serde_json/)
|
||||
- [Serde API documentation](https://docs.serde.rs/serde/)
|
||||
- [Detailed documentation about Serde](https://serde.rs/)
|
||||
- [Setting up `#[derive(Serialize, Deserialize)]`](https://serde.rs/codegen.html)
|
||||
- [Release notes](https://github.com/serde-rs/json/releases)
|
||||
|
||||
JSON is a ubiquitous open-standard format that uses human-readable text to
|
||||
transmit data objects consisting of key-value pairs.
|
||||
|
||||
```json,ignore
|
||||
{
|
||||
"name": "John Doe",
|
||||
"age": 43,
|
||||
"address": {
|
||||
"street": "10 Downing Street",
|
||||
"city": "London"
|
||||
},
|
||||
"phones": [
|
||||
"+44 1234567",
|
||||
"+44 2345678"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
There are three common ways that you might find yourself needing to work
|
||||
with JSON data in Rust.
|
||||
|
||||
- **As text data.** An unprocessed string of JSON data that you receive on
|
||||
an HTTP endpoint, read from a file, or prepare to send to a remote
|
||||
server.
|
||||
- **As an untyped or loosely typed representation.** Maybe you want to
|
||||
check that some JSON data is valid before passing it on, but without
|
||||
knowing the structure of what it contains. Or you want to do very basic
|
||||
manipulations like insert a key in a particular spot.
|
||||
- **As a strongly typed Rust data structure.** When you expect all or most
|
||||
of your data to conform to a particular structure and want to get real
|
||||
work done without JSON's loosey-goosey nature tripping you up.
|
||||
|
||||
Serde JSON provides efficient, flexible, safe ways of converting data
|
||||
between each of these representations.
|
||||
|
||||
## Operating on untyped JSON values
|
||||
|
||||
Any valid JSON data can be manipulated in the following recursive enum
|
||||
representation. This data structure is [`serde_json::Value`][value].
|
||||
|
||||
```rust,ignore
|
||||
enum Value {
|
||||
Null,
|
||||
Bool(bool),
|
||||
Number(Number),
|
||||
String(String),
|
||||
Array(Vec<Value>),
|
||||
Object(Map<String, Value>),
|
||||
}
|
||||
```
|
||||
|
||||
A string of JSON data can be parsed into a `serde_json::Value` by the
|
||||
[`serde_json::from_str`][from_str] function. There is also
|
||||
[`from_slice`][from_slice] for parsing from a byte slice &[u8] and
|
||||
[`from_reader`][from_reader] for parsing from any `io::Read` like a File or
|
||||
a TCP stream.
|
||||
|
||||
<a href="http://play.integer32.com/?gist=a266662bc71712e080efbf25ce30f306" target="_blank">
|
||||
<img align="right" width="50" src="https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/img/run.png">
|
||||
</a>
|
||||
|
||||
```rust
|
||||
extern crate serde_json;
|
||||
|
||||
use serde_json::{Value, Error};
|
||||
|
||||
fn untyped_example() -> Result<(), Error> {
|
||||
// Some JSON input data as a &str. Maybe this comes from the user.
|
||||
let data = r#"{
|
||||
"name": "John Doe",
|
||||
"age": 43,
|
||||
"phones": [
|
||||
"+44 1234567",
|
||||
"+44 2345678"
|
||||
]
|
||||
}"#;
|
||||
|
||||
// Parse the string of data into serde_json::Value.
|
||||
let v: Value = serde_json::from_str(data)?;
|
||||
|
||||
// Access parts of the data by indexing with square brackets.
|
||||
println!("Please call {} at the number {}", v["name"], v["phones"][0]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
The result of square bracket indexing like `v["name"]` is a borrow of the data
|
||||
at that index, so the type is `&Value`. A JSON map can be indexed with string
|
||||
keys, while a JSON array can be indexed with integer keys. If the type of the
|
||||
data is not right for the type with which it is being indexed, or if a map does
|
||||
not contain the key being indexed, or if the index into a vector is out of
|
||||
bounds, the returned element is `Value::Null`.
|
||||
|
||||
When a `Value` is printed, it is printed as a JSON string. So in the code above,
|
||||
the output looks like `Please call "John Doe" at the number "+44 1234567"`. The
|
||||
quotation marks appear because `v["name"]` is a `&Value` containing a JSON
|
||||
string and its JSON representation is `"John Doe"`. Printing as a plain string
|
||||
without quotation marks involves converting from a JSON string to a Rust string
|
||||
with [`as_str()`] or avoiding the use of `Value` as described in the following
|
||||
section.
|
||||
|
||||
[`as_str()`]: https://docs.serde.rs/serde_json/enum.Value.html#method.as_str
|
||||
|
||||
The `Value` representation is sufficient for very basic tasks but can be tedious
|
||||
to work with for anything more significant. Error handling is verbose to
|
||||
implement correctly, for example imagine trying to detect the presence of
|
||||
unrecognized fields in the input data. The compiler is powerless to help you
|
||||
when you make a mistake, for example imagine typoing `v["name"]` as `v["nmae"]`
|
||||
in one of the dozens of places it is used in your code.
|
||||
|
||||
## Parsing JSON as strongly typed data structures
|
||||
|
||||
Serde provides a powerful way of mapping JSON data into Rust data structures
|
||||
largely automatically.
|
||||
|
||||
<a href="http://play.integer32.com/?gist=cff572b80d3f078c942a2151e6020adc" target="_blank">
|
||||
<img align="right" width="50" src="https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/img/run.png">
|
||||
</a>
|
||||
|
||||
```rust
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use serde_json::Error;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Person {
|
||||
name: String,
|
||||
age: u8,
|
||||
phones: Vec<String>,
|
||||
}
|
||||
|
||||
fn typed_example() -> Result<(), Error> {
|
||||
// Some JSON input data as a &str. Maybe this comes from the user.
|
||||
let data = r#"{
|
||||
"name": "John Doe",
|
||||
"age": 43,
|
||||
"phones": [
|
||||
"+44 1234567",
|
||||
"+44 2345678"
|
||||
]
|
||||
}"#;
|
||||
|
||||
// Parse the string of data into a Person object. This is exactly the
|
||||
// same function as the one that produced serde_json::Value above, but
|
||||
// now we are asking it for a Person as output.
|
||||
let p: Person = serde_json::from_str(data)?;
|
||||
|
||||
// Do things just like with any other Rust data structure.
|
||||
println!("Please call {} at the number {}", p.name, p.phones[0]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
This is the same `serde_json::from_str` function as before, but this time we
|
||||
assign the return value to a variable of type `Person` so Serde will
|
||||
automatically interpret the input data as a `Person` and produce informative
|
||||
error messages if the layout does not conform to what a `Person` is expected
|
||||
to look like.
|
||||
|
||||
Any type that implements Serde's `Deserialize` trait can be deserialized
|
||||
this way. This includes built-in Rust standard library types like `Vec<T>`
|
||||
and `HashMap<K, V>`, as well as any structs or enums annotated with
|
||||
`#[derive(Deserialize)]`.
|
||||
|
||||
Once we have `p` of type `Person`, our IDE and the Rust compiler can help us
|
||||
use it correctly like they do for any other Rust code. The IDE can
|
||||
autocomplete field names to prevent typos, which was impossible in the
|
||||
`serde_json::Value` representation. And the Rust compiler can check that
|
||||
when we write `p.phones[0]`, then `p.phones` is guaranteed to be a
|
||||
`Vec<String>` so indexing into it makes sense and produces a `String`.
|
||||
|
||||
## Constructing JSON values
|
||||
|
||||
Serde JSON provides a [`json!` macro][macro] to build `serde_json::Value`
|
||||
objects with very natural JSON syntax. In order to use this macro,
|
||||
`serde_json` needs to be imported with the `#[macro_use]` attribute.
|
||||
|
||||
<a href="http://play.integer32.com/?gist=c216d6beabd9429a6ac13b8f88938dfe" target="_blank">
|
||||
<img align="right" width="50" src="https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/img/run.png">
|
||||
</a>
|
||||
|
||||
```rust
|
||||
#[macro_use]
|
||||
extern crate serde_json;
|
||||
|
||||
fn main() {
|
||||
// The type of `john` is `serde_json::Value`
|
||||
let john = json!({
|
||||
"name": "John Doe",
|
||||
"age": 43,
|
||||
"phones": [
|
||||
"+44 1234567",
|
||||
"+44 2345678"
|
||||
]
|
||||
});
|
||||
|
||||
println!("first phone number: {}", john["phones"][0]);
|
||||
|
||||
// Convert to a string of JSON and print it out
|
||||
println!("{}", john.to_string());
|
||||
}
|
||||
```
|
||||
|
||||
The `Value::to_string()` function converts a `serde_json::Value` into a
|
||||
`String` of JSON text.
|
||||
|
||||
One neat thing about the `json!` macro is that variables and expressions can
|
||||
be interpolated directly into the JSON value as you are building it. Serde
|
||||
will check at compile time that the value you are interpolating is able to
|
||||
be represented as JSON.
|
||||
|
||||
<a href="http://play.integer32.com/?gist=aae3af4d274bd249d1c8a947076355f2" target="_blank">
|
||||
<img align="right" width="50" src="https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/img/run.png">
|
||||
</a>
|
||||
|
||||
```rust
|
||||
let full_name = "John Doe";
|
||||
let age_last_year = 42;
|
||||
|
||||
// The type of `john` is `serde_json::Value`
|
||||
let john = json!({
|
||||
"name": full_name,
|
||||
"age": age_last_year + 1,
|
||||
"phones": [
|
||||
format!("+44 {}", random_phone())
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
This is amazingly convenient but we have the problem we had before with
|
||||
`Value` which is that the IDE and Rust compiler cannot help us if we get it
|
||||
wrong. Serde JSON provides a better way of serializing strongly-typed data
|
||||
structures into JSON text.
|
||||
|
||||
## Creating JSON by serializing data structures
|
||||
|
||||
A data structure can be converted to a JSON string by
|
||||
[`serde_json::to_string`][to_string]. There is also
|
||||
[`serde_json::to_vec`][to_vec] which serializes to a `Vec<u8>` and
|
||||
[`serde_json::to_writer`][to_writer] which serializes to any `io::Write`
|
||||
such as a File or a TCP stream.
|
||||
|
||||
<a href="http://play.integer32.com/?gist=40967ece79921c77fd78ebc8f177c063" target="_blank">
|
||||
<img align="right" width="50" src="https://raw.githubusercontent.com/serde-rs/serde-rs.github.io/master/img/run.png">
|
||||
</a>
|
||||
|
||||
```rust
|
||||
extern crate serde;
|
||||
extern crate serde_json;
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
use serde_json::Error;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct Address {
|
||||
street: String,
|
||||
city: String,
|
||||
}
|
||||
|
||||
fn print_an_address() -> Result<(), Error> {
|
||||
// Some data structure.
|
||||
let address = Address {
|
||||
street: "10 Downing Street".to_owned(),
|
||||
city: "London".to_owned(),
|
||||
};
|
||||
|
||||
// Serialize it to a JSON string.
|
||||
let j = serde_json::to_string(&address)?;
|
||||
|
||||
// Print, write to a file, or send to an HTTP server.
|
||||
println!("{}", j);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
Any type that implements Serde's `Serialize` trait can be serialized this
|
||||
way. This includes built-in Rust standard library types like `Vec<T>` and
|
||||
`HashMap<K, V>`, as well as any structs or enums annotated with
|
||||
`#[derive(Serialize)]`.
|
||||
|
||||
## Performance
|
||||
|
||||
It is fast. You should expect in the ballpark of 500 to 1000 megabytes per
|
||||
second deserialization and 600 to 900 megabytes per second serialization,
|
||||
depending on the characteristics of your data. This is competitive with the
|
||||
fastest C and C++ JSON libraries or even 30% faster for many use cases.
|
||||
Benchmarks live in the [serde-rs/json-benchmark] repo.
|
||||
|
||||
[serde-rs/json-benchmark]: https://github.com/serde-rs/json-benchmark
|
||||
|
||||
## Getting help
|
||||
|
||||
Serde developers live in the #serde channel on
|
||||
[`irc.mozilla.org`](https://wiki.mozilla.org/IRC). The #rust channel is also a
|
||||
good resource with generally faster response time but less specific knowledge
|
||||
about Serde. If IRC is not your thing, we are happy to respond to [GitHub
|
||||
issues](https://github.com/serde-rs/json/issues/new) as well.
|
||||
|
||||
## No-std support
|
||||
|
||||
This crate currently requires the Rust standard library. For JSON support in
|
||||
Serde without a standard library, please see the [`serde-json-core`] crate.
|
||||
|
||||
[`serde-json-core`]: https://japaric.github.io/serde-json-core/serde_json_core/
|
||||
|
||||
## License
|
||||
|
||||
Serde JSON is licensed under either of
|
||||
|
||||
* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
||||
http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
||||
http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Serde JSON by you, as defined in the Apache-2.0 license, shall
|
||||
be dual licensed as above, without any additional terms or conditions.
|
||||
|
||||
[value]: https://docs.serde.rs/serde_json/value/enum.Value.html
|
||||
[from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html
|
||||
[from_slice]: https://docs.serde.rs/serde_json/de/fn.from_slice.html
|
||||
[from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html
|
||||
[to_string]: https://docs.serde.rs/serde_json/ser/fn.to_string.html
|
||||
[to_vec]: https://docs.serde.rs/serde_json/ser/fn.to_vec.html
|
||||
[to_writer]: https://docs.serde.rs/serde_json/ser/fn.to_writer.html
|
||||
[macro]: https://docs.serde.rs/serde_json/macro.json.html
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,429 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! When serializing or deserializing JSON goes wrong.
|
||||
|
||||
use std::error;
|
||||
use std::fmt::{self, Debug, Display};
|
||||
use std::io;
|
||||
use std::result;
|
||||
|
||||
use serde::de;
|
||||
use serde::ser;
|
||||
|
||||
/// This type represents all possible errors that can occur when serializing or
|
||||
/// deserializing JSON data.
|
||||
pub struct Error {
|
||||
/// This `Box` allows us to keep the size of `Error` as small as possible. A
|
||||
/// larger `Error` type was substantially slower due to all the functions
|
||||
/// that pass around `Result<T, Error>`.
|
||||
err: Box<ErrorImpl>,
|
||||
}
|
||||
|
||||
/// Alias for a `Result` with the error type `serde_json::Error`.
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
impl Error {
|
||||
/// One-based line number at which the error was detected.
|
||||
///
|
||||
/// Characters in the first line of the input (before the first newline
|
||||
/// character) are in line 1.
|
||||
pub fn line(&self) -> usize {
|
||||
self.err.line
|
||||
}
|
||||
|
||||
/// One-based column number at which the error was detected.
|
||||
///
|
||||
/// The first character in the input and any characters immediately
|
||||
/// following a newline character are in column 1.
|
||||
///
|
||||
/// Note that errors may occur in column 0, for example if a read from an IO
|
||||
/// stream fails immediately following a previously read newline character.
|
||||
pub fn column(&self) -> usize {
|
||||
self.err.column
|
||||
}
|
||||
|
||||
/// Categorizes the cause of this error.
|
||||
///
|
||||
/// - `Category::Io` - failure to read or write bytes on an IO stream
|
||||
/// - `Category::Syntax` - input that is not syntactically valid JSON
|
||||
/// - `Category::Data` - input data that is semantically incorrect
|
||||
/// - `Category::Eof` - unexpected end of the input data
|
||||
pub fn classify(&self) -> Category {
|
||||
match self.err.code {
|
||||
ErrorCode::Message(_) => Category::Data,
|
||||
ErrorCode::Io(_) => Category::Io,
|
||||
ErrorCode::EofWhileParsingList
|
||||
| ErrorCode::EofWhileParsingObject
|
||||
| ErrorCode::EofWhileParsingString
|
||||
| ErrorCode::EofWhileParsingValue => Category::Eof,
|
||||
ErrorCode::ExpectedColon
|
||||
| ErrorCode::ExpectedListCommaOrEnd
|
||||
| ErrorCode::ExpectedObjectCommaOrEnd
|
||||
| ErrorCode::ExpectedObjectOrArray
|
||||
| ErrorCode::ExpectedSomeIdent
|
||||
| ErrorCode::ExpectedSomeValue
|
||||
| ErrorCode::ExpectedSomeString
|
||||
| ErrorCode::InvalidEscape
|
||||
| ErrorCode::InvalidNumber
|
||||
| ErrorCode::NumberOutOfRange
|
||||
| ErrorCode::InvalidUnicodeCodePoint
|
||||
| ErrorCode::ControlCharacterWhileParsingString
|
||||
| ErrorCode::KeyMustBeAString
|
||||
| ErrorCode::LoneLeadingSurrogateInHexEscape
|
||||
| ErrorCode::TrailingComma
|
||||
| ErrorCode::TrailingCharacters
|
||||
| ErrorCode::UnexpectedEndOfHexEscape
|
||||
| ErrorCode::RecursionLimitExceeded => Category::Syntax,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if this error was caused by a failure to read or write
|
||||
/// bytes on an IO stream.
|
||||
pub fn is_io(&self) -> bool {
|
||||
self.classify() == Category::Io
|
||||
}
|
||||
|
||||
/// Returns true if this error was caused by input that was not
|
||||
/// syntactically valid JSON.
|
||||
pub fn is_syntax(&self) -> bool {
|
||||
self.classify() == Category::Syntax
|
||||
}
|
||||
|
||||
/// Returns true if this error was caused by input data that was
|
||||
/// semantically incorrect.
|
||||
///
|
||||
/// For example, JSON containing a number is semantically incorrect when the
|
||||
/// type being deserialized into holds a String.
|
||||
pub fn is_data(&self) -> bool {
|
||||
self.classify() == Category::Data
|
||||
}
|
||||
|
||||
/// Returns true if this error was caused by prematurely reaching the end of
|
||||
/// the input data.
|
||||
///
|
||||
/// Callers that process streaming input may be interested in retrying the
|
||||
/// deserialization once more data is available.
|
||||
pub fn is_eof(&self) -> bool {
|
||||
self.classify() == Category::Eof
|
||||
}
|
||||
}
|
||||
|
||||
/// Categorizes the cause of a `serde_json::Error`.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||
pub enum Category {
|
||||
/// The error was caused by a failure to read or write bytes on an IO
|
||||
/// stream.
|
||||
Io,
|
||||
|
||||
/// The error was caused by input that was not syntactically valid JSON.
|
||||
Syntax,
|
||||
|
||||
/// The error was caused by input data that was semantically incorrect.
|
||||
///
|
||||
/// For example, JSON containing a number is semantically incorrect when the
|
||||
/// type being deserialized into holds a String.
|
||||
Data,
|
||||
|
||||
/// The error was caused by prematurely reaching the end of the input data.
|
||||
///
|
||||
/// Callers that process streaming input may be interested in retrying the
|
||||
/// deserialization once more data is available.
|
||||
Eof,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "cargo-clippy", allow(fallible_impl_from))]
|
||||
impl From<Error> for io::Error {
|
||||
/// Convert a `serde_json::Error` into an `io::Error`.
|
||||
///
|
||||
/// JSON syntax and data errors are turned into `InvalidData` IO errors.
|
||||
/// EOF errors are turned into `UnexpectedEof` IO errors.
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io;
|
||||
///
|
||||
/// enum MyError {
|
||||
/// Io(io::Error),
|
||||
/// Json(serde_json::Error),
|
||||
/// }
|
||||
///
|
||||
/// impl From<serde_json::Error> for MyError {
|
||||
/// fn from(err: serde_json::Error) -> MyError {
|
||||
/// use serde_json::error::Category;
|
||||
/// match err.classify() {
|
||||
/// Category::Io => {
|
||||
/// MyError::Io(err.into())
|
||||
/// }
|
||||
/// Category::Syntax | Category::Data | Category::Eof => {
|
||||
/// MyError::Json(err)
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
fn from(j: Error) -> Self {
|
||||
if let ErrorCode::Io(err) = j.err.code {
|
||||
err
|
||||
} else {
|
||||
match j.classify() {
|
||||
Category::Io => unreachable!(),
|
||||
Category::Syntax | Category::Data => io::Error::new(io::ErrorKind::InvalidData, j),
|
||||
Category::Eof => io::Error::new(io::ErrorKind::UnexpectedEof, j),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ErrorImpl {
|
||||
code: ErrorCode,
|
||||
line: usize,
|
||||
column: usize,
|
||||
}
|
||||
|
||||
// Not public API. Should be pub(crate).
|
||||
#[doc(hidden)]
|
||||
pub enum ErrorCode {
|
||||
/// Catchall for syntax error messages
|
||||
Message(Box<str>),
|
||||
|
||||
/// Some IO error occurred while serializing or deserializing.
|
||||
Io(io::Error),
|
||||
|
||||
/// EOF while parsing a list.
|
||||
EofWhileParsingList,
|
||||
|
||||
/// EOF while parsing an object.
|
||||
EofWhileParsingObject,
|
||||
|
||||
/// EOF while parsing a string.
|
||||
EofWhileParsingString,
|
||||
|
||||
/// EOF while parsing a JSON value.
|
||||
EofWhileParsingValue,
|
||||
|
||||
/// Expected this character to be a `':'`.
|
||||
ExpectedColon,
|
||||
|
||||
/// Expected this character to be either a `','` or a `']'`.
|
||||
ExpectedListCommaOrEnd,
|
||||
|
||||
/// Expected this character to be either a `','` or a `'}'`.
|
||||
ExpectedObjectCommaOrEnd,
|
||||
|
||||
/// Expected this character to be either a `'{'` or a `'['`.
|
||||
ExpectedObjectOrArray,
|
||||
|
||||
/// Expected to parse either a `true`, `false`, or a `null`.
|
||||
ExpectedSomeIdent,
|
||||
|
||||
/// Expected this character to start a JSON value.
|
||||
ExpectedSomeValue,
|
||||
|
||||
/// Expected this character to start a JSON string.
|
||||
ExpectedSomeString,
|
||||
|
||||
/// Invalid hex escape code.
|
||||
InvalidEscape,
|
||||
|
||||
/// Invalid number.
|
||||
InvalidNumber,
|
||||
|
||||
/// Number is bigger than the maximum value of its type.
|
||||
NumberOutOfRange,
|
||||
|
||||
/// Invalid unicode code point.
|
||||
InvalidUnicodeCodePoint,
|
||||
|
||||
/// Control character found while parsing a string.
|
||||
ControlCharacterWhileParsingString,
|
||||
|
||||
/// Object key is not a string.
|
||||
KeyMustBeAString,
|
||||
|
||||
/// Lone leading surrogate in hex escape.
|
||||
LoneLeadingSurrogateInHexEscape,
|
||||
|
||||
/// JSON has a comma after the last value in an array or map.
|
||||
TrailingComma,
|
||||
|
||||
/// JSON has non-whitespace trailing characters after the value.
|
||||
TrailingCharacters,
|
||||
|
||||
/// Unexpected end of hex excape.
|
||||
UnexpectedEndOfHexEscape,
|
||||
|
||||
/// Encountered nesting of JSON maps and arrays more than 128 layers deep.
|
||||
RecursionLimitExceeded,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
// Not public API. Should be pub(crate).
|
||||
#[doc(hidden)]
|
||||
#[cold]
|
||||
pub fn syntax(code: ErrorCode, line: usize, column: usize) -> Self {
|
||||
Error {
|
||||
err: Box::new(ErrorImpl {
|
||||
code: code,
|
||||
line: line,
|
||||
column: column,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Not public API. Should be pub(crate).
|
||||
//
|
||||
// Update `eager_json` crate when this function changes.
|
||||
#[doc(hidden)]
|
||||
#[cold]
|
||||
pub fn io(error: io::Error) -> Self {
|
||||
Error {
|
||||
err: Box::new(ErrorImpl {
|
||||
code: ErrorCode::Io(error),
|
||||
line: 0,
|
||||
column: 0,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Not public API. Should be pub(crate).
|
||||
#[doc(hidden)]
|
||||
#[cold]
|
||||
pub fn fix_position<F>(self, f: F) -> Self
|
||||
where
|
||||
F: FnOnce(ErrorCode) -> Error,
|
||||
{
|
||||
if self.err.line == 0 {
|
||||
f(self.err.code)
|
||||
} else {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ErrorCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
ErrorCode::Message(ref msg) => f.write_str(msg),
|
||||
ErrorCode::Io(ref err) => Display::fmt(err, f),
|
||||
ErrorCode::EofWhileParsingList => f.write_str("EOF while parsing a list"),
|
||||
ErrorCode::EofWhileParsingObject => f.write_str("EOF while parsing an object"),
|
||||
ErrorCode::EofWhileParsingString => f.write_str("EOF while parsing a string"),
|
||||
ErrorCode::EofWhileParsingValue => f.write_str("EOF while parsing a value"),
|
||||
ErrorCode::ExpectedColon => f.write_str("expected `:`"),
|
||||
ErrorCode::ExpectedListCommaOrEnd => f.write_str("expected `,` or `]`"),
|
||||
ErrorCode::ExpectedObjectCommaOrEnd => f.write_str("expected `,` or `}`"),
|
||||
ErrorCode::ExpectedObjectOrArray => f.write_str("expected `{` or `[`"),
|
||||
ErrorCode::ExpectedSomeIdent => f.write_str("expected ident"),
|
||||
ErrorCode::ExpectedSomeValue => f.write_str("expected value"),
|
||||
ErrorCode::ExpectedSomeString => f.write_str("expected string"),
|
||||
ErrorCode::InvalidEscape => f.write_str("invalid escape"),
|
||||
ErrorCode::InvalidNumber => f.write_str("invalid number"),
|
||||
ErrorCode::NumberOutOfRange => f.write_str("number out of range"),
|
||||
ErrorCode::InvalidUnicodeCodePoint => f.write_str("invalid unicode code point"),
|
||||
ErrorCode::ControlCharacterWhileParsingString => {
|
||||
f.write_str("control character (\\u0000-\\u001F) found while parsing a string")
|
||||
}
|
||||
ErrorCode::KeyMustBeAString => f.write_str("key must be a string"),
|
||||
ErrorCode::LoneLeadingSurrogateInHexEscape => {
|
||||
f.write_str("lone leading surrogate in hex escape")
|
||||
}
|
||||
ErrorCode::TrailingComma => f.write_str("trailing comma"),
|
||||
ErrorCode::TrailingCharacters => f.write_str("trailing characters"),
|
||||
ErrorCode::UnexpectedEndOfHexEscape => f.write_str("unexpected end of hex escape"),
|
||||
ErrorCode::RecursionLimitExceeded => f.write_str("recursion limit exceeded"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
fn description(&self) -> &str {
|
||||
match self.err.code {
|
||||
ErrorCode::Io(ref err) => error::Error::description(err),
|
||||
_ => {
|
||||
// If you want a better message, use Display::fmt or to_string().
|
||||
"JSON error"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn cause(&self) -> Option<&error::Error> {
|
||||
match self.err.code {
|
||||
ErrorCode::Io(ref err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt(&*self.err, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ErrorImpl {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
if self.line == 0 {
|
||||
Display::fmt(&self.code, f)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"{} at line {} column {}",
|
||||
self.code, self.line, self.column
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove two layers of verbosity from the debug representation. Humans often
|
||||
// end up seeing this representation because it is what unwrap() shows.
|
||||
impl Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Error({:?}, line: {}, column: {})",
|
||||
self.err.code.to_string(),
|
||||
self.err.line,
|
||||
self.err.column
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl de::Error for Error {
|
||||
#[cold]
|
||||
fn custom<T: Display>(msg: T) -> Error {
|
||||
Error {
|
||||
err: Box::new(ErrorImpl {
|
||||
code: ErrorCode::Message(msg.to_string().into_boxed_str()),
|
||||
line: 0,
|
||||
column: 0,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn invalid_type(unexp: de::Unexpected, exp: &de::Expected) -> Self {
|
||||
if let de::Unexpected::Unit = unexp {
|
||||
Error::custom(format_args!("invalid type: null, expected {}", exp))
|
||||
} else {
|
||||
Error::custom(format_args!("invalid type: {}, expected {}", unexp, exp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Error for Error {
|
||||
#[cold]
|
||||
fn custom<T: Display>(msg: T) -> Error {
|
||||
Error {
|
||||
err: Box::new(ErrorImpl {
|
||||
code: ErrorCode::Message(msg.to_string().into_boxed_str()),
|
||||
line: 0,
|
||||
column: 0,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::io;
|
||||
|
||||
pub struct LineColIterator<I> {
|
||||
iter: I,
|
||||
|
||||
/// Index of the current line. Characters in the first line of the input
|
||||
/// (before the first newline character) are in line 1.
|
||||
line: usize,
|
||||
|
||||
/// Index of the current column. The first character in the input and any
|
||||
/// characters immediately following a newline character are in column 1.
|
||||
/// The column is 0 immediately after a newline character has been read.
|
||||
col: usize,
|
||||
|
||||
/// Byte offset of the start of the current line. This is the sum of lenghts
|
||||
/// of all previous lines. Keeping track of things this way allows efficient
|
||||
/// computation of the current line, column, and byte offset while only
|
||||
/// updating one of the counters in `next()` in the common case.
|
||||
start_of_line: usize,
|
||||
}
|
||||
|
||||
impl<I> LineColIterator<I>
|
||||
where
|
||||
I: Iterator<Item = io::Result<u8>>,
|
||||
{
|
||||
pub fn new(iter: I) -> LineColIterator<I> {
|
||||
LineColIterator {
|
||||
iter: iter,
|
||||
line: 1,
|
||||
col: 0,
|
||||
start_of_line: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line(&self) -> usize {
|
||||
self.line
|
||||
}
|
||||
|
||||
pub fn col(&self) -> usize {
|
||||
self.col
|
||||
}
|
||||
|
||||
pub fn byte_offset(&self) -> usize {
|
||||
self.start_of_line + self.col
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> Iterator for LineColIterator<I>
|
||||
where
|
||||
I: Iterator<Item = io::Result<u8>>,
|
||||
{
|
||||
type Item = io::Result<u8>;
|
||||
|
||||
fn next(&mut self) -> Option<io::Result<u8>> {
|
||||
match self.iter.next() {
|
||||
None => None,
|
||||
Some(Ok(b'\n')) => {
|
||||
self.start_of_line += self.col + 1;
|
||||
self.line += 1;
|
||||
self.col = 0;
|
||||
Some(Ok(b'\n'))
|
||||
}
|
||||
Some(Ok(c)) => {
|
||||
self.col += 1;
|
||||
Some(Ok(c))
|
||||
}
|
||||
Some(Err(e)) => Some(Err(e)),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,391 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! # Serde JSON
|
||||
//!
|
||||
//! JSON is a ubiquitous open-standard format that uses human-readable text to
|
||||
//! transmit data objects consisting of key-value pairs.
|
||||
//!
|
||||
//! ```json,ignore
|
||||
//! {
|
||||
//! "name": "John Doe",
|
||||
//! "age": 43,
|
||||
//! "address": {
|
||||
//! "street": "10 Downing Street",
|
||||
//! "city": "London"
|
||||
//! },
|
||||
//! "phones": [
|
||||
//! "+44 1234567",
|
||||
//! "+44 2345678"
|
||||
//! ]
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! There are three common ways that you might find yourself needing to work
|
||||
//! with JSON data in Rust.
|
||||
//!
|
||||
//! - **As text data.** An unprocessed string of JSON data that you receive on
|
||||
//! an HTTP endpoint, read from a file, or prepare to send to a remote
|
||||
//! server.
|
||||
//! - **As an untyped or loosely typed representation.** Maybe you want to
|
||||
//! check that some JSON data is valid before passing it on, but without
|
||||
//! knowing the structure of what it contains. Or you want to do very basic
|
||||
//! manipulations like insert a key in a particular spot.
|
||||
//! - **As a strongly typed Rust data structure.** When you expect all or most
|
||||
//! of your data to conform to a particular structure and want to get real
|
||||
//! work done without JSON's loosey-goosey nature tripping you up.
|
||||
//!
|
||||
//! Serde JSON provides efficient, flexible, safe ways of converting data
|
||||
//! between each of these representations.
|
||||
//!
|
||||
//! # Operating on untyped JSON values
|
||||
//!
|
||||
//! Any valid JSON data can be manipulated in the following recursive enum
|
||||
//! representation. This data structure is [`serde_json::Value`][value].
|
||||
//!
|
||||
//! ```rust
|
||||
//! # use serde_json::{Number, Map};
|
||||
//! #
|
||||
//! # #[allow(dead_code)]
|
||||
//! enum Value {
|
||||
//! Null,
|
||||
//! Bool(bool),
|
||||
//! Number(Number),
|
||||
//! String(String),
|
||||
//! Array(Vec<Value>),
|
||||
//! Object(Map<String, Value>),
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! A string of JSON data can be parsed into a `serde_json::Value` by the
|
||||
//! [`serde_json::from_str`][from_str] function. There is also
|
||||
//! [`from_slice`][from_slice] for parsing from a byte slice &[u8] and
|
||||
//! [`from_reader`][from_reader] for parsing from any `io::Read` like a File or
|
||||
//! a TCP stream.
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate serde_json;
|
||||
//!
|
||||
//! use serde_json::{Value, Error};
|
||||
//!
|
||||
//! fn untyped_example() -> Result<(), Error> {
|
||||
//! // Some JSON input data as a &str. Maybe this comes from the user.
|
||||
//! let data = r#"{
|
||||
//! "name": "John Doe",
|
||||
//! "age": 43,
|
||||
//! "phones": [
|
||||
//! "+44 1234567",
|
||||
//! "+44 2345678"
|
||||
//! ]
|
||||
//! }"#;
|
||||
//!
|
||||
//! // Parse the string of data into serde_json::Value.
|
||||
//! let v: Value = serde_json::from_str(data)?;
|
||||
//!
|
||||
//! // Access parts of the data by indexing with square brackets.
|
||||
//! println!("Please call {} at the number {}", v["name"], v["phones"][0]);
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! #
|
||||
//! # fn main() {
|
||||
//! # untyped_example().unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! The result of square bracket indexing like `v["name"]` is a borrow of the
|
||||
//! data at that index, so the type is `&Value`. A JSON map can be indexed with
|
||||
//! string keys, while a JSON array can be indexed with integer keys. If the
|
||||
//! type of the data is not right for the type with which it is being indexed,
|
||||
//! or if a map does not contain the key being indexed, or if the index into a
|
||||
//! vector is out of bounds, the returned element is `Value::Null`.
|
||||
//!
|
||||
//! When a `Value` is printed, it is printed as a JSON string. So in the code
|
||||
//! above, the output looks like `Please call "John Doe" at the number "+44
|
||||
//! 1234567"`. The quotation marks appear because `v["name"]` is a `&Value`
|
||||
//! containing a JSON string and its JSON representation is `"John Doe"`.
|
||||
//! Printing as a plain string without quotation marks involves converting from
|
||||
//! a JSON string to a Rust string with [`as_str()`] or avoiding the use of
|
||||
//! `Value` as described in the following section.
|
||||
//!
|
||||
//! [`as_str()`]: https://docs.serde.rs/serde_json/enum.Value.html#method.as_str
|
||||
//!
|
||||
//! The `Value` representation is sufficient for very basic tasks but can be
|
||||
//! tedious to work with for anything more significant. Error handling is
|
||||
//! verbose to implement correctly, for example imagine trying to detect the
|
||||
//! presence of unrecognized fields in the input data. The compiler is powerless
|
||||
//! to help you when you make a mistake, for example imagine typoing `v["name"]`
|
||||
//! as `v["nmae"]` in one of the dozens of places it is used in your code.
|
||||
//!
|
||||
//! # Parsing JSON as strongly typed data structures
|
||||
//!
|
||||
//! Serde provides a powerful way of mapping JSON data into Rust data structures
|
||||
//! largely automatically.
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate serde;
|
||||
//! extern crate serde_json;
|
||||
//!
|
||||
//! #[macro_use]
|
||||
//! extern crate serde_derive;
|
||||
//!
|
||||
//! use serde_json::Error;
|
||||
//!
|
||||
//! #[derive(Serialize, Deserialize)]
|
||||
//! struct Person {
|
||||
//! name: String,
|
||||
//! age: u8,
|
||||
//! phones: Vec<String>,
|
||||
//! }
|
||||
//!
|
||||
//! fn typed_example() -> Result<(), Error> {
|
||||
//! // Some JSON input data as a &str. Maybe this comes from the user.
|
||||
//! let data = r#"{
|
||||
//! "name": "John Doe",
|
||||
//! "age": 43,
|
||||
//! "phones": [
|
||||
//! "+44 1234567",
|
||||
//! "+44 2345678"
|
||||
//! ]
|
||||
//! }"#;
|
||||
//!
|
||||
//! // Parse the string of data into a Person object. This is exactly the
|
||||
//! // same function as the one that produced serde_json::Value above, but
|
||||
//! // now we are asking it for a Person as output.
|
||||
//! let p: Person = serde_json::from_str(data)?;
|
||||
//!
|
||||
//! // Do things just like with any other Rust data structure.
|
||||
//! println!("Please call {} at the number {}", p.name, p.phones[0]);
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! #
|
||||
//! # fn main() {
|
||||
//! # typed_example().unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! This is the same `serde_json::from_str` function as before, but this time we
|
||||
//! assign the return value to a variable of type `Person` so Serde will
|
||||
//! automatically interpret the input data as a `Person` and produce informative
|
||||
//! error messages if the layout does not conform to what a `Person` is expected
|
||||
//! to look like.
|
||||
//!
|
||||
//! Any type that implements Serde's `Deserialize` trait can be deserialized
|
||||
//! this way. This includes built-in Rust standard library types like `Vec<T>`
|
||||
//! and `HashMap<K, V>`, as well as any structs or enums annotated with
|
||||
//! `#[derive(Deserialize)]`.
|
||||
//!
|
||||
//! Once we have `p` of type `Person`, our IDE and the Rust compiler can help us
|
||||
//! use it correctly like they do for any other Rust code. The IDE can
|
||||
//! autocomplete field names to prevent typos, which was impossible in the
|
||||
//! `serde_json::Value` representation. And the Rust compiler can check that
|
||||
//! when we write `p.phones[0]`, then `p.phones` is guaranteed to be a
|
||||
//! `Vec<String>` so indexing into it makes sense and produces a `String`.
|
||||
//!
|
||||
//! # Constructing JSON values
|
||||
//!
|
||||
//! Serde JSON provides a [`json!` macro][macro] to build `serde_json::Value`
|
||||
//! objects with very natural JSON syntax. In order to use this macro,
|
||||
//! `serde_json` needs to be imported with the `#[macro_use]` attribute.
|
||||
//!
|
||||
//! ```rust
|
||||
//! #[macro_use]
|
||||
//! extern crate serde_json;
|
||||
//!
|
||||
//! fn main() {
|
||||
//! // The type of `john` is `serde_json::Value`
|
||||
//! let john = json!({
|
||||
//! "name": "John Doe",
|
||||
//! "age": 43,
|
||||
//! "phones": [
|
||||
//! "+44 1234567",
|
||||
//! "+44 2345678"
|
||||
//! ]
|
||||
//! });
|
||||
//!
|
||||
//! println!("first phone number: {}", john["phones"][0]);
|
||||
//!
|
||||
//! // Convert to a string of JSON and print it out
|
||||
//! println!("{}", john.to_string());
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! The `Value::to_string()` function converts a `serde_json::Value` into a
|
||||
//! `String` of JSON text.
|
||||
//!
|
||||
//! One neat thing about the `json!` macro is that variables and expressions can
|
||||
//! be interpolated directly into the JSON value as you are building it. Serde
|
||||
//! will check at compile time that the value you are interpolating is able to
|
||||
//! be represented as JSON.
|
||||
//!
|
||||
//! ```rust
|
||||
//! # #[macro_use]
|
||||
//! # extern crate serde_json;
|
||||
//! #
|
||||
//! # fn random_phone() -> u16 { 0 }
|
||||
//! #
|
||||
//! # fn main() {
|
||||
//! let full_name = "John Doe";
|
||||
//! let age_last_year = 42;
|
||||
//!
|
||||
//! // The type of `john` is `serde_json::Value`
|
||||
//! let john = json!({
|
||||
//! "name": full_name,
|
||||
//! "age": age_last_year + 1,
|
||||
//! "phones": [
|
||||
//! format!("+44 {}", random_phone())
|
||||
//! ]
|
||||
//! });
|
||||
//! # let _ = john;
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! This is amazingly convenient but we have the problem we had before with
|
||||
//! `Value` which is that the IDE and Rust compiler cannot help us if we get it
|
||||
//! wrong. Serde JSON provides a better way of serializing strongly-typed data
|
||||
//! structures into JSON text.
|
||||
//!
|
||||
//! # Creating JSON by serializing data structures
|
||||
//!
|
||||
//! A data structure can be converted to a JSON string by
|
||||
//! [`serde_json::to_string`][to_string]. There is also
|
||||
//! [`serde_json::to_vec`][to_vec] which serializes to a `Vec<u8>` and
|
||||
//! [`serde_json::to_writer`][to_writer] which serializes to any `io::Write`
|
||||
//! such as a File or a TCP stream.
|
||||
//!
|
||||
//! ```rust
|
||||
//! extern crate serde;
|
||||
//! extern crate serde_json;
|
||||
//!
|
||||
//! #[macro_use]
|
||||
//! extern crate serde_derive;
|
||||
//!
|
||||
//! use serde_json::Error;
|
||||
//!
|
||||
//! #[derive(Serialize, Deserialize)]
|
||||
//! struct Address {
|
||||
//! street: String,
|
||||
//! city: String,
|
||||
//! }
|
||||
//!
|
||||
//! fn print_an_address() -> Result<(), Error> {
|
||||
//! // Some data structure.
|
||||
//! let address = Address {
|
||||
//! street: "10 Downing Street".to_owned(),
|
||||
//! city: "London".to_owned(),
|
||||
//! };
|
||||
//!
|
||||
//! // Serialize it to a JSON string.
|
||||
//! let j = serde_json::to_string(&address)?;
|
||||
//!
|
||||
//! // Print, write to a file, or send to an HTTP server.
|
||||
//! println!("{}", j);
|
||||
//!
|
||||
//! Ok(())
|
||||
//! }
|
||||
//! #
|
||||
//! # fn main() {
|
||||
//! # print_an_address().unwrap();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Any type that implements Serde's `Serialize` trait can be serialized this
|
||||
//! way. This includes built-in Rust standard library types like `Vec<T>` and
|
||||
//! `HashMap<K, V>`, as well as any structs or enums annotated with
|
||||
//! `#[derive(Serialize)]`.
|
||||
//!
|
||||
//! # No-std support
|
||||
//!
|
||||
//! This crate currently requires the Rust standard library. For JSON support in
|
||||
//! Serde without a standard library, please see the [`serde-json-core`] crate.
|
||||
//!
|
||||
//! [value]: https://docs.serde.rs/serde_json/value/enum.Value.html
|
||||
//! [from_str]: https://docs.serde.rs/serde_json/de/fn.from_str.html
|
||||
//! [from_slice]: https://docs.serde.rs/serde_json/de/fn.from_slice.html
|
||||
//! [from_reader]: https://docs.serde.rs/serde_json/de/fn.from_reader.html
|
||||
//! [to_string]: https://docs.serde.rs/serde_json/ser/fn.to_string.html
|
||||
//! [to_vec]: https://docs.serde.rs/serde_json/ser/fn.to_vec.html
|
||||
//! [to_writer]: https://docs.serde.rs/serde_json/ser/fn.to_writer.html
|
||||
//! [macro]: https://docs.serde.rs/serde_json/macro.json.html
|
||||
//! [`serde-json-core`]: https://japaric.github.io/serde-json-core/serde_json_core/
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/serde_json/1.0.26")]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||
// Whitelisted clippy lints
|
||||
#![cfg_attr(
|
||||
feature = "cargo-clippy",
|
||||
allow(doc_markdown, needless_pass_by_value)
|
||||
)]
|
||||
// Whitelisted clippy_pedantic lints
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(
|
||||
// Deserializer::from_str, into_iter
|
||||
should_implement_trait,
|
||||
// integer and float ser/de requires these sorts of casts
|
||||
cast_possible_truncation,
|
||||
cast_possible_wrap,
|
||||
cast_precision_loss,
|
||||
cast_sign_loss,
|
||||
// string ser/de uses indexing and slicing
|
||||
indexing_slicing,
|
||||
// things are often more readable this way
|
||||
cast_lossless,
|
||||
shadow_reuse,
|
||||
shadow_unrelated,
|
||||
single_match_else,
|
||||
stutter,
|
||||
use_self,
|
||||
// not practical
|
||||
missing_docs_in_private_items,
|
||||
similar_names,
|
||||
// we support older compilers
|
||||
redundant_field_names,
|
||||
))]
|
||||
#![deny(missing_docs)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate serde;
|
||||
extern crate ryu;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
extern crate indexmap;
|
||||
extern crate itoa;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use self::de::{from_reader, from_slice, from_str, Deserializer, StreamDeserializer};
|
||||
#[doc(inline)]
|
||||
pub use self::error::{Error, Result};
|
||||
#[doc(inline)]
|
||||
pub use self::ser::{
|
||||
to_string, to_string_pretty, to_vec, to_vec_pretty, to_writer, to_writer_pretty, Serializer,
|
||||
};
|
||||
#[doc(inline)]
|
||||
pub use self::value::{from_value, to_value, Map, Number, Value};
|
||||
|
||||
// We only use our own error type; no need for From conversions provided by the
|
||||
// standard library's try! macro. This reduces lines of LLVM IR by 4%.
|
||||
macro_rules! try {
|
||||
($e:expr) => {
|
||||
match $e {
|
||||
::std::result::Result::Ok(val) => val,
|
||||
::std::result::Result::Err(err) => return ::std::result::Result::Err(err),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub mod de;
|
||||
pub mod error;
|
||||
pub mod map;
|
||||
pub mod ser;
|
||||
pub mod value;
|
||||
|
||||
mod iter;
|
||||
mod number;
|
||||
mod read;
|
|
@ -1,309 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
/// Construct a `serde_json::Value` from a JSON literal.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let value = json!({
|
||||
/// "code": 200,
|
||||
/// "success": true,
|
||||
/// "payload": {
|
||||
/// "features": [
|
||||
/// "serde",
|
||||
/// "json"
|
||||
/// ]
|
||||
/// }
|
||||
/// });
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Variables or expressions can be interpolated into the JSON literal. Any type
|
||||
/// interpolated into an array element or object value must implement Serde's
|
||||
/// `Serialize` trait, while any type interpolated into a object key must
|
||||
/// implement `Into<String>`. If the `Serialize` implementation of the
|
||||
/// interpolated type decides to fail, or if the interpolated type contains a
|
||||
/// map with non-string keys, the `json!` macro will panic.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let code = 200;
|
||||
/// let features = vec!["serde", "json"];
|
||||
///
|
||||
/// let value = json!({
|
||||
/// "code": code,
|
||||
/// "success": code == 200,
|
||||
/// "payload": {
|
||||
/// features[0]: features[1]
|
||||
/// }
|
||||
/// });
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Trailing commas are allowed inside both arrays and objects.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let value = json!([
|
||||
/// "notice",
|
||||
/// "the",
|
||||
/// "trailing",
|
||||
/// "comma -->",
|
||||
/// ]);
|
||||
/// # }
|
||||
/// ```
|
||||
#[macro_export(local_inner_macros)]
|
||||
macro_rules! json {
|
||||
// Hide distracting implementation details from the generated rustdoc.
|
||||
($($json:tt)+) => {
|
||||
json_internal!($($json)+)
|
||||
};
|
||||
}
|
||||
|
||||
// Rocket relies on this because they export their own `json!` with a different
|
||||
// doc comment than ours, and various Rust bugs prevent them from calling our
|
||||
// `json!` from their `json!` so they call `json_internal!` directly. Check with
|
||||
// @SergioBenitez before making breaking changes to this macro.
|
||||
//
|
||||
// Changes are fine as long as `json_internal!` does not call any new helper
|
||||
// macros and can still be invoked as `json_internal!($($json)+)`.
|
||||
#[macro_export(local_inner_macros)]
|
||||
#[doc(hidden)]
|
||||
macro_rules! json_internal {
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TT muncher for parsing the inside of an array [...]. Produces a vec![...]
|
||||
// of the elements.
|
||||
//
|
||||
// Must be invoked as: json_internal!(@array [] $($tt)*)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Done with trailing comma.
|
||||
(@array [$($elems:expr,)*]) => {
|
||||
json_internal_vec![$($elems,)*]
|
||||
};
|
||||
|
||||
// Done without trailing comma.
|
||||
(@array [$($elems:expr),*]) => {
|
||||
json_internal_vec![$($elems),*]
|
||||
};
|
||||
|
||||
// Next element is `null`.
|
||||
(@array [$($elems:expr,)*] null $($rest:tt)*) => {
|
||||
json_internal!(@array [$($elems,)* json_internal!(null)] $($rest)*)
|
||||
};
|
||||
|
||||
// Next element is `true`.
|
||||
(@array [$($elems:expr,)*] true $($rest:tt)*) => {
|
||||
json_internal!(@array [$($elems,)* json_internal!(true)] $($rest)*)
|
||||
};
|
||||
|
||||
// Next element is `false`.
|
||||
(@array [$($elems:expr,)*] false $($rest:tt)*) => {
|
||||
json_internal!(@array [$($elems,)* json_internal!(false)] $($rest)*)
|
||||
};
|
||||
|
||||
// Next element is an array.
|
||||
(@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => {
|
||||
json_internal!(@array [$($elems,)* json_internal!([$($array)*])] $($rest)*)
|
||||
};
|
||||
|
||||
// Next element is a map.
|
||||
(@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => {
|
||||
json_internal!(@array [$($elems,)* json_internal!({$($map)*})] $($rest)*)
|
||||
};
|
||||
|
||||
// Next element is an expression followed by comma.
|
||||
(@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => {
|
||||
json_internal!(@array [$($elems,)* json_internal!($next),] $($rest)*)
|
||||
};
|
||||
|
||||
// Last element is an expression with no trailing comma.
|
||||
(@array [$($elems:expr,)*] $last:expr) => {
|
||||
json_internal!(@array [$($elems,)* json_internal!($last)])
|
||||
};
|
||||
|
||||
// Comma after the most recent element.
|
||||
(@array [$($elems:expr),*] , $($rest:tt)*) => {
|
||||
json_internal!(@array [$($elems,)*] $($rest)*)
|
||||
};
|
||||
|
||||
// Unexpected token after most recent element.
|
||||
(@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => {
|
||||
json_unexpected!($unexpected)
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// TT muncher for parsing the inside of an object {...}. Each entry is
|
||||
// inserted into the given map variable.
|
||||
//
|
||||
// Must be invoked as: json_internal!(@object $map () ($($tt)*) ($($tt)*))
|
||||
//
|
||||
// We require two copies of the input tokens so that we can match on one
|
||||
// copy and trigger errors on the other copy.
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Done.
|
||||
(@object $object:ident () () ()) => {};
|
||||
|
||||
// Insert the current entry followed by trailing comma.
|
||||
(@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => {
|
||||
let _ = $object.insert(($($key)+).into(), $value);
|
||||
json_internal!(@object $object () ($($rest)*) ($($rest)*));
|
||||
};
|
||||
|
||||
// Current entry followed by unexpected token.
|
||||
(@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => {
|
||||
json_unexpected!($unexpected);
|
||||
};
|
||||
|
||||
// Insert the last entry without trailing comma.
|
||||
(@object $object:ident [$($key:tt)+] ($value:expr)) => {
|
||||
let _ = $object.insert(($($key)+).into(), $value);
|
||||
};
|
||||
|
||||
// Next value is `null`.
|
||||
(@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => {
|
||||
json_internal!(@object $object [$($key)+] (json_internal!(null)) $($rest)*);
|
||||
};
|
||||
|
||||
// Next value is `true`.
|
||||
(@object $object:ident ($($key:tt)+) (: true $($rest:tt)*) $copy:tt) => {
|
||||
json_internal!(@object $object [$($key)+] (json_internal!(true)) $($rest)*);
|
||||
};
|
||||
|
||||
// Next value is `false`.
|
||||
(@object $object:ident ($($key:tt)+) (: false $($rest:tt)*) $copy:tt) => {
|
||||
json_internal!(@object $object [$($key)+] (json_internal!(false)) $($rest)*);
|
||||
};
|
||||
|
||||
// Next value is an array.
|
||||
(@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => {
|
||||
json_internal!(@object $object [$($key)+] (json_internal!([$($array)*])) $($rest)*);
|
||||
};
|
||||
|
||||
// Next value is a map.
|
||||
(@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => {
|
||||
json_internal!(@object $object [$($key)+] (json_internal!({$($map)*})) $($rest)*);
|
||||
};
|
||||
|
||||
// Next value is an expression followed by comma.
|
||||
(@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => {
|
||||
json_internal!(@object $object [$($key)+] (json_internal!($value)) , $($rest)*);
|
||||
};
|
||||
|
||||
// Last value is an expression with no trailing comma.
|
||||
(@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => {
|
||||
json_internal!(@object $object [$($key)+] (json_internal!($value)));
|
||||
};
|
||||
|
||||
// Missing value for last entry. Trigger a reasonable error message.
|
||||
(@object $object:ident ($($key:tt)+) (:) $copy:tt) => {
|
||||
// "unexpected end of macro invocation"
|
||||
json_internal!();
|
||||
};
|
||||
|
||||
// Missing colon and value for last entry. Trigger a reasonable error
|
||||
// message.
|
||||
(@object $object:ident ($($key:tt)+) () $copy:tt) => {
|
||||
// "unexpected end of macro invocation"
|
||||
json_internal!();
|
||||
};
|
||||
|
||||
// Misplaced colon. Trigger a reasonable error message.
|
||||
(@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => {
|
||||
// Takes no arguments so "no rules expected the token `:`".
|
||||
json_unexpected!($colon);
|
||||
};
|
||||
|
||||
// Found a comma inside a key. Trigger a reasonable error message.
|
||||
(@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => {
|
||||
// Takes no arguments so "no rules expected the token `,`".
|
||||
json_unexpected!($comma);
|
||||
};
|
||||
|
||||
// Key is fully parenthesized. This avoids clippy double_parens false
|
||||
// positives because the parenthesization may be necessary here.
|
||||
(@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => {
|
||||
json_internal!(@object $object ($key) (: $($rest)*) (: $($rest)*));
|
||||
};
|
||||
|
||||
// Munch a token into the current key.
|
||||
(@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => {
|
||||
json_internal!(@object $object ($($key)* $tt) ($($rest)*) ($($rest)*));
|
||||
};
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// The main implementation.
|
||||
//
|
||||
// Must be invoked as: json_internal!($($json)+)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
(null) => {
|
||||
$crate::Value::Null
|
||||
};
|
||||
|
||||
(true) => {
|
||||
$crate::Value::Bool(true)
|
||||
};
|
||||
|
||||
(false) => {
|
||||
$crate::Value::Bool(false)
|
||||
};
|
||||
|
||||
([]) => {
|
||||
$crate::Value::Array(json_internal_vec![])
|
||||
};
|
||||
|
||||
([ $($tt:tt)+ ]) => {
|
||||
$crate::Value::Array(json_internal!(@array [] $($tt)+))
|
||||
};
|
||||
|
||||
({}) => {
|
||||
$crate::Value::Object($crate::Map::new())
|
||||
};
|
||||
|
||||
({ $($tt:tt)+ }) => {
|
||||
$crate::Value::Object({
|
||||
let mut object = $crate::Map::new();
|
||||
json_internal!(@object object () ($($tt)+) ($($tt)+));
|
||||
object
|
||||
})
|
||||
};
|
||||
|
||||
// Any Serialize type: numbers, strings, struct literals, variables etc.
|
||||
// Must be below every other rule.
|
||||
($other:expr) => {
|
||||
$crate::to_value(&$other).unwrap()
|
||||
};
|
||||
}
|
||||
|
||||
// The json_internal macro above cannot invoke vec directly because it uses
|
||||
// local_inner_macros. A vec invocation there would resolve to $crate::vec.
|
||||
// Instead invoke vec here outside of local_inner_macros.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! json_internal_vec {
|
||||
($($content:tt)*) => {
|
||||
vec![$($content)*]
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! json_unexpected {
|
||||
() => {};
|
||||
}
|
|
@ -1,852 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! A map of String to serde_json::Value.
|
||||
//!
|
||||
//! By default the map is backed by a [`BTreeMap`]. Enable the `preserve_order`
|
||||
//! feature of serde_json to use [`IndexMap`] instead.
|
||||
//!
|
||||
//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
|
||||
//! [`IndexMap`]: https://docs.rs/indexmap/*/indexmap/map/struct.IndexMap.html
|
||||
|
||||
use serde::{de, ser};
|
||||
use std::borrow::Borrow;
|
||||
use std::fmt::{self, Debug};
|
||||
use std::hash::Hash;
|
||||
use std::iter::FromIterator;
|
||||
use std::ops;
|
||||
use value::Value;
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
use std::collections::{btree_map, BTreeMap};
|
||||
|
||||
#[cfg(feature = "preserve_order")]
|
||||
use indexmap::{self, IndexMap};
|
||||
|
||||
/// Represents a JSON key/value type.
|
||||
pub struct Map<K, V> {
|
||||
map: MapImpl<K, V>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type MapImpl<K, V> = BTreeMap<K, V>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type MapImpl<K, V> = IndexMap<K, V>;
|
||||
|
||||
impl Map<String, Value> {
|
||||
/// Makes a new empty Map.
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Map {
|
||||
map: MapImpl::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
/// Makes a new empty Map with the given initial capacity.
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
// does not support with_capacity
|
||||
let _ = capacity;
|
||||
Map {
|
||||
map: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "preserve_order")]
|
||||
/// Makes a new empty Map with the given initial capacity.
|
||||
#[inline]
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Map {
|
||||
map: IndexMap::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears the map, removing all values.
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
self.map.clear()
|
||||
}
|
||||
|
||||
/// Returns a reference to the value corresponding to the key.
|
||||
///
|
||||
/// The key may be any borrowed form of the map's key type, but the ordering
|
||||
/// on the borrowed form *must* match the ordering on the key type.
|
||||
#[inline]
|
||||
pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&Value>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: Ord + Eq + Hash,
|
||||
{
|
||||
self.map.get(key)
|
||||
}
|
||||
|
||||
/// Returns true if the map contains a value for the specified key.
|
||||
///
|
||||
/// The key may be any borrowed form of the map's key type, but the ordering
|
||||
/// on the borrowed form *must* match the ordering on the key type.
|
||||
#[inline]
|
||||
pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: Ord + Eq + Hash,
|
||||
{
|
||||
self.map.contains_key(key)
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the value corresponding to the key.
|
||||
///
|
||||
/// The key may be any borrowed form of the map's key type, but the ordering
|
||||
/// on the borrowed form *must* match the ordering on the key type.
|
||||
#[inline]
|
||||
pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut Value>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: Ord + Eq + Hash,
|
||||
{
|
||||
self.map.get_mut(key)
|
||||
}
|
||||
|
||||
/// Inserts a key-value pair into the map.
|
||||
///
|
||||
/// If the map did not have this key present, `None` is returned.
|
||||
///
|
||||
/// If the map did have this key present, the value is updated, and the old
|
||||
/// value is returned.
|
||||
#[inline]
|
||||
pub fn insert(&mut self, k: String, v: Value) -> Option<Value> {
|
||||
self.map.insert(k, v)
|
||||
}
|
||||
|
||||
/// Removes a key from the map, returning the value at the key if the key
|
||||
/// was previously in the map.
|
||||
///
|
||||
/// The key may be any borrowed form of the map's key type, but the ordering
|
||||
/// on the borrowed form *must* match the ordering on the key type.
|
||||
#[inline]
|
||||
pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<Value>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: Ord + Eq + Hash,
|
||||
{
|
||||
self.map.remove(key)
|
||||
}
|
||||
|
||||
/// Gets the given key's corresponding entry in the map for in-place
|
||||
/// manipulation.
|
||||
pub fn entry<S>(&mut self, key: S) -> Entry
|
||||
where
|
||||
S: Into<String>,
|
||||
{
|
||||
#[cfg(feature = "preserve_order")]
|
||||
use indexmap::map::Entry as EntryImpl;
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
use std::collections::btree_map::Entry as EntryImpl;
|
||||
|
||||
match self.map.entry(key.into()) {
|
||||
EntryImpl::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant: vacant }),
|
||||
EntryImpl::Occupied(occupied) => Entry::Occupied(OccupiedEntry { occupied: occupied }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of elements in the map.
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.map.len()
|
||||
}
|
||||
|
||||
/// Returns true if the map contains no elements.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.map.is_empty()
|
||||
}
|
||||
|
||||
/// Gets an iterator over the entries of the map.
|
||||
#[inline]
|
||||
pub fn iter(&self) -> Iter {
|
||||
Iter {
|
||||
iter: self.map.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets a mutable iterator over the entries of the map.
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> IterMut {
|
||||
IterMut {
|
||||
iter: self.map.iter_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets an iterator over the keys of the map.
|
||||
#[inline]
|
||||
pub fn keys(&self) -> Keys {
|
||||
Keys {
|
||||
iter: self.map.keys(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets an iterator over the values of the map.
|
||||
#[inline]
|
||||
pub fn values(&self) -> Values {
|
||||
Values {
|
||||
iter: self.map.values(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets an iterator over mutable values of the map.
|
||||
#[inline]
|
||||
pub fn values_mut(&mut self) -> ValuesMut {
|
||||
ValuesMut {
|
||||
iter: self.map.values_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Map<String, Value> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Map {
|
||||
map: MapImpl::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Map<String, Value> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
Map {
|
||||
map: self.map.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Map<String, Value> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
if cfg!(feature = "preserve_order") {
|
||||
if self.len() != other.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
self.iter()
|
||||
.all(|(key, value)| other.get(key).map_or(false, |v| *value == *v))
|
||||
} else {
|
||||
self.map.eq(&other.map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access an element of this map. Panics if the given key is not present in the
|
||||
/// map.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use serde_json::Value;
|
||||
/// #
|
||||
/// # let val = &Value::String("".to_owned());
|
||||
/// # let _ =
|
||||
/// match *val {
|
||||
/// Value::String(ref s) => Some(s.as_str()),
|
||||
/// Value::Array(ref arr) => arr[0].as_str(),
|
||||
/// Value::Object(ref map) => map["type"].as_str(),
|
||||
/// _ => None,
|
||||
/// }
|
||||
/// # ;
|
||||
/// ```
|
||||
impl<'a, Q: ?Sized> ops::Index<&'a Q> for Map<String, Value>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: Ord + Eq + Hash,
|
||||
{
|
||||
type Output = Value;
|
||||
|
||||
fn index(&self, index: &Q) -> &Value {
|
||||
self.map.index(index)
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutably access an element of this map. Panics if the given key is not
|
||||
/// present in the map.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// # let mut map = serde_json::Map::new();
|
||||
/// # map.insert("key".to_owned(), serde_json::Value::Null);
|
||||
/// #
|
||||
/// map["key"] = json!("value");
|
||||
/// # }
|
||||
/// ```
|
||||
impl<'a, Q: ?Sized> ops::IndexMut<&'a Q> for Map<String, Value>
|
||||
where
|
||||
String: Borrow<Q>,
|
||||
Q: Ord + Eq + Hash,
|
||||
{
|
||||
fn index_mut(&mut self, index: &Q) -> &mut Value {
|
||||
self.map.get_mut(index).expect("no entry found for key")
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Map<String, Value> {
|
||||
#[inline]
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
self.map.fmt(formatter)
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Serialize for Map<String, Value> {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
use serde::ser::SerializeMap;
|
||||
let mut map = try!(serializer.serialize_map(Some(self.len())));
|
||||
for (k, v) in self {
|
||||
try!(map.serialize_key(k));
|
||||
try!(map.serialize_value(v));
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> de::Deserialize<'de> for Map<String, Value> {
|
||||
#[inline]
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct Visitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for Visitor {
|
||||
type Value = Map<String, Value>;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a map")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_unit<E>(self) -> Result<Self::Value, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(Map::new())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: de::MapAccess<'de>,
|
||||
{
|
||||
let mut values = Map::new();
|
||||
|
||||
while let Some((key, value)) = try!(visitor.next_entry()) {
|
||||
values.insert(key, value);
|
||||
}
|
||||
|
||||
Ok(values)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_map(Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(String, Value)> for Map<String, Value> {
|
||||
fn from_iter<T>(iter: T) -> Self
|
||||
where
|
||||
T: IntoIterator<Item = (String, Value)>,
|
||||
{
|
||||
Map {
|
||||
map: FromIterator::from_iter(iter),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<(String, Value)> for Map<String, Value> {
|
||||
fn extend<T>(&mut self, iter: T)
|
||||
where
|
||||
T: IntoIterator<Item = (String, Value)>,
|
||||
{
|
||||
self.map.extend(iter);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! delegate_iterator {
|
||||
(($name:ident $($generics:tt)*) => $item:ty) => {
|
||||
impl $($generics)* Iterator for $name $($generics)* {
|
||||
type Item = $item;
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next()
|
||||
}
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl $($generics)* DoubleEndedIterator for $name $($generics)* {
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next_back()
|
||||
}
|
||||
}
|
||||
|
||||
impl $($generics)* ExactSizeIterator for $name $($generics)* {
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.iter.len()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// A view into a single entry in a map, which may either be vacant or occupied.
|
||||
/// This enum is constructed from the [`entry`] method on [`Map`].
|
||||
///
|
||||
/// [`entry`]: struct.Map.html#method.entry
|
||||
/// [`Map`]: struct.Map.html
|
||||
pub enum Entry<'a> {
|
||||
/// A vacant Entry.
|
||||
Vacant(VacantEntry<'a>),
|
||||
/// An occupied Entry.
|
||||
Occupied(OccupiedEntry<'a>),
|
||||
}
|
||||
|
||||
/// A vacant Entry. It is part of the [`Entry`] enum.
|
||||
///
|
||||
/// [`Entry`]: enum.Entry.html
|
||||
pub struct VacantEntry<'a> {
|
||||
vacant: VacantEntryImpl<'a>,
|
||||
}
|
||||
|
||||
/// An occupied Entry. It is part of the [`Entry`] enum.
|
||||
///
|
||||
/// [`Entry`]: enum.Entry.html
|
||||
pub struct OccupiedEntry<'a> {
|
||||
occupied: OccupiedEntryImpl<'a>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type VacantEntryImpl<'a> = btree_map::VacantEntry<'a, String, Value>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type VacantEntryImpl<'a> = indexmap::map::VacantEntry<'a, String, Value>;
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type OccupiedEntryImpl<'a> = btree_map::OccupiedEntry<'a, String, Value>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type OccupiedEntryImpl<'a> = indexmap::map::OccupiedEntry<'a, String, Value>;
|
||||
|
||||
impl<'a> Entry<'a> {
|
||||
/// Returns a reference to this entry's key.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// assert_eq!(map.entry("serde").key(), &"serde");
|
||||
/// ```
|
||||
pub fn key(&self) -> &String {
|
||||
match *self {
|
||||
Entry::Vacant(ref e) => e.key(),
|
||||
Entry::Occupied(ref e) => e.key(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures a value is in the entry by inserting the default if empty, and
|
||||
/// returns a mutable reference to the value in the entry.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// map.entry("serde").or_insert(json!(12));
|
||||
///
|
||||
/// assert_eq!(map["serde"], 12);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn or_insert(self, default: Value) -> &'a mut Value {
|
||||
match self {
|
||||
Entry::Vacant(entry) => entry.insert(default),
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensures a value is in the entry by inserting the result of the default
|
||||
/// function if empty, and returns a mutable reference to the value in the
|
||||
/// entry.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// map.entry("serde").or_insert_with(|| json!("hoho"));
|
||||
///
|
||||
/// assert_eq!(map["serde"], "hoho".to_owned());
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn or_insert_with<F>(self, default: F) -> &'a mut Value
|
||||
where
|
||||
F: FnOnce() -> Value,
|
||||
{
|
||||
match self {
|
||||
Entry::Vacant(entry) => entry.insert(default()),
|
||||
Entry::Occupied(entry) => entry.into_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> VacantEntry<'a> {
|
||||
/// Gets a reference to the key that would be used when inserting a value
|
||||
/// through the VacantEntry.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use serde_json::map::Entry;
|
||||
///
|
||||
/// let mut map = serde_json::Map::new();
|
||||
///
|
||||
/// match map.entry("serde") {
|
||||
/// Entry::Vacant(vacant) => {
|
||||
/// assert_eq!(vacant.key(), &"serde");
|
||||
/// }
|
||||
/// Entry::Occupied(_) => unimplemented!(),
|
||||
/// }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn key(&self) -> &String {
|
||||
self.vacant.key()
|
||||
}
|
||||
|
||||
/// Sets the value of the entry with the VacantEntry's key, and returns a
|
||||
/// mutable reference to it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::map::Entry;
|
||||
///
|
||||
/// let mut map = serde_json::Map::new();
|
||||
///
|
||||
/// match map.entry("serde") {
|
||||
/// Entry::Vacant(vacant) => {
|
||||
/// vacant.insert(json!("hoho"));
|
||||
/// }
|
||||
/// Entry::Occupied(_) => unimplemented!(),
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn insert(self, value: Value) -> &'a mut Value {
|
||||
self.vacant.insert(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> OccupiedEntry<'a> {
|
||||
/// Gets a reference to the key in the entry.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::map::Entry;
|
||||
///
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// map.insert("serde".to_owned(), json!(12));
|
||||
///
|
||||
/// match map.entry("serde") {
|
||||
/// Entry::Occupied(occupied) => {
|
||||
/// assert_eq!(occupied.key(), &"serde");
|
||||
/// }
|
||||
/// Entry::Vacant(_) => unimplemented!(),
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn key(&self) -> &String {
|
||||
self.occupied.key()
|
||||
}
|
||||
|
||||
/// Gets a reference to the value in the entry.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::map::Entry;
|
||||
///
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// map.insert("serde".to_owned(), json!(12));
|
||||
///
|
||||
/// match map.entry("serde") {
|
||||
/// Entry::Occupied(occupied) => {
|
||||
/// assert_eq!(occupied.get(), 12);
|
||||
/// }
|
||||
/// Entry::Vacant(_) => unimplemented!(),
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get(&self) -> &Value {
|
||||
self.occupied.get()
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the value in the entry.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::map::Entry;
|
||||
///
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// map.insert("serde".to_owned(), json!([1, 2, 3]));
|
||||
///
|
||||
/// match map.entry("serde") {
|
||||
/// Entry::Occupied(mut occupied) => {
|
||||
/// occupied.get_mut().as_array_mut().unwrap().push(json!(4));
|
||||
/// }
|
||||
/// Entry::Vacant(_) => unimplemented!(),
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(map["serde"].as_array().unwrap().len(), 4);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> &mut Value {
|
||||
self.occupied.get_mut()
|
||||
}
|
||||
|
||||
/// Converts the entry into a mutable reference to its value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::map::Entry;
|
||||
///
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// map.insert("serde".to_owned(), json!([1, 2, 3]));
|
||||
///
|
||||
/// match map.entry("serde") {
|
||||
/// Entry::Occupied(mut occupied) => {
|
||||
/// occupied.into_mut().as_array_mut().unwrap().push(json!(4));
|
||||
/// }
|
||||
/// Entry::Vacant(_) => unimplemented!(),
|
||||
/// }
|
||||
///
|
||||
/// assert_eq!(map["serde"].as_array().unwrap().len(), 4);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn into_mut(self) -> &'a mut Value {
|
||||
self.occupied.into_mut()
|
||||
}
|
||||
|
||||
/// Sets the value of the entry with the `OccupiedEntry`'s key, and returns
|
||||
/// the entry's old value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::map::Entry;
|
||||
///
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// map.insert("serde".to_owned(), json!(12));
|
||||
///
|
||||
/// match map.entry("serde") {
|
||||
/// Entry::Occupied(mut occupied) => {
|
||||
/// assert_eq!(occupied.insert(json!(13)), 12);
|
||||
/// assert_eq!(occupied.get(), 13);
|
||||
/// }
|
||||
/// Entry::Vacant(_) => unimplemented!(),
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn insert(&mut self, value: Value) -> Value {
|
||||
self.occupied.insert(value)
|
||||
}
|
||||
|
||||
/// Takes the value of the entry out of the map, and returns it.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::map::Entry;
|
||||
///
|
||||
/// let mut map = serde_json::Map::new();
|
||||
/// map.insert("serde".to_owned(), json!(12));
|
||||
///
|
||||
/// match map.entry("serde") {
|
||||
/// Entry::Occupied(occupied) => {
|
||||
/// assert_eq!(occupied.remove(), 12);
|
||||
/// }
|
||||
/// Entry::Vacant(_) => unimplemented!(),
|
||||
/// }
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn remove(self) -> Value {
|
||||
self.occupied.remove()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl<'a> IntoIterator for &'a Map<String, Value> {
|
||||
type Item = (&'a String, &'a Value);
|
||||
type IntoIter = Iter<'a>;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Iter {
|
||||
iter: self.map.iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An iterator over a serde_json::Map's entries.
|
||||
pub struct Iter<'a> {
|
||||
iter: IterImpl<'a>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type IterImpl<'a> = btree_map::Iter<'a, String, Value>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type IterImpl<'a> = indexmap::map::Iter<'a, String, Value>;
|
||||
|
||||
delegate_iterator!((Iter<'a>) => (&'a String, &'a Value));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl<'a> IntoIterator for &'a mut Map<String, Value> {
|
||||
type Item = (&'a String, &'a mut Value);
|
||||
type IntoIter = IterMut<'a>;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IterMut {
|
||||
iter: self.map.iter_mut(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A mutable iterator over a serde_json::Map's entries.
|
||||
pub struct IterMut<'a> {
|
||||
iter: IterMutImpl<'a>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type IterMutImpl<'a> = btree_map::IterMut<'a, String, Value>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type IterMutImpl<'a> = indexmap::map::IterMut<'a, String, Value>;
|
||||
|
||||
delegate_iterator!((IterMut<'a>) => (&'a String, &'a mut Value));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl IntoIterator for Map<String, Value> {
|
||||
type Item = (String, Value);
|
||||
type IntoIter = IntoIter;
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter {
|
||||
iter: self.map.into_iter(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An owning iterator over a serde_json::Map's entries.
|
||||
pub struct IntoIter {
|
||||
iter: IntoIterImpl,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type IntoIterImpl = btree_map::IntoIter<String, Value>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type IntoIterImpl = indexmap::map::IntoIter<String, Value>;
|
||||
|
||||
delegate_iterator!((IntoIter) => (String, Value));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// An iterator over a serde_json::Map's keys.
|
||||
pub struct Keys<'a> {
|
||||
iter: KeysImpl<'a>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type KeysImpl<'a> = btree_map::Keys<'a, String, Value>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type KeysImpl<'a> = indexmap::map::Keys<'a, String, Value>;
|
||||
|
||||
delegate_iterator!((Keys<'a>) => &'a String);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// An iterator over a serde_json::Map's values.
|
||||
pub struct Values<'a> {
|
||||
iter: ValuesImpl<'a>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type ValuesImpl<'a> = btree_map::Values<'a, String, Value>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type ValuesImpl<'a> = indexmap::map::Values<'a, String, Value>;
|
||||
|
||||
delegate_iterator!((Values<'a>) => &'a Value);
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// A mutable iterator over a serde_json::Map's values.
|
||||
pub struct ValuesMut<'a> {
|
||||
iter: ValuesMutImpl<'a>,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "preserve_order"))]
|
||||
type ValuesMutImpl<'a> = btree_map::ValuesMut<'a, String, Value>;
|
||||
#[cfg(feature = "preserve_order")]
|
||||
type ValuesMutImpl<'a> = indexmap::map::ValuesMut<'a, String, Value>;
|
||||
|
||||
delegate_iterator!((ValuesMut<'a>) => &'a mut Value);
|
|
@ -1,770 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use error::Error;
|
||||
use serde::de::{self, Unexpected, Visitor};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt::{self, Debug, Display};
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
use ryu;
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
use itoa;
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
use serde::de::{IntoDeserializer, MapAccess};
|
||||
|
||||
use de::ParserNumber;
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
use error::ErrorCode;
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
/// Not public API. Should be pub(crate).
|
||||
#[doc(hidden)]
|
||||
pub const SERDE_STRUCT_FIELD_NAME: &'static str = "$__serde_private_number";
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
/// Not public API. Should be pub(crate).
|
||||
#[doc(hidden)]
|
||||
pub const SERDE_STRUCT_NAME: &'static str = "$__serde_private_Number";
|
||||
|
||||
/// Represents a JSON number, whether integer or floating point.
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Number {
|
||||
n: N,
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
enum N {
|
||||
PosInt(u64),
|
||||
/// Always less than zero.
|
||||
NegInt(i64),
|
||||
/// Always finite.
|
||||
Float(f64),
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
type N = String;
|
||||
|
||||
impl Number {
|
||||
/// Returns true if the `Number` is an integer between `i64::MIN` and
|
||||
/// `i64::MAX`.
|
||||
///
|
||||
/// For any Number on which `is_i64` returns true, `as_i64` is guaranteed to
|
||||
/// return the integer value.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let big = i64::max_value() as u64 + 10;
|
||||
/// let v = json!({ "a": 64, "b": big, "c": 256.0 });
|
||||
///
|
||||
/// assert!(v["a"].is_i64());
|
||||
///
|
||||
/// // Greater than i64::MAX.
|
||||
/// assert!(!v["b"].is_i64());
|
||||
///
|
||||
/// // Numbers with a decimal point are not considered integers.
|
||||
/// assert!(!v["c"].is_i64());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_i64(&self) -> bool {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
match self.n {
|
||||
N::PosInt(v) => v <= i64::max_value() as u64,
|
||||
N::NegInt(_) => true,
|
||||
N::Float(_) => false,
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
self.as_i64().is_some()
|
||||
}
|
||||
|
||||
/// Returns true if the `Number` is an integer between zero and `u64::MAX`.
|
||||
///
|
||||
/// For any Number on which `is_u64` returns true, `as_u64` is guaranteed to
|
||||
/// return the integer value.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let v = json!({ "a": 64, "b": -64, "c": 256.0 });
|
||||
///
|
||||
/// assert!(v["a"].is_u64());
|
||||
///
|
||||
/// // Negative integer.
|
||||
/// assert!(!v["b"].is_u64());
|
||||
///
|
||||
/// // Numbers with a decimal point are not considered integers.
|
||||
/// assert!(!v["c"].is_u64());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_u64(&self) -> bool {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
match self.n {
|
||||
N::PosInt(_) => true,
|
||||
N::NegInt(_) | N::Float(_) => false,
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
self.as_u64().is_some()
|
||||
}
|
||||
|
||||
/// Returns true if the `Number` can be represented by f64.
|
||||
///
|
||||
/// For any Number on which `is_f64` returns true, `as_f64` is guaranteed to
|
||||
/// return the floating point value.
|
||||
///
|
||||
/// Currently this function returns true if and only if both `is_i64` and
|
||||
/// `is_u64` return false but this is not a guarantee in the future.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let v = json!({ "a": 256.0, "b": 64, "c": -64 });
|
||||
///
|
||||
/// assert!(v["a"].is_f64());
|
||||
///
|
||||
/// // Integers.
|
||||
/// assert!(!v["b"].is_f64());
|
||||
/// assert!(!v["c"].is_f64());
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_f64(&self) -> bool {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
match self.n {
|
||||
N::Float(_) => true,
|
||||
N::PosInt(_) | N::NegInt(_) => false,
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
{
|
||||
for c in self.n.chars() {
|
||||
if c == '.' || c == 'e' || c == 'E' {
|
||||
return self.n.parse::<f64>().ok().map_or(false, |f| f.is_finite());
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// If the `Number` is an integer, represent it as i64 if possible. Returns
|
||||
/// None otherwise.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let big = i64::max_value() as u64 + 10;
|
||||
/// let v = json!({ "a": 64, "b": big, "c": 256.0 });
|
||||
///
|
||||
/// assert_eq!(v["a"].as_i64(), Some(64));
|
||||
/// assert_eq!(v["b"].as_i64(), None);
|
||||
/// assert_eq!(v["c"].as_i64(), None);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_i64(&self) -> Option<i64> {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
match self.n {
|
||||
N::PosInt(n) => if n <= i64::max_value() as u64 {
|
||||
Some(n as i64)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
N::NegInt(n) => Some(n),
|
||||
N::Float(_) => None,
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
self.n.parse().ok()
|
||||
}
|
||||
|
||||
/// If the `Number` is an integer, represent it as u64 if possible. Returns
|
||||
/// None otherwise.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let v = json!({ "a": 64, "b": -64, "c": 256.0 });
|
||||
///
|
||||
/// assert_eq!(v["a"].as_u64(), Some(64));
|
||||
/// assert_eq!(v["b"].as_u64(), None);
|
||||
/// assert_eq!(v["c"].as_u64(), None);
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_u64(&self) -> Option<u64> {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
match self.n {
|
||||
N::PosInt(n) => Some(n),
|
||||
N::NegInt(_) | N::Float(_) => None,
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
self.n.parse().ok()
|
||||
}
|
||||
|
||||
/// Represents the number as f64 if possible. Returns None otherwise.
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let v = json!({ "a": 256.0, "b": 64, "c": -64 });
|
||||
///
|
||||
/// assert_eq!(v["a"].as_f64(), Some(256.0));
|
||||
/// assert_eq!(v["b"].as_f64(), Some(64.0));
|
||||
/// assert_eq!(v["c"].as_f64(), Some(-64.0));
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
match self.n {
|
||||
N::PosInt(n) => Some(n as f64),
|
||||
N::NegInt(n) => Some(n as f64),
|
||||
N::Float(n) => Some(n),
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
self.n.parse().ok()
|
||||
}
|
||||
|
||||
/// Converts a finite `f64` to a `Number`. Infinite or NaN values are not JSON
|
||||
/// numbers.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use std::f64;
|
||||
/// #
|
||||
/// # use serde_json::Number;
|
||||
/// #
|
||||
/// assert!(Number::from_f64(256.0).is_some());
|
||||
///
|
||||
/// assert!(Number::from_f64(f64::NAN).is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_f64(f: f64) -> Option<Number> {
|
||||
if f.is_finite() {
|
||||
let n = {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
{
|
||||
N::Float(f)
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
{
|
||||
ryu::Buffer::new().format(f).to_owned()
|
||||
}
|
||||
};
|
||||
Some(Number { n: n })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
/// Not public API. Only tests use this.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub fn from_string_unchecked(n: String) -> Self {
|
||||
Number { n: n }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Number {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.n {
|
||||
N::PosInt(u) => Display::fmt(&u, formatter),
|
||||
N::NegInt(i) => Display::fmt(&i, formatter),
|
||||
N::Float(f) => Display::fmt(&f, formatter),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
Display::fmt(&self.n, formatter)
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Number {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
let mut debug = formatter.debug_tuple("Number");
|
||||
match self.n {
|
||||
N::PosInt(i) => {
|
||||
debug.field(&i);
|
||||
}
|
||||
N::NegInt(i) => {
|
||||
debug.field(&i);
|
||||
}
|
||||
N::Float(f) => {
|
||||
debug.field(&f);
|
||||
}
|
||||
}
|
||||
debug.finish()
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(formatter, "Number({})", &self.n)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for Number {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
match self.n {
|
||||
N::PosInt(u) => serializer.serialize_u64(u),
|
||||
N::NegInt(i) => serializer.serialize_i64(i),
|
||||
N::Float(f) => serializer.serialize_f64(f),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
use serde::ser::SerializeStruct;
|
||||
|
||||
let mut s = serializer.serialize_struct(SERDE_STRUCT_NAME, 1)?;
|
||||
s.serialize_field(SERDE_STRUCT_FIELD_NAME, &self.n)?;
|
||||
s.end()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Number {
|
||||
#[inline]
|
||||
fn deserialize<D>(deserializer: D) -> Result<Number, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
struct NumberVisitor;
|
||||
|
||||
impl<'de> Visitor<'de> for NumberVisitor {
|
||||
type Value = Number;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a JSON number")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_i64<E>(self, value: i64) -> Result<Number, E> {
|
||||
Ok(value.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_u64<E>(self, value: u64) -> Result<Number, E> {
|
||||
Ok(value.into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_f64<E>(self, value: f64) -> Result<Number, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Number::from_f64(value).ok_or_else(|| de::Error::custom("not a JSON number"))
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
#[inline]
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Number, V::Error>
|
||||
where
|
||||
V: de::MapAccess<'de>,
|
||||
{
|
||||
let value = visitor.next_key::<NumberKey>()?;
|
||||
if value.is_none() {
|
||||
return Err(de::Error::invalid_type(Unexpected::Map, &self));
|
||||
}
|
||||
let v: NumberFromString = visitor.next_value()?;
|
||||
Ok(v.value)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(NumberVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
struct NumberKey;
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
impl<'de> de::Deserialize<'de> for NumberKey {
|
||||
fn deserialize<D>(deserializer: D) -> Result<NumberKey, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct FieldVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for FieldVisitor {
|
||||
type Value = ();
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("a valid number field")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<(), E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
if s == SERDE_STRUCT_FIELD_NAME {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(de::Error::custom("expected field with custom name"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_identifier(FieldVisitor)?;
|
||||
Ok(NumberKey)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
pub struct NumberFromString {
|
||||
pub value: Number,
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
impl<'de> de::Deserialize<'de> for NumberFromString {
|
||||
fn deserialize<D>(deserializer: D) -> Result<NumberFromString, D::Error>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct Visitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for Visitor {
|
||||
type Value = NumberFromString;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("string containing a number")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, s: &str) -> Result<NumberFromString, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
let n = try!(s.parse().map_err(de::Error::custom));
|
||||
Ok(NumberFromString { value: n })
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_str(Visitor)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
fn invalid_number() -> Error {
|
||||
Error::syntax(ErrorCode::InvalidNumber, 0, 0)
|
||||
}
|
||||
|
||||
macro_rules! deserialize_any {
|
||||
(@expand [$($num_string:tt)*]) => {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
#[inline]
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
match self.n {
|
||||
N::PosInt(u) => visitor.visit_u64(u),
|
||||
N::NegInt(i) => visitor.visit_i64(i),
|
||||
N::Float(f) => visitor.visit_f64(f),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
#[inline]
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where V: Visitor<'de>
|
||||
{
|
||||
if let Some(u) = self.as_u64() {
|
||||
return visitor.visit_u64(u);
|
||||
} else if let Some(i) = self.as_i64() {
|
||||
return visitor.visit_i64(i);
|
||||
} else if let Some(f) = self.as_f64() {
|
||||
if f.to_string() == self.n {
|
||||
return visitor.visit_f64(f);
|
||||
}
|
||||
}
|
||||
|
||||
visitor.visit_map(NumberDeserializer {
|
||||
number: Some(self.$($num_string)*),
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
(owned) => {
|
||||
deserialize_any!(@expand [n]);
|
||||
};
|
||||
|
||||
(ref) => {
|
||||
deserialize_any!(@expand [n.clone()]);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! deserialize_number {
|
||||
($deserialize:ident => $visit:ident) => {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
fn $deserialize<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
fn $deserialize<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.$visit(self.n.parse().map_err(|_| invalid_number())?)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserializer<'de> for Number {
|
||||
type Error = Error;
|
||||
|
||||
deserialize_any!(owned);
|
||||
|
||||
deserialize_number!(deserialize_i8 => visit_i8);
|
||||
deserialize_number!(deserialize_i16 => visit_i16);
|
||||
deserialize_number!(deserialize_i32 => visit_i32);
|
||||
deserialize_number!(deserialize_i64 => visit_i64);
|
||||
deserialize_number!(deserialize_u8 => visit_u8);
|
||||
deserialize_number!(deserialize_u16 => visit_u16);
|
||||
deserialize_number!(deserialize_u32 => visit_u32);
|
||||
deserialize_number!(deserialize_u64 => visit_u64);
|
||||
deserialize_number!(deserialize_f32 => visit_f32);
|
||||
deserialize_number!(deserialize_f64 => visit_f64);
|
||||
|
||||
serde_if_integer128! {
|
||||
deserialize_number!(deserialize_i128 => visit_i128);
|
||||
deserialize_number!(deserialize_u128 => visit_u128);
|
||||
}
|
||||
|
||||
forward_to_deserialize_any! {
|
||||
bool char str string bytes byte_buf option unit unit_struct
|
||||
newtype_struct seq tuple tuple_struct map struct enum identifier
|
||||
ignored_any
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, 'a> Deserializer<'de> for &'a Number {
|
||||
type Error = Error;
|
||||
|
||||
deserialize_any!(ref);
|
||||
|
||||
deserialize_number!(deserialize_i8 => visit_i8);
|
||||
deserialize_number!(deserialize_i16 => visit_i16);
|
||||
deserialize_number!(deserialize_i32 => visit_i32);
|
||||
deserialize_number!(deserialize_i64 => visit_i64);
|
||||
deserialize_number!(deserialize_u8 => visit_u8);
|
||||
deserialize_number!(deserialize_u16 => visit_u16);
|
||||
deserialize_number!(deserialize_u32 => visit_u32);
|
||||
deserialize_number!(deserialize_u64 => visit_u64);
|
||||
deserialize_number!(deserialize_f32 => visit_f32);
|
||||
deserialize_number!(deserialize_f64 => visit_f64);
|
||||
|
||||
serde_if_integer128! {
|
||||
deserialize_number!(deserialize_i128 => visit_i128);
|
||||
deserialize_number!(deserialize_u128 => visit_u128);
|
||||
}
|
||||
|
||||
forward_to_deserialize_any! {
|
||||
bool char str string bytes byte_buf option unit unit_struct
|
||||
newtype_struct seq tuple tuple_struct map struct enum identifier
|
||||
ignored_any
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
// Not public API. Should be pub(crate).
|
||||
#[doc(hidden)]
|
||||
pub struct NumberDeserializer {
|
||||
pub number: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
impl<'de> MapAccess<'de> for NumberDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
|
||||
where
|
||||
K: de::DeserializeSeed<'de>,
|
||||
{
|
||||
if self.number.is_none() {
|
||||
return Ok(None);
|
||||
}
|
||||
seed.deserialize(NumberFieldDeserializer).map(Some)
|
||||
}
|
||||
|
||||
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: de::DeserializeSeed<'de>,
|
||||
{
|
||||
seed.deserialize(self.number.take().unwrap().into_deserializer())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
struct NumberFieldDeserializer;
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
impl<'de> Deserializer<'de> for NumberFieldDeserializer {
|
||||
type Error = Error;
|
||||
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
|
||||
where
|
||||
V: de::Visitor<'de>,
|
||||
{
|
||||
visitor.visit_borrowed_str(SERDE_STRUCT_FIELD_NAME)
|
||||
}
|
||||
|
||||
forward_to_deserialize_any! {
|
||||
bool u8 u16 u32 u64 u128 i8 i16 i32 i64 i128 f32 f64 char str string seq
|
||||
bytes byte_buf map struct option unit newtype_struct ignored_any
|
||||
unit_struct tuple_struct tuple enum identifier
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParserNumber> for Number {
|
||||
fn from(value: ParserNumber) -> Self {
|
||||
let n = match value {
|
||||
ParserNumber::F64(f) => {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
{
|
||||
N::Float(f)
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
{
|
||||
f.to_string()
|
||||
}
|
||||
}
|
||||
ParserNumber::U64(u) => {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
{
|
||||
N::PosInt(u)
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
{
|
||||
u.to_string()
|
||||
}
|
||||
}
|
||||
ParserNumber::I64(i) => {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
{
|
||||
N::NegInt(i)
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
{
|
||||
i.to_string()
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
ParserNumber::String(s) => s,
|
||||
};
|
||||
Number { n: n }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_from_unsigned {
|
||||
(
|
||||
$($ty:ty),*
|
||||
) => {
|
||||
$(
|
||||
impl From<$ty> for Number {
|
||||
#[inline]
|
||||
fn from(u: $ty) -> Self {
|
||||
let n = {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
{ N::PosInt(u as u64) }
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
{
|
||||
let mut buf = Vec::new();
|
||||
itoa::write(&mut buf, u).unwrap();
|
||||
String::from_utf8(buf).unwrap()
|
||||
}
|
||||
};
|
||||
Number { n: n }
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_from_signed {
|
||||
(
|
||||
$($ty:ty),*
|
||||
) => {
|
||||
$(
|
||||
impl From<$ty> for Number {
|
||||
#[inline]
|
||||
fn from(i: $ty) -> Self {
|
||||
let n = {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
{
|
||||
if i < 0 {
|
||||
N::NegInt(i as i64)
|
||||
} else {
|
||||
N::PosInt(i as u64)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
{
|
||||
let mut buf = Vec::new();
|
||||
itoa::write(&mut buf, i).unwrap();
|
||||
String::from_utf8(buf).unwrap()
|
||||
}
|
||||
};
|
||||
Number { n: n }
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_from_unsigned!(u8, u16, u32, u64, usize);
|
||||
impl_from_signed!(i8, i16, i32, i64, isize);
|
||||
|
||||
impl Number {
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
// Not public API. Should be pub(crate).
|
||||
#[doc(hidden)]
|
||||
#[cold]
|
||||
pub fn unexpected(&self) -> Unexpected {
|
||||
match self.n {
|
||||
N::PosInt(u) => Unexpected::Unsigned(u),
|
||||
N::NegInt(i) => Unexpected::Signed(i),
|
||||
N::Float(f) => Unexpected::Float(f),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
// Not public API. Should be pub(crate).
|
||||
#[doc(hidden)]
|
||||
#[cold]
|
||||
pub fn unexpected(&self) -> Unexpected {
|
||||
Unexpected::Other("number")
|
||||
}
|
||||
}
|
|
@ -1,677 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::ops::Deref;
|
||||
use std::{char, cmp, io, str};
|
||||
|
||||
use iter::LineColIterator;
|
||||
|
||||
use super::error::{Error, ErrorCode, Result};
|
||||
|
||||
/// Trait used by the deserializer for iterating over input. This is manually
|
||||
/// "specialized" for iterating over &[u8]. Once feature(specialization) is
|
||||
/// stable we can use actual specialization.
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented for types outside of
|
||||
/// `serde_json`.
|
||||
pub trait Read<'de>: private::Sealed {
|
||||
#[doc(hidden)]
|
||||
fn next(&mut self) -> io::Result<Option<u8>>;
|
||||
#[doc(hidden)]
|
||||
fn peek(&mut self) -> io::Result<Option<u8>>;
|
||||
|
||||
/// Only valid after a call to peek(). Discards the peeked byte.
|
||||
#[doc(hidden)]
|
||||
fn discard(&mut self);
|
||||
|
||||
/// Position of the most recent call to next().
|
||||
///
|
||||
/// The most recent call was probably next() and not peek(), but this method
|
||||
/// should try to return a sensible result if the most recent call was
|
||||
/// actually peek() because we don't always know.
|
||||
///
|
||||
/// Only called in case of an error, so performance is not important.
|
||||
#[doc(hidden)]
|
||||
fn position(&self) -> Position;
|
||||
|
||||
/// Position of the most recent call to peek().
|
||||
///
|
||||
/// The most recent call was probably peek() and not next(), but this method
|
||||
/// should try to return a sensible result if the most recent call was
|
||||
/// actually next() because we don't always know.
|
||||
///
|
||||
/// Only called in case of an error, so performance is not important.
|
||||
#[doc(hidden)]
|
||||
fn peek_position(&self) -> Position;
|
||||
|
||||
/// Offset from the beginning of the input to the next byte that would be
|
||||
/// returned by next() or peek().
|
||||
#[doc(hidden)]
|
||||
fn byte_offset(&self) -> usize;
|
||||
|
||||
/// Assumes the previous byte was a quotation mark. Parses a JSON-escaped
|
||||
/// string until the next quotation mark using the given scratch space if
|
||||
/// necessary. The scratch space is initially empty.
|
||||
#[doc(hidden)]
|
||||
fn parse_str<'s>(&'s mut self, scratch: &'s mut Vec<u8>) -> Result<Reference<'de, 's, str>>;
|
||||
|
||||
/// Assumes the previous byte was a quotation mark. Parses a JSON-escaped
|
||||
/// string until the next quotation mark using the given scratch space if
|
||||
/// necessary. The scratch space is initially empty.
|
||||
///
|
||||
/// This function returns the raw bytes in the string with escape sequences
|
||||
/// expanded but without performing unicode validation.
|
||||
#[doc(hidden)]
|
||||
fn parse_str_raw<'s>(
|
||||
&'s mut self,
|
||||
scratch: &'s mut Vec<u8>,
|
||||
) -> Result<Reference<'de, 's, [u8]>>;
|
||||
|
||||
/// Assumes the previous byte was a quotation mark. Parses a JSON-escaped
|
||||
/// string until the next quotation mark but discards the data.
|
||||
#[doc(hidden)]
|
||||
fn ignore_str(&mut self) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct Position {
|
||||
pub line: usize,
|
||||
pub column: usize,
|
||||
}
|
||||
|
||||
pub enum Reference<'b, 'c, T: ?Sized + 'static> {
|
||||
Borrowed(&'b T),
|
||||
Copied(&'c T),
|
||||
}
|
||||
|
||||
impl<'b, 'c, T: ?Sized + 'static> Deref for Reference<'b, 'c, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match *self {
|
||||
Reference::Borrowed(b) => b,
|
||||
Reference::Copied(c) => c,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// JSON input source that reads from a std::io input stream.
|
||||
pub struct IoRead<R>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
iter: LineColIterator<io::Bytes<R>>,
|
||||
/// Temporary storage of peeked byte.
|
||||
ch: Option<u8>,
|
||||
}
|
||||
|
||||
/// JSON input source that reads from a slice of bytes.
|
||||
//
|
||||
// This is more efficient than other iterators because peek() can be read-only
|
||||
// and we can compute line/col position only if an error happens.
|
||||
pub struct SliceRead<'a> {
|
||||
slice: &'a [u8],
|
||||
/// Index of the *next* byte that will be returned by next() or peek().
|
||||
index: usize,
|
||||
}
|
||||
|
||||
/// JSON input source that reads from a UTF-8 string.
|
||||
//
|
||||
// Able to elide UTF-8 checks by assuming that the input is valid UTF-8.
|
||||
pub struct StrRead<'a> {
|
||||
delegate: SliceRead<'a>,
|
||||
}
|
||||
|
||||
// Prevent users from implementing the Read trait.
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl<R> IoRead<R>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
/// Create a JSON input source to read from a std::io input stream.
|
||||
pub fn new(reader: R) -> Self {
|
||||
IoRead {
|
||||
iter: LineColIterator::new(reader.bytes()),
|
||||
ch: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R> private::Sealed for IoRead<R> where R: io::Read {}
|
||||
|
||||
impl<R> IoRead<R>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
fn parse_str_bytes<'s, T, F>(
|
||||
&'s mut self,
|
||||
scratch: &'s mut Vec<u8>,
|
||||
validate: bool,
|
||||
result: F,
|
||||
) -> Result<T>
|
||||
where
|
||||
T: 's,
|
||||
F: FnOnce(&'s Self, &'s [u8]) -> Result<T>,
|
||||
{
|
||||
loop {
|
||||
let ch = try!(next_or_eof(self));
|
||||
if !ESCAPE[ch as usize] {
|
||||
scratch.push(ch);
|
||||
continue;
|
||||
}
|
||||
match ch {
|
||||
b'"' => {
|
||||
return result(self, scratch);
|
||||
}
|
||||
b'\\' => {
|
||||
try!(parse_escape(self, scratch));
|
||||
}
|
||||
_ => {
|
||||
if validate {
|
||||
return error(self, ErrorCode::ControlCharacterWhileParsingString);
|
||||
}
|
||||
scratch.push(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, R> Read<'de> for IoRead<R>
|
||||
where
|
||||
R: io::Read,
|
||||
{
|
||||
#[inline]
|
||||
fn next(&mut self) -> io::Result<Option<u8>> {
|
||||
match self.ch.take() {
|
||||
Some(ch) => Ok(Some(ch)),
|
||||
None => match self.iter.next() {
|
||||
Some(Err(err)) => Err(err),
|
||||
Some(Ok(ch)) => Ok(Some(ch)),
|
||||
None => Ok(None),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn peek(&mut self) -> io::Result<Option<u8>> {
|
||||
match self.ch {
|
||||
Some(ch) => Ok(Some(ch)),
|
||||
None => match self.iter.next() {
|
||||
Some(Err(err)) => Err(err),
|
||||
Some(Ok(ch)) => {
|
||||
self.ch = Some(ch);
|
||||
Ok(self.ch)
|
||||
}
|
||||
None => Ok(None),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn discard(&mut self) {
|
||||
self.ch = None;
|
||||
}
|
||||
|
||||
fn position(&self) -> Position {
|
||||
Position {
|
||||
line: self.iter.line(),
|
||||
column: self.iter.col(),
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_position(&self) -> Position {
|
||||
// The LineColIterator updates its position during peek() so it has the
|
||||
// right one here.
|
||||
self.position()
|
||||
}
|
||||
|
||||
fn byte_offset(&self) -> usize {
|
||||
match self.ch {
|
||||
Some(_) => self.iter.byte_offset() - 1,
|
||||
None => self.iter.byte_offset(),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_str<'s>(&'s mut self, scratch: &'s mut Vec<u8>) -> Result<Reference<'de, 's, str>> {
|
||||
self.parse_str_bytes(scratch, true, as_str)
|
||||
.map(Reference::Copied)
|
||||
}
|
||||
|
||||
fn parse_str_raw<'s>(
|
||||
&'s mut self,
|
||||
scratch: &'s mut Vec<u8>,
|
||||
) -> Result<Reference<'de, 's, [u8]>> {
|
||||
self.parse_str_bytes(scratch, false, |_, bytes| Ok(bytes))
|
||||
.map(Reference::Copied)
|
||||
}
|
||||
|
||||
fn ignore_str(&mut self) -> Result<()> {
|
||||
loop {
|
||||
let ch = try!(next_or_eof(self));
|
||||
if !ESCAPE[ch as usize] {
|
||||
continue;
|
||||
}
|
||||
match ch {
|
||||
b'"' => {
|
||||
return Ok(());
|
||||
}
|
||||
b'\\' => {
|
||||
try!(ignore_escape(self));
|
||||
}
|
||||
_ => {
|
||||
return error(self, ErrorCode::ControlCharacterWhileParsingString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl<'a> SliceRead<'a> {
|
||||
/// Create a JSON input source to read from a slice of bytes.
|
||||
pub fn new(slice: &'a [u8]) -> Self {
|
||||
SliceRead {
|
||||
slice: slice,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn position_of_index(&self, i: usize) -> Position {
|
||||
let mut position = Position { line: 1, column: 0 };
|
||||
for ch in &self.slice[..i] {
|
||||
match *ch {
|
||||
b'\n' => {
|
||||
position.line += 1;
|
||||
position.column = 0;
|
||||
}
|
||||
_ => {
|
||||
position.column += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
position
|
||||
}
|
||||
|
||||
/// The big optimization here over IoRead is that if the string contains no
|
||||
/// backslash escape sequences, the returned &str is a slice of the raw JSON
|
||||
/// data so we avoid copying into the scratch space.
|
||||
fn parse_str_bytes<'s, T: ?Sized, F>(
|
||||
&'s mut self,
|
||||
scratch: &'s mut Vec<u8>,
|
||||
validate: bool,
|
||||
result: F,
|
||||
) -> Result<Reference<'a, 's, T>>
|
||||
where
|
||||
T: 's,
|
||||
F: for<'f> FnOnce(&'s Self, &'f [u8]) -> Result<&'f T>,
|
||||
{
|
||||
// Index of the first byte not yet copied into the scratch space.
|
||||
let mut start = self.index;
|
||||
|
||||
loop {
|
||||
while self.index < self.slice.len() && !ESCAPE[self.slice[self.index] as usize] {
|
||||
self.index += 1;
|
||||
}
|
||||
if self.index == self.slice.len() {
|
||||
return error(self, ErrorCode::EofWhileParsingString);
|
||||
}
|
||||
match self.slice[self.index] {
|
||||
b'"' => {
|
||||
if scratch.is_empty() {
|
||||
// Fast path: return a slice of the raw JSON without any
|
||||
// copying.
|
||||
let borrowed = &self.slice[start..self.index];
|
||||
self.index += 1;
|
||||
return result(self, borrowed).map(Reference::Borrowed);
|
||||
} else {
|
||||
scratch.extend_from_slice(&self.slice[start..self.index]);
|
||||
self.index += 1;
|
||||
return result(self, scratch).map(Reference::Copied);
|
||||
}
|
||||
}
|
||||
b'\\' => {
|
||||
scratch.extend_from_slice(&self.slice[start..self.index]);
|
||||
self.index += 1;
|
||||
try!(parse_escape(self, scratch));
|
||||
start = self.index;
|
||||
}
|
||||
_ => {
|
||||
if validate {
|
||||
return error(self, ErrorCode::ControlCharacterWhileParsingString);
|
||||
}
|
||||
self.index += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> private::Sealed for SliceRead<'a> {}
|
||||
|
||||
impl<'a> Read<'a> for SliceRead<'a> {
|
||||
#[inline]
|
||||
fn next(&mut self) -> io::Result<Option<u8>> {
|
||||
// `Ok(self.slice.get(self.index).map(|ch| { self.index += 1; *ch }))`
|
||||
// is about 10% slower.
|
||||
Ok(if self.index < self.slice.len() {
|
||||
let ch = self.slice[self.index];
|
||||
self.index += 1;
|
||||
Some(ch)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn peek(&mut self) -> io::Result<Option<u8>> {
|
||||
// `Ok(self.slice.get(self.index).map(|ch| *ch))` is about 10% slower
|
||||
// for some reason.
|
||||
Ok(if self.index < self.slice.len() {
|
||||
Some(self.slice[self.index])
|
||||
} else {
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn discard(&mut self) {
|
||||
self.index += 1;
|
||||
}
|
||||
|
||||
fn position(&self) -> Position {
|
||||
self.position_of_index(self.index)
|
||||
}
|
||||
|
||||
fn peek_position(&self) -> Position {
|
||||
// Cap it at slice.len() just in case the most recent call was next()
|
||||
// and it returned the last byte.
|
||||
self.position_of_index(cmp::min(self.slice.len(), self.index + 1))
|
||||
}
|
||||
|
||||
fn byte_offset(&self) -> usize {
|
||||
self.index
|
||||
}
|
||||
|
||||
fn parse_str<'s>(&'s mut self, scratch: &'s mut Vec<u8>) -> Result<Reference<'a, 's, str>> {
|
||||
self.parse_str_bytes(scratch, true, as_str)
|
||||
}
|
||||
|
||||
fn parse_str_raw<'s>(
|
||||
&'s mut self,
|
||||
scratch: &'s mut Vec<u8>,
|
||||
) -> Result<Reference<'a, 's, [u8]>> {
|
||||
self.parse_str_bytes(scratch, false, |_, bytes| Ok(bytes))
|
||||
}
|
||||
|
||||
fn ignore_str(&mut self) -> Result<()> {
|
||||
loop {
|
||||
while self.index < self.slice.len() && !ESCAPE[self.slice[self.index] as usize] {
|
||||
self.index += 1;
|
||||
}
|
||||
if self.index == self.slice.len() {
|
||||
return error(self, ErrorCode::EofWhileParsingString);
|
||||
}
|
||||
match self.slice[self.index] {
|
||||
b'"' => {
|
||||
self.index += 1;
|
||||
return Ok(());
|
||||
}
|
||||
b'\\' => {
|
||||
self.index += 1;
|
||||
try!(ignore_escape(self));
|
||||
}
|
||||
_ => {
|
||||
return error(self, ErrorCode::ControlCharacterWhileParsingString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
impl<'a> StrRead<'a> {
|
||||
/// Create a JSON input source to read from a UTF-8 string.
|
||||
pub fn new(s: &'a str) -> Self {
|
||||
StrRead {
|
||||
delegate: SliceRead::new(s.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> private::Sealed for StrRead<'a> {}
|
||||
|
||||
impl<'a> Read<'a> for StrRead<'a> {
|
||||
#[inline]
|
||||
fn next(&mut self) -> io::Result<Option<u8>> {
|
||||
self.delegate.next()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn peek(&mut self) -> io::Result<Option<u8>> {
|
||||
self.delegate.peek()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn discard(&mut self) {
|
||||
self.delegate.discard();
|
||||
}
|
||||
|
||||
fn position(&self) -> Position {
|
||||
self.delegate.position()
|
||||
}
|
||||
|
||||
fn peek_position(&self) -> Position {
|
||||
self.delegate.peek_position()
|
||||
}
|
||||
|
||||
fn byte_offset(&self) -> usize {
|
||||
self.delegate.byte_offset()
|
||||
}
|
||||
|
||||
fn parse_str<'s>(&'s mut self, scratch: &'s mut Vec<u8>) -> Result<Reference<'a, 's, str>> {
|
||||
self.delegate.parse_str_bytes(scratch, true, |_, bytes| {
|
||||
// The input is assumed to be valid UTF-8 and the \u-escapes are
|
||||
// checked along the way, so don't need to check here.
|
||||
Ok(unsafe { str::from_utf8_unchecked(bytes) })
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_str_raw<'s>(
|
||||
&'s mut self,
|
||||
scratch: &'s mut Vec<u8>,
|
||||
) -> Result<Reference<'a, 's, [u8]>> {
|
||||
self.delegate.parse_str_raw(scratch)
|
||||
}
|
||||
|
||||
fn ignore_str(&mut self) -> Result<()> {
|
||||
self.delegate.ignore_str()
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const CT: bool = true; // control character \x00...\x1F
|
||||
const QU: bool = true; // quote \x22
|
||||
const BS: bool = true; // backslash \x5C
|
||||
const O: bool = false; // allow unescaped
|
||||
|
||||
// Lookup table of bytes that must be escaped. A value of true at index i means
|
||||
// that byte i requires an escape sequence in the input.
|
||||
#[cfg_attr(rustfmt, rustfmt_skip)]
|
||||
static ESCAPE: [bool; 256] = [
|
||||
// 1 2 3 4 5 6 7 8 9 A B C D E F
|
||||
CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 0
|
||||
CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, CT, // 1
|
||||
O, O, QU, O, O, O, O, O, O, O, O, O, O, O, O, O, // 2
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // 3
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // 4
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, BS, O, O, O, // 5
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // 6
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // 7
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // 8
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // 9
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // A
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // B
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // C
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // D
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // E
|
||||
O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, O, // F
|
||||
];
|
||||
|
||||
fn next_or_eof<'de, R: ?Sized + Read<'de>>(read: &mut R) -> Result<u8> {
|
||||
match try!(read.next().map_err(Error::io)) {
|
||||
Some(b) => Ok(b),
|
||||
None => error(read, ErrorCode::EofWhileParsingString),
|
||||
}
|
||||
}
|
||||
|
||||
fn error<'de, R: ?Sized + Read<'de>, T>(read: &R, reason: ErrorCode) -> Result<T> {
|
||||
let position = read.position();
|
||||
Err(Error::syntax(reason, position.line, position.column))
|
||||
}
|
||||
|
||||
fn as_str<'de, 's, R: Read<'de>>(read: &R, slice: &'s [u8]) -> Result<&'s str> {
|
||||
str::from_utf8(slice).or_else(|_| error(read, ErrorCode::InvalidUnicodeCodePoint))
|
||||
}
|
||||
|
||||
/// Parses a JSON escape sequence and appends it into the scratch space. Assumes
|
||||
/// the previous byte read was a backslash.
|
||||
fn parse_escape<'de, R: Read<'de>>(read: &mut R, scratch: &mut Vec<u8>) -> Result<()> {
|
||||
let ch = try!(next_or_eof(read));
|
||||
|
||||
match ch {
|
||||
b'"' => scratch.push(b'"'),
|
||||
b'\\' => scratch.push(b'\\'),
|
||||
b'/' => scratch.push(b'/'),
|
||||
b'b' => scratch.push(b'\x08'),
|
||||
b'f' => scratch.push(b'\x0c'),
|
||||
b'n' => scratch.push(b'\n'),
|
||||
b'r' => scratch.push(b'\r'),
|
||||
b't' => scratch.push(b'\t'),
|
||||
b'u' => {
|
||||
let c = match try!(decode_hex_escape(read)) {
|
||||
0xDC00...0xDFFF => {
|
||||
return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape);
|
||||
}
|
||||
|
||||
// Non-BMP characters are encoded as a sequence of
|
||||
// two hex escapes, representing UTF-16 surrogates.
|
||||
n1 @ 0xD800...0xDBFF => {
|
||||
if try!(next_or_eof(read)) != b'\\' {
|
||||
return error(read, ErrorCode::UnexpectedEndOfHexEscape);
|
||||
}
|
||||
if try!(next_or_eof(read)) != b'u' {
|
||||
return error(read, ErrorCode::UnexpectedEndOfHexEscape);
|
||||
}
|
||||
|
||||
let n2 = try!(decode_hex_escape(read));
|
||||
|
||||
if n2 < 0xDC00 || n2 > 0xDFFF {
|
||||
return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape);
|
||||
}
|
||||
|
||||
let n = (((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000;
|
||||
|
||||
match char::from_u32(n) {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return error(read, ErrorCode::InvalidUnicodeCodePoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
n => match char::from_u32(n as u32) {
|
||||
Some(c) => c,
|
||||
None => {
|
||||
return error(read, ErrorCode::InvalidUnicodeCodePoint);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
scratch.extend_from_slice(c.encode_utf8(&mut [0_u8; 4]).as_bytes());
|
||||
}
|
||||
_ => {
|
||||
return error(read, ErrorCode::InvalidEscape);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parses a JSON escape sequence and discards the value. Assumes the previous
|
||||
/// byte read was a backslash.
|
||||
fn ignore_escape<'de, R: ?Sized + Read<'de>>(read: &mut R) -> Result<()> {
|
||||
let ch = try!(next_or_eof(read));
|
||||
|
||||
match ch {
|
||||
b'"' | b'\\' | b'/' | b'b' | b'f' | b'n' | b'r' | b't' => {}
|
||||
b'u' => {
|
||||
let n = match try!(decode_hex_escape(read)) {
|
||||
0xDC00...0xDFFF => {
|
||||
return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape);
|
||||
}
|
||||
|
||||
// Non-BMP characters are encoded as a sequence of
|
||||
// two hex escapes, representing UTF-16 surrogates.
|
||||
n1 @ 0xD800...0xDBFF => {
|
||||
if try!(next_or_eof(read)) != b'\\' {
|
||||
return error(read, ErrorCode::UnexpectedEndOfHexEscape);
|
||||
}
|
||||
if try!(next_or_eof(read)) != b'u' {
|
||||
return error(read, ErrorCode::UnexpectedEndOfHexEscape);
|
||||
}
|
||||
|
||||
let n2 = try!(decode_hex_escape(read));
|
||||
|
||||
if n2 < 0xDC00 || n2 > 0xDFFF {
|
||||
return error(read, ErrorCode::LoneLeadingSurrogateInHexEscape);
|
||||
}
|
||||
|
||||
(((n1 - 0xD800) as u32) << 10 | (n2 - 0xDC00) as u32) + 0x1_0000
|
||||
}
|
||||
|
||||
n => n as u32,
|
||||
};
|
||||
|
||||
if char::from_u32(n).is_none() {
|
||||
return error(read, ErrorCode::InvalidUnicodeCodePoint);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
return error(read, ErrorCode::InvalidEscape);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn decode_hex_escape<'de, R: ?Sized + Read<'de>>(read: &mut R) -> Result<u16> {
|
||||
let mut n = 0;
|
||||
for _ in 0..4 {
|
||||
n = match try!(next_or_eof(read)) {
|
||||
c @ b'0'...b'9' => n * 16_u16 + ((c as u16) - (b'0' as u16)),
|
||||
b'a' | b'A' => n * 16_u16 + 10_u16,
|
||||
b'b' | b'B' => n * 16_u16 + 11_u16,
|
||||
b'c' | b'C' => n * 16_u16 + 12_u16,
|
||||
b'd' | b'D' => n * 16_u16 + 13_u16,
|
||||
b'e' | b'E' => n * 16_u16 + 14_u16,
|
||||
b'f' | b'F' => n * 16_u16 + 15_u16,
|
||||
_ => {
|
||||
return error(read, ErrorCode::InvalidEscape);
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(n)
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,266 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::Value;
|
||||
use map::Map;
|
||||
use number::Number;
|
||||
|
||||
macro_rules! from_integer {
|
||||
($($ty:ident)*) => {
|
||||
$(
|
||||
impl From<$ty> for Value {
|
||||
fn from(n: $ty) -> Self {
|
||||
Value::Number(n.into())
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
from_integer! {
|
||||
i8 i16 i32 i64 isize
|
||||
u8 u16 u32 u64 usize
|
||||
}
|
||||
|
||||
impl From<f32> for Value {
|
||||
/// Convert 32-bit floating point number to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let f: f32 = 13.37;
|
||||
/// let x: Value = f.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: f32) -> Self {
|
||||
From::from(f as f64)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<f64> for Value {
|
||||
/// Convert 64-bit floating point number to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let f: f64 = 13.37;
|
||||
/// let x: Value = f.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: f64) -> Self {
|
||||
Number::from_f64(f).map_or(Value::Null, Value::Number)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<bool> for Value {
|
||||
/// Convert boolean to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let b = false;
|
||||
/// let x: Value = b.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: bool) -> Self {
|
||||
Value::Bool(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for Value {
|
||||
/// Convert `String` to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let s: String = "lorem".to_string();
|
||||
/// let x: Value = s.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: String) -> Self {
|
||||
Value::String(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for Value {
|
||||
/// Convert string slice to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let s: &str = "lorem";
|
||||
/// let x: Value = s.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: &str) -> Self {
|
||||
Value::String(f.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<Cow<'a, str>> for Value {
|
||||
/// Convert copy-on-write string to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
/// use std::borrow::Cow;
|
||||
///
|
||||
/// let s: Cow<str> = Cow::Borrowed("lorem");
|
||||
/// let x: Value = s.into();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
/// use std::borrow::Cow;
|
||||
///
|
||||
/// let s: Cow<str> = Cow::Owned("lorem".to_string());
|
||||
/// let x: Value = s.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: Cow<'a, str>) -> Self {
|
||||
Value::String(f.into_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Map<String, Value>> for Value {
|
||||
/// Convert map (with string keys) to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::{Map, Value};
|
||||
///
|
||||
/// let mut m = Map::new();
|
||||
/// m.insert("Lorem".to_string(), "ipsum".into());
|
||||
/// let x: Value = m.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: Map<String, Value>) -> Self {
|
||||
Value::Object(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Value>> From<Vec<T>> for Value {
|
||||
/// Convert a `Vec` to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let v = vec!["lorem", "ipsum", "dolor"];
|
||||
/// let x: Value = v.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: Vec<T>) -> Self {
|
||||
Value::Array(f.into_iter().map(Into::into).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Clone + Into<Value>> From<&'a [T]> for Value {
|
||||
/// Convert a slice to `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let v: &[&str] = &["lorem", "ipsum", "dolor"];
|
||||
/// let x: Value = v.into();
|
||||
/// # }
|
||||
/// ```
|
||||
fn from(f: &'a [T]) -> Self {
|
||||
Value::Array(f.into_iter().cloned().map(Into::into).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Value>> ::std::iter::FromIterator<T> for Value {
|
||||
/// Convert an iteratable type to a `Value`
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let v = std::iter::repeat(42).take(5);
|
||||
/// let x: Value = v.collect();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let v: Vec<_> = vec!["lorem", "ipsum", "dolor"];
|
||||
/// let x: Value = v.into_iter().collect();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// use std::iter::FromIterator;
|
||||
/// use serde_json::Value;
|
||||
///
|
||||
/// let x: Value = Value::from_iter(vec!["lorem", "ipsum", "dolor"]);
|
||||
/// # }
|
||||
/// ```
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
Value::Array(iter.into_iter().map(Into::into).collect())
|
||||
}
|
||||
}
|
|
@ -1,274 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
use super::Value;
|
||||
use map::Map;
|
||||
|
||||
/// A type that can be used to index into a `serde_json::Value`.
|
||||
///
|
||||
/// The [`get`] and [`get_mut`] methods of `Value` accept any type that
|
||||
/// implements `Index`, as does the [square-bracket indexing operator]. This
|
||||
/// trait is implemented for strings which are used as the index into a JSON
|
||||
/// map, and for `usize` which is used as the index into a JSON array.
|
||||
///
|
||||
/// [`get`]: ../enum.Value.html#method.get
|
||||
/// [`get_mut`]: ../enum.Value.html#method.get_mut
|
||||
/// [square-bracket indexing operator]: ../enum.Value.html#impl-Index%3CI%3E
|
||||
///
|
||||
/// This trait is sealed and cannot be implemented for types outside of
|
||||
/// `serde_json`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let data = json!({ "inner": [1, 2, 3] });
|
||||
///
|
||||
/// // Data is a JSON map so it can be indexed with a string.
|
||||
/// let inner = &data["inner"];
|
||||
///
|
||||
/// // Inner is a JSON array so it can be indexed with an integer.
|
||||
/// let first = &inner[0];
|
||||
///
|
||||
/// assert_eq!(first, 1);
|
||||
/// # }
|
||||
/// ```
|
||||
pub trait Index: private::Sealed {
|
||||
/// Return None if the key is not already in the array or object.
|
||||
#[doc(hidden)]
|
||||
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value>;
|
||||
|
||||
/// Return None if the key is not already in the array or object.
|
||||
#[doc(hidden)]
|
||||
fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value>;
|
||||
|
||||
/// Panic if array index out of bounds. If key is not already in the object,
|
||||
/// insert it with a value of null. Panic if Value is a type that cannot be
|
||||
/// indexed into, except if Value is null then it can be treated as an empty
|
||||
/// object.
|
||||
#[doc(hidden)]
|
||||
fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value;
|
||||
}
|
||||
|
||||
impl Index for usize {
|
||||
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
|
||||
match *v {
|
||||
Value::Array(ref vec) => vec.get(*self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
|
||||
match *v {
|
||||
Value::Array(ref mut vec) => vec.get_mut(*self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
|
||||
match *v {
|
||||
Value::Array(ref mut vec) => {
|
||||
let len = vec.len();
|
||||
vec.get_mut(*self).unwrap_or_else(|| {
|
||||
panic!(
|
||||
"cannot access index {} of JSON array of length {}",
|
||||
self, len
|
||||
)
|
||||
})
|
||||
}
|
||||
_ => panic!("cannot access index {} of JSON {}", self, Type(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index for str {
|
||||
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
|
||||
match *v {
|
||||
Value::Object(ref map) => map.get(self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
|
||||
match *v {
|
||||
Value::Object(ref mut map) => map.get_mut(self),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
|
||||
if let Value::Null = *v {
|
||||
*v = Value::Object(Map::new());
|
||||
}
|
||||
match *v {
|
||||
Value::Object(ref mut map) => map.entry(self.to_owned()).or_insert(Value::Null),
|
||||
_ => panic!("cannot access key {:?} in JSON {}", self, Type(v)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Index for String {
|
||||
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
|
||||
self[..].index_into(v)
|
||||
}
|
||||
fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
|
||||
self[..].index_into_mut(v)
|
||||
}
|
||||
fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
|
||||
self[..].index_or_insert(v)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> Index for &'a T
|
||||
where
|
||||
T: Index,
|
||||
{
|
||||
fn index_into<'v>(&self, v: &'v Value) -> Option<&'v Value> {
|
||||
(**self).index_into(v)
|
||||
}
|
||||
fn index_into_mut<'v>(&self, v: &'v mut Value) -> Option<&'v mut Value> {
|
||||
(**self).index_into_mut(v)
|
||||
}
|
||||
fn index_or_insert<'v>(&self, v: &'v mut Value) -> &'v mut Value {
|
||||
(**self).index_or_insert(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Prevent users from implementing the Index trait.
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
impl Sealed for usize {}
|
||||
impl Sealed for str {}
|
||||
impl Sealed for String {}
|
||||
impl<'a, T: ?Sized> Sealed for &'a T where T: Sealed {}
|
||||
}
|
||||
|
||||
/// Used in panic messages.
|
||||
struct Type<'a>(&'a Value);
|
||||
|
||||
impl<'a> fmt::Display for Type<'a> {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self.0 {
|
||||
Value::Null => formatter.write_str("null"),
|
||||
Value::Bool(_) => formatter.write_str("boolean"),
|
||||
Value::Number(_) => formatter.write_str("number"),
|
||||
Value::String(_) => formatter.write_str("string"),
|
||||
Value::Array(_) => formatter.write_str("array"),
|
||||
Value::Object(_) => formatter.write_str("object"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The usual semantics of Index is to panic on invalid indexing.
|
||||
//
|
||||
// That said, the usual semantics are for things like Vec and BTreeMap which
|
||||
// have different use cases than Value. If you are working with a Vec, you know
|
||||
// that you are working with a Vec and you can get the len of the Vec and make
|
||||
// sure your indices are within bounds. The Value use cases are more
|
||||
// loosey-goosey. You got some JSON from an endpoint and you want to pull values
|
||||
// out of it. Outside of this Index impl, you already have the option of using
|
||||
// value.as_array() and working with the Vec directly, or matching on
|
||||
// Value::Array and getting the Vec directly. The Index impl means you can skip
|
||||
// that and index directly into the thing using a concise syntax. You don't have
|
||||
// to check the type, you don't have to check the len, it is all about what you
|
||||
// expect the Value to look like.
|
||||
//
|
||||
// Basically the use cases that would be well served by panicking here are
|
||||
// better served by using one of the other approaches: get and get_mut,
|
||||
// as_array, or match. The value of this impl is that it adds a way of working
|
||||
// with Value that is not well served by the existing approaches: concise and
|
||||
// careless and sometimes that is exactly what you want.
|
||||
impl<I> ops::Index<I> for Value
|
||||
where
|
||||
I: Index,
|
||||
{
|
||||
type Output = Value;
|
||||
|
||||
/// Index into a `serde_json::Value` using the syntax `value[0]` or
|
||||
/// `value["k"]`.
|
||||
///
|
||||
/// Returns `Value::Null` if the type of `self` does not match the type of
|
||||
/// the index, for example if the index is a string and `self` is an array
|
||||
/// or a number. Also returns `Value::Null` if the given key does not exist
|
||||
/// in the map or the given index is not within the bounds of the array.
|
||||
///
|
||||
/// For retrieving deeply nested values, you should have a look at the
|
||||
/// `Value::pointer` method.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let data = json!({
|
||||
/// "x": {
|
||||
/// "y": ["z", "zz"]
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// assert_eq!(data["x"]["y"], json!(["z", "zz"]));
|
||||
/// assert_eq!(data["x"]["y"][0], json!("z"));
|
||||
///
|
||||
/// assert_eq!(data["a"], json!(null)); // returns null for undefined values
|
||||
/// assert_eq!(data["a"]["b"], json!(null)); // does not panic
|
||||
/// # }
|
||||
/// ```
|
||||
fn index(&self, index: I) -> &Value {
|
||||
static NULL: Value = Value::Null;
|
||||
index.index_into(self).unwrap_or(&NULL)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> ops::IndexMut<I> for Value
|
||||
where
|
||||
I: Index,
|
||||
{
|
||||
/// Write into a `serde_json::Value` using the syntax `value[0] = ...` or
|
||||
/// `value["k"] = ...`.
|
||||
///
|
||||
/// If the index is a number, the value must be an array of length bigger
|
||||
/// than the index. Indexing into a value that is not an array or an array
|
||||
/// that is too small will panic.
|
||||
///
|
||||
/// If the index is a string, the value must be an object or null which is
|
||||
/// treated like an empty object. If the key is not already present in the
|
||||
/// object, it will be inserted with a value of null. Indexing into a value
|
||||
/// that is neither an object nor null will panic.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # #[macro_use]
|
||||
/// # extern crate serde_json;
|
||||
/// #
|
||||
/// # fn main() {
|
||||
/// let mut data = json!({ "x": 0 });
|
||||
///
|
||||
/// // replace an existing key
|
||||
/// data["x"] = json!(1);
|
||||
///
|
||||
/// // insert a new key
|
||||
/// data["y"] = json!([false, false, false]);
|
||||
///
|
||||
/// // replace an array value
|
||||
/// data["y"][0] = json!(true);
|
||||
///
|
||||
/// // inserted a deeply nested key
|
||||
/// data["a"]["b"]["c"]["d"] = json!(true);
|
||||
///
|
||||
/// println!("{}", data);
|
||||
/// # }
|
||||
/// ```
|
||||
fn index_mut(&mut self, index: I) -> &mut Value {
|
||||
index.index_or_insert(self)
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,102 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::Value;
|
||||
|
||||
fn eq_i64(value: &Value, other: i64) -> bool {
|
||||
value.as_i64().map_or(false, |i| i == other)
|
||||
}
|
||||
|
||||
fn eq_u64(value: &Value, other: u64) -> bool {
|
||||
value.as_u64().map_or(false, |i| i == other)
|
||||
}
|
||||
|
||||
fn eq_f64(value: &Value, other: f64) -> bool {
|
||||
value.as_f64().map_or(false, |i| i == other)
|
||||
}
|
||||
|
||||
fn eq_bool(value: &Value, other: bool) -> bool {
|
||||
value.as_bool().map_or(false, |i| i == other)
|
||||
}
|
||||
|
||||
fn eq_str(value: &Value, other: &str) -> bool {
|
||||
value.as_str().map_or(false, |i| i == other)
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Value {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
eq_str(self, other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for Value {
|
||||
fn eq(&self, other: &&str) -> bool {
|
||||
eq_str(self, *other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Value> for str {
|
||||
fn eq(&self, other: &Value) -> bool {
|
||||
eq_str(other, self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Value> for &'a str {
|
||||
fn eq(&self, other: &Value) -> bool {
|
||||
eq_str(other, *self)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for Value {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
eq_str(self, other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Value> for String {
|
||||
fn eq(&self, other: &Value) -> bool {
|
||||
eq_str(other, self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! partialeq_numeric {
|
||||
($($eq:ident [$($ty:ty)*])*) => {
|
||||
$($(
|
||||
impl PartialEq<$ty> for Value {
|
||||
fn eq(&self, other: &$ty) -> bool {
|
||||
$eq(self, *other as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Value> for $ty {
|
||||
fn eq(&self, other: &Value) -> bool {
|
||||
$eq(other, *self as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<$ty> for &'a Value {
|
||||
fn eq(&self, other: &$ty) -> bool {
|
||||
$eq(*self, *other as _)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<$ty> for &'a mut Value {
|
||||
fn eq(&self, other: &$ty) -> bool {
|
||||
$eq(*self, *other as _)
|
||||
}
|
||||
}
|
||||
)*)*
|
||||
}
|
||||
}
|
||||
|
||||
partialeq_numeric! {
|
||||
eq_i64[i8 i16 i32 i64 isize]
|
||||
eq_u64[u8 u16 u32 u64 usize]
|
||||
eq_f64[f32 f64]
|
||||
eq_bool[bool]
|
||||
}
|
|
@ -1,827 +0,0 @@
|
|||
// Copyright 2017 Serde Developers
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use serde::ser::Impossible;
|
||||
use serde::{self, Serialize};
|
||||
|
||||
use error::{Error, ErrorCode};
|
||||
use map::Map;
|
||||
use number::Number;
|
||||
use value::{to_value, Value};
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
use serde::ser;
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
use number::{SERDE_STRUCT_FIELD_NAME, SERDE_STRUCT_NAME};
|
||||
|
||||
impl Serialize for Value {
|
||||
#[inline]
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: ::serde::Serializer,
|
||||
{
|
||||
match *self {
|
||||
Value::Null => serializer.serialize_unit(),
|
||||
Value::Bool(b) => serializer.serialize_bool(b),
|
||||
Value::Number(ref n) => n.serialize(serializer),
|
||||
Value::String(ref s) => serializer.serialize_str(s),
|
||||
Value::Array(ref v) => v.serialize(serializer),
|
||||
Value::Object(ref m) => {
|
||||
use serde::ser::SerializeMap;
|
||||
let mut map = try!(serializer.serialize_map(Some(m.len())));
|
||||
for (k, v) in m {
|
||||
try!(map.serialize_key(k));
|
||||
try!(map.serialize_value(v));
|
||||
}
|
||||
map.end()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Serializer;
|
||||
|
||||
impl serde::Serializer for Serializer {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = SerializeVec;
|
||||
type SerializeTuple = SerializeVec;
|
||||
type SerializeTupleStruct = SerializeVec;
|
||||
type SerializeTupleVariant = SerializeTupleVariant;
|
||||
type SerializeMap = SerializeMap;
|
||||
type SerializeStruct = SerializeMap;
|
||||
type SerializeStructVariant = SerializeStructVariant;
|
||||
|
||||
#[inline]
|
||||
fn serialize_bool(self, value: bool) -> Result<Value, Error> {
|
||||
Ok(Value::Bool(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i8(self, value: i8) -> Result<Value, Error> {
|
||||
self.serialize_i64(value as i64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i16(self, value: i16) -> Result<Value, Error> {
|
||||
self.serialize_i64(value as i64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_i32(self, value: i32) -> Result<Value, Error> {
|
||||
self.serialize_i64(value as i64)
|
||||
}
|
||||
|
||||
fn serialize_i64(self, value: i64) -> Result<Value, Error> {
|
||||
Ok(Value::Number(value.into()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u8(self, value: u8) -> Result<Value, Error> {
|
||||
self.serialize_u64(value as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u16(self, value: u16) -> Result<Value, Error> {
|
||||
self.serialize_u64(value as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u32(self, value: u32) -> Result<Value, Error> {
|
||||
self.serialize_u64(value as u64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_u64(self, value: u64) -> Result<Value, Error> {
|
||||
Ok(Value::Number(value.into()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_f32(self, value: f32) -> Result<Value, Error> {
|
||||
self.serialize_f64(value as f64)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_f64(self, value: f64) -> Result<Value, Error> {
|
||||
Ok(Number::from_f64(value).map_or(Value::Null, Value::Number))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_char(self, value: char) -> Result<Value, Error> {
|
||||
let mut s = String::new();
|
||||
s.push(value);
|
||||
self.serialize_str(&s)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_str(self, value: &str) -> Result<Value, Error> {
|
||||
Ok(Value::String(value.to_owned()))
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, value: &[u8]) -> Result<Value, Error> {
|
||||
let vec = value.iter().map(|&b| Value::Number(b.into())).collect();
|
||||
Ok(Value::Array(vec))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit(self) -> Result<Value, Error> {
|
||||
Ok(Value::Null)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Value, Error> {
|
||||
self.serialize_unit()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Value, Error> {
|
||||
self.serialize_str(variant)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T: ?Sized>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Value, Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: ?Sized>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Value, Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let mut values = Map::new();
|
||||
values.insert(String::from(variant), try!(to_value(&value)));
|
||||
Ok(Value::Object(values))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_none(self) -> Result<Value, Error> {
|
||||
self.serialize_unit()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Value, Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
|
||||
Ok(SerializeVec {
|
||||
vec: Vec::with_capacity(len.unwrap_or(0)),
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Error> {
|
||||
self.serialize_seq(Some(len))
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Error> {
|
||||
Ok(SerializeTupleVariant {
|
||||
name: String::from(variant),
|
||||
vec: Vec::with_capacity(len),
|
||||
})
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Error> {
|
||||
Ok(SerializeMap::Map {
|
||||
map: Map::new(),
|
||||
next_key: None,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "arbitrary_precision"))]
|
||||
fn serialize_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStruct, Error> {
|
||||
self.serialize_map(Some(len))
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
fn serialize_struct(
|
||||
self,
|
||||
name: &'static str,
|
||||
len: usize,
|
||||
) -> Result<Self::SerializeStruct, Error> {
|
||||
if name == SERDE_STRUCT_NAME {
|
||||
Ok(SerializeMap::Number { out_value: None })
|
||||
} else {
|
||||
self.serialize_map(Some(len))
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Error> {
|
||||
Ok(SerializeStructVariant {
|
||||
name: String::from(variant),
|
||||
map: Map::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SerializeVec {
|
||||
vec: Vec<Value>,
|
||||
}
|
||||
|
||||
pub struct SerializeTupleVariant {
|
||||
name: String,
|
||||
vec: Vec<Value>,
|
||||
}
|
||||
|
||||
pub enum SerializeMap {
|
||||
Map {
|
||||
map: Map<String, Value>,
|
||||
next_key: Option<String>,
|
||||
},
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
Number { out_value: Option<Value> },
|
||||
}
|
||||
|
||||
pub struct SerializeStructVariant {
|
||||
name: String,
|
||||
map: Map<String, Value>,
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeSeq for SerializeVec {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.vec.push(try!(to_value(&value)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
Ok(Value::Array(self.vec))
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTuple for SerializeVec {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
serde::ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
serde::ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTupleStruct for SerializeVec {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
serde::ser::SerializeSeq::serialize_element(self, value)
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
serde::ser::SerializeSeq::end(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.vec.push(try!(to_value(&value)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
let mut object = Map::new();
|
||||
|
||||
object.insert(self.name, Value::Array(self.vec));
|
||||
|
||||
Ok(Value::Object(object))
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeMap for SerializeMap {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match *self {
|
||||
SerializeMap::Map {
|
||||
ref mut next_key, ..
|
||||
} => {
|
||||
*next_key = Some(try!(key.serialize(MapKeySerializer)));
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
SerializeMap::Number { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match *self {
|
||||
SerializeMap::Map {
|
||||
ref mut map,
|
||||
ref mut next_key,
|
||||
} => {
|
||||
let key = next_key.take();
|
||||
// Panic because this indicates a bug in the program rather than an
|
||||
// expected failure.
|
||||
let key = key.expect("serialize_value called before serialize_key");
|
||||
map.insert(key, try!(to_value(&value)));
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
SerializeMap::Number { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
match self {
|
||||
SerializeMap::Map { map, .. } => Ok(Value::Object(map)),
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
SerializeMap::Number { .. } => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MapKeySerializer;
|
||||
|
||||
fn key_must_be_a_string() -> Error {
|
||||
Error::syntax(ErrorCode::KeyMustBeAString, 0, 0)
|
||||
}
|
||||
|
||||
impl serde::Serializer for MapKeySerializer {
|
||||
type Ok = String;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<String, Error>;
|
||||
type SerializeTuple = Impossible<String, Error>;
|
||||
type SerializeTupleStruct = Impossible<String, Error>;
|
||||
type SerializeTupleVariant = Impossible<String, Error>;
|
||||
type SerializeMap = Impossible<String, Error>;
|
||||
type SerializeStruct = Impossible<String, Error>;
|
||||
type SerializeStructVariant = Impossible<String, Error>;
|
||||
|
||||
#[inline]
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
variant: &'static str,
|
||||
) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(variant.to_owned())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_newtype_struct<T: ?Sized>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_bool(self, _value: bool) -> Result<Self::Ok, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_i8(self, value: i8) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_string())
|
||||
}
|
||||
|
||||
fn serialize_i16(self, value: i16) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_string())
|
||||
}
|
||||
|
||||
fn serialize_i32(self, value: i32) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_string())
|
||||
}
|
||||
|
||||
fn serialize_i64(self, value: i64) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_string())
|
||||
}
|
||||
|
||||
fn serialize_u8(self, value: u8) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_string())
|
||||
}
|
||||
|
||||
fn serialize_u16(self, value: u16) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_string())
|
||||
}
|
||||
|
||||
fn serialize_u32(self, value: u32) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_string())
|
||||
}
|
||||
|
||||
fn serialize_u64(self, value: u64) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_string())
|
||||
}
|
||||
|
||||
fn serialize_f32(self, _value: f32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_f64(self, _value: f64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_char(self, value: char) -> Result<Self::Ok, Self::Error> {
|
||||
Ok({
|
||||
let mut s = String::new();
|
||||
s.push(value);
|
||||
s
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
|
||||
Ok(value.to_owned())
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: ?Sized>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
Err(key_must_be_a_string())
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeStruct for SerializeMap {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
match *self {
|
||||
SerializeMap::Map { .. } => {
|
||||
try!(serde::ser::SerializeMap::serialize_key(self, key));
|
||||
serde::ser::SerializeMap::serialize_value(self, value)
|
||||
}
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
SerializeMap::Number { ref mut out_value } => {
|
||||
if key == SERDE_STRUCT_FIELD_NAME {
|
||||
*out_value = Some(value.serialize(NumberValueEmitter)?);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(invalid_number())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
match self {
|
||||
SerializeMap::Map { .. } => serde::ser::SerializeMap::end(self),
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
SerializeMap::Number { out_value, .. } => {
|
||||
Ok(out_value.expect("number value was not emitted"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl serde::ser::SerializeStructVariant for SerializeStructVariant {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
self.map.insert(String::from(key), try!(to_value(&value)));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn end(self) -> Result<Value, Error> {
|
||||
let mut object = Map::new();
|
||||
|
||||
object.insert(self.name, Value::Object(self.map));
|
||||
|
||||
Ok(Value::Object(object))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
struct NumberValueEmitter;
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
fn invalid_number() -> Error {
|
||||
Error::syntax(ErrorCode::InvalidNumber, 0, 0)
|
||||
}
|
||||
|
||||
#[cfg(feature = "arbitrary_precision")]
|
||||
impl ser::Serializer for NumberValueEmitter {
|
||||
type Ok = Value;
|
||||
type Error = Error;
|
||||
|
||||
type SerializeSeq = Impossible<Value, Error>;
|
||||
type SerializeTuple = Impossible<Value, Error>;
|
||||
type SerializeTupleStruct = Impossible<Value, Error>;
|
||||
type SerializeTupleVariant = Impossible<Value, Error>;
|
||||
type SerializeMap = Impossible<Value, Error>;
|
||||
type SerializeStruct = Impossible<Value, Error>;
|
||||
type SerializeStructVariant = Impossible<Value, Error>;
|
||||
|
||||
fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
|
||||
let n = try!(value.to_owned().parse());
|
||||
Ok(Value::Number(n))
|
||||
}
|
||||
|
||||
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_unit_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
) -> Result<Self::Ok, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct<T: ?Sized>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant<T: ?Sized>(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_value: &T,
|
||||
) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleStruct, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeTupleVariant, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_struct(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStruct, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
self,
|
||||
_name: &'static str,
|
||||
_variant_index: u32,
|
||||
_variant: &'static str,
|
||||
_len: usize,
|
||||
) -> Result<Self::SerializeStructVariant, Self::Error> {
|
||||
Err(invalid_number())
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче