From 0a540a4f35efd3bdd2e6069de510637dec67dd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20SDPC?= Date: Mon, 22 May 2023 23:29:20 +0200 Subject: [PATCH 1/3] feat(rad): decode JSON more efficiently --- rad/src/operators/string.rs | 121 +++++++---------------- rad/src/types/mod.rs | 190 ++++++++++++++++++++++++++++++++++-- 2 files changed, 218 insertions(+), 93 deletions(-) diff --git a/rad/src/operators/string.rs b/rad/src/operators/string.rs index b9cc6fb33..85b711f1d 100644 --- a/rad/src/operators/string.rs +++ b/rad/src/operators/string.rs @@ -19,16 +19,13 @@ const MAX_DEPTH: u8 = 20; const DEFAULT_THOUSANDS_SEPARATOR: &str = ","; const DEFAULT_DECIMAL_SEPARATOR: &str = "."; +/// Parse `RadonTypes` from a JSON-encoded `RadonString`. pub fn parse_json(input: &RadonString) -> Result { - match json::parse(&input.value()) { - Ok(json_value) => { - let value = json_to_cbor(&json_value); - RadonTypes::try_from(value) - } - Err(json_error) => Err(RadError::JsonParse { - description: json_error.to_string(), - }), - } + let json_value = json::parse(&input.value()).map_err(|err| RadError::JsonParse { + description: err.to_string(), + })?; + + RadonTypes::try_from(json_value) } pub fn parse_json_map(input: &RadonString) -> Result { @@ -282,39 +279,6 @@ pub fn string_match(input: &RadonString, args: &[Value]) -> Result Value { - match value { - json::JsonValue::Array(value) => Value::Array(value.iter().map(json_to_cbor).collect()), - json::JsonValue::Object(value) => { - let entries = value - .iter() - .map(|(key, value)| (Value::Text(String::from(key)), json_to_cbor(value))) - .collect(); - Value::Map(entries) - } - json::JsonValue::Short(value) => Value::Text(String::from(value.as_str())), - json::JsonValue::Number(value) => { - let (_, _, exponent) = value.as_parts(); - let floating = f64::from(*value); - // Cast the float into an integer if it has no fractional part and its value will fit - // into the range of `i128` (38 is the biggest power of 10 that `i128` can safely hold) - if floating.fract() == 0.0 && exponent.unsigned_abs() < 38 { - // This cast is assumed to be safe as per the previous guard - Value::Integer(floating as i128) - } else { - Value::Float(floating) - } - } - json::JsonValue::String(value) => Value::Text(String::from(value.as_str())), - json::JsonValue::Boolean(b) => Value::Bool(*b), - json::JsonValue::Null => Value::Null, - } -} - /// Replace thousands and decimals separators in a `String`. #[inline] pub fn replace_separators( @@ -1168,48 +1132,48 @@ mod tests { } #[test] - fn test_json_numbers_to_cbor_numbers() { + fn test_json_numbers_to_radon_numbers() { use json::{number::Number, JsonValue}; let json = JsonValue::Number(Number::from(2.0)); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Integer(2); - assert_eq!(resulting_cbor, expected_cbor); + let resulting_radon = RadonTypes::try_from(json).unwrap(); + let expected_radon = RadonInteger::from(2).into(); + assert_eq!(resulting_radon, expected_radon); let json = JsonValue::Number(Number::from(20.0)); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Integer(20); - assert_eq!(resulting_cbor, expected_cbor); + let resulting_radon = RadonTypes::try_from(json).unwrap(); + let expected_radon = RadonInteger::from(20).into(); + assert_eq!(resulting_radon, expected_radon); let json = JsonValue::Number(Number::from(2_000.0)); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Integer(2_000); - assert_eq!(resulting_cbor, expected_cbor); + let resulting_radon = RadonTypes::try_from(json).unwrap(); + let expected_radon = RadonInteger::from(2_000).into(); + assert_eq!(resulting_radon, expected_radon); let json = JsonValue::Number(Number::from(2_000_000.0)); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Integer(2_000_000); - assert_eq!(resulting_cbor, expected_cbor); + let resulting_radon = RadonTypes::try_from(json).unwrap(); + let expected_radon = RadonInteger::from(2_000_000).into(); + assert_eq!(resulting_radon, expected_radon); let json = JsonValue::Number(Number::from(std::f64::consts::PI)); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Float(std::f64::consts::PI); - assert_eq!(resulting_cbor, expected_cbor); + let resulting_radon = RadonTypes::try_from(json).unwrap(); + let expected_radon = RadonFloat::from(std::f64::consts::PI).into(); + assert_eq!(resulting_radon, expected_radon); let json = JsonValue::Number(Number::from(1e100)); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Float(1e100); - assert_eq!(resulting_cbor, expected_cbor); + let resulting_radon = RadonTypes::try_from(json).unwrap(); + let expected_radon = RadonFloat::from(1e100).into(); + assert_eq!(resulting_radon, expected_radon); let json = JsonValue::Number(Number::from(4.0)); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Integer(4); - assert_eq!(resulting_cbor, expected_cbor); + let resulting_radon = RadonTypes::try_from(json).unwrap(); + let expected_radon = RadonInteger::from(4).into(); + assert_eq!(resulting_radon, expected_radon); let json = JsonValue::Number(Number::from(4.1)); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Float(4.1); - assert_eq!(resulting_cbor, expected_cbor); + let resulting_radon = RadonTypes::try_from(json).unwrap(); + let expected_radon = RadonFloat::from(4.1).into(); + assert_eq!(resulting_radon, expected_radon); } #[test] @@ -1227,30 +1191,15 @@ mod tests { // This number is rounded to exactly 0.0 when converted to f64 assert_eq!(json.as_f64().unwrap(), 0.0); - // Convert to CBOR - let resulting_cbor = json_to_cbor(&json); + // Convert to RadonTypes + let resulting_radon = RadonTypes::try_from(json).unwrap(); // This exponent is too small to fit in a f64, so expected_f64 is equal to 0.0 let expected_f64 = 0.1E-99999; assert_eq!(expected_f64, 0.0); // And the expected CBOR value is a float, not an integer - let expected_cbor = serde_cbor::Value::Float(expected_f64); - assert_eq!(resulting_cbor, expected_cbor); - } - - #[test] - fn test_json_numbers_to_cbor_booleans() { - use json::JsonValue; - - let json = JsonValue::Boolean(false); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Bool(false); - assert_eq!(resulting_cbor, expected_cbor); - - let json = JsonValue::Boolean(true); - let resulting_cbor = json_to_cbor(&json); - let expected_cbor = serde_cbor::Value::Bool(true); - assert_eq!(resulting_cbor, expected_cbor); + let expected_radon = RadonFloat::from(expected_f64).into(); + assert_eq!(resulting_radon, expected_radon); } #[test] diff --git a/rad/src/types/mod.rs b/rad/src/types/mod.rs index d6ffefb40..917f5e61e 100644 --- a/rad/src/types/mod.rs +++ b/rad/src/types/mod.rs @@ -4,11 +4,17 @@ use std::{ fmt, panic, }; +use json::JsonValue; + use cbor::value::Value as CborValue; use serde::{Serialize, Serializer}; use serde_cbor::{to_vec, Value}; - use witnet_crypto::hash::calculate_sha256; +use witnet_data_structures::{ + chain::{tapi::ActiveWips, Hash}, + radon_error::{try_from_cbor_value_for_serde_cbor_value, RadonError}, + radon_report::{RadonReport, ReportContext, TypeLike}, +}; use crate::{ error::RadError, @@ -18,11 +24,6 @@ use crate::{ integer::RadonInteger, map::RadonMap, string::RadonString, }, }; -use witnet_data_structures::{ - chain::{tapi::ActiveWips, Hash}, - radon_error::{try_from_cbor_value_for_serde_cbor_value, RadonError}, - radon_report::{RadonReport, ReportContext, TypeLike}, -}; pub mod array; pub mod boolean; @@ -459,6 +460,57 @@ impl TryFrom for RadonTypes { } } +/// Allow JSON decoding of any variant of `RadonTypes`. +impl TryFrom for RadonTypes { + type Error = RadError; + + #[allow(clippy::cast_possible_truncation)] + fn try_from(value: JsonValue) -> Result { + match value { + JsonValue::Null => Err(RadError::Decode { + from: "JsonValue::Null", + to: "RadonTypes", + }), + JsonValue::Short(short) => Ok(RadonString::from(short.as_str()).into()), + JsonValue::String(string) => Ok(RadonString::from(string).into()), + JsonValue::Number(number) => { + let (positive, mantissa, exponent) = number.as_parts(); + let floating = f64::from(number); + // Cast the float into an integer if it has no fractional part and its value will fit + // into the range of `i128` (38 is the biggest power of 10 that `i128` can safely hold) + if floating.fract() == 0.0 && exponent >= 0 && exponent.unsigned_abs() < 38 { + let abs = i128::from(mantissa) * 10i128.pow(exponent.unsigned_abs().into()); + Ok(RadonInteger::from(if positive { abs } else { -abs }).into()) + } else { + // All other numbers will be treated as a float + Ok(RadonFloat::from(floating).into()) + } + } + JsonValue::Boolean(boolean) => Ok(RadonBoolean::from(boolean).into()), + JsonValue::Object(object) => Ok(RadonMap::from( + object + .iter() + // Skip null values + .filter_map(|(key, value)| { + RadonTypes::try_from(value.clone()) + .map(|value| (key.into(), value)) + .ok() + }) + .collect::>(), + ) + .into()), + JsonValue::Array(array) => Ok(RadonArray::from( + array + .into_iter() + // Skip null values + .filter_map(|value| RadonTypes::try_from(value).ok()) + .collect::>(), + ) + .into()), + } + } +} + /// Decode a vector of instances of RadonTypes from any iterator that yields `(&[u8], &T)`. /// The `err_action` argument allows the caller of this function to decide whether /// it should act in a lossy way, i.e. ignoring items that cannot be decoded or replacing them with @@ -497,10 +549,11 @@ pub fn serial_iter_decode( #[cfg(test)] mod tests { + use std::collections::HashSet; + use witnet_data_structures::chain::tapi::all_wips_active; use super::*; - use std::collections::HashSet; #[test] fn serial_iter_decode_invalid_reveals() { @@ -640,4 +693,127 @@ mod tests { .collect(); assert_eq!(rad_decode_error_as_result, vec![malformed_reveal]); } + + #[test] + fn test_json_short_to_radon_types() { + let text = "Short"; + let json_value = JsonValue::Short(unsafe { json::short::Short::from_slice(text) }); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonString::from(text).into()); + } + + #[test] + fn test_json_string_to_radon_types() { + let text = "This is a proper string because it contains more than 30 characters"; + let json_value = JsonValue::String(text.into()); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonString::from(text).into()); + } + + #[test] + fn test_json_number_to_radon_types() { + let json_value = JsonValue::Number(json::number::Number::from(u8::MAX)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonInteger::from(i128::from(u8::MAX)).into()); + + let json_value = JsonValue::Number(json::number::Number::from(u16::MAX)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonInteger::from(i128::from(u16::MAX)).into()); + + let json_value = JsonValue::Number(json::number::Number::from(u32::MAX)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonInteger::from(i128::from(u32::MAX)).into()); + + // Not using u64::MAX because of precision loss + let json_value = JsonValue::Number(json::number::Number::from(u64::MAX / 100)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!( + radon_types, + RadonInteger::from(i128::from(u64::MAX / 100)).into() + ); + + // `Number::from` won't take negative numbers, so these are using a workaround + let json_value = JsonValue::Number(-json::number::Number::from(i8::MAX)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!( + radon_types, + RadonInteger::from(i128::from(i8::MIN + 1)).into() + ); + + let json_value = JsonValue::Number(-json::number::Number::from(i16::MAX)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!( + radon_types, + RadonInteger::from(i128::from(i16::MIN + 1)).into() + ); + + let json_value = JsonValue::Number(-json::number::Number::from(i32::MAX)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!( + radon_types, + RadonInteger::from(i128::from(i32::MIN + 1)).into() + ); + + let json_value = JsonValue::Number(-json::number::Number::from(i64::MAX)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!( + radon_types, + RadonInteger::from(i128::from(i64::MIN + 1)).into() + ); + + let json_value = JsonValue::Number(json::number::Number::from(std::f64::consts::PI)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonFloat::from(std::f64::consts::PI).into()); + + let json_value = JsonValue::Number(-json::number::Number::from(std::f64::consts::PI)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonFloat::from(-std::f64::consts::PI).into()); + + let json_value = JsonValue::Number(json::number::Number::from(f64::NAN)); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonFloat::from(f64::NAN).into()); + } + + #[test] + fn test_json_boolean_to_radon_types() { + let json_value = JsonValue::Boolean(true); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonBoolean::from(true).into()); + + let json_value = JsonValue::Boolean(false); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!(radon_types, RadonBoolean::from(false).into()); + } + + #[test] + fn test_json_object_to_radon_types() { + let data = Vec::from([("foo", "bar"), ("fee", "beer")]); + let json_value = JsonValue::Object(json::object::Object::from_iter(data.iter().cloned())); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!( + radon_types, + RadonMap::from(BTreeMap::from([ + ("foo".into(), RadonString::from("bar").into()), + ("fee".into(), RadonString::from("beer").into()), + ])) + .into() + ); + } + + #[test] + fn test_json_array_to_radon_types() { + let json_value = JsonValue::Array(Vec::from([ + JsonValue::Boolean(true), + JsonValue::String("awesomeness".into()), + ])); + let radon_types = RadonTypes::try_from(json_value).unwrap(); + assert_eq!( + radon_types, + RadonArray::from(Vec::from([ + RadonBoolean::from(true).into(), + RadonString::from("awesomeness").into(), + ])) + .into() + ); + } } From 21a22293fed6a4e15aa0bc476d60deed18060d1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20SDPC?= Date: Tue, 23 May 2023 16:54:36 +0200 Subject: [PATCH 2/3] feat(rad): drop deprecated `json` library and use `serde_json` instead --- Cargo.lock | 8 +-- rad/Cargo.toml | 4 +- rad/src/operators/string.rs | 40 +++++++------ rad/src/types/mod.rs | 109 ++++++++++++++---------------------- 4 files changed, 65 insertions(+), 96 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 658ca9a90..e1920396e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2117,12 +2117,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "json" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "078e285eafdfb6c4b434e0d31e8cfcb5115b651496faca5749b88fafd4f23bfd" - [[package]] name = "jsonrpc-core" version = "10.1.0" @@ -5803,7 +5797,6 @@ dependencies = [ "hex", "http", "if_rust_version", - "json", "log 0.4.19", "minidom", "num_enum", @@ -5811,6 +5804,7 @@ dependencies = [ "rand 0.7.3", "serde", "serde_cbor", + "serde_json", "url", "witnet_config", "witnet_crypto", diff --git a/rad/Cargo.toml b/rad/Cargo.toml index 2ae1fe49b..b888d9e0d 100644 --- a/rad/Cargo.toml +++ b/rad/Cargo.toml @@ -15,14 +15,14 @@ if_rust_version = "1.0.0" # the http crate is used to perform additional validations before passing arguments to the surf http client # the version of http must be kept in sync with the version used by surf http = "0.2.1" -json = "0.12.1" log = "0.4.8" minidom = { git = "https://github.com/witnet/xmpp-rs", rev = "bc8a33ff5da95ee4039ad7ee3376c100d9e35c74" } num_enum = "0.4.2" ordered-float = "3.0" rand = "0.7.3" serde = "1.0.111" -serde_cbor = "0.11.1" +serde_cbor = "0.11.2" +serde_json = "1.0.96" # the url crate is used to perform additional validations before passing arguments to the surf http client # the version of url must be kept in sync with the version used by surf in the `witnet_net` crate url = "2.1.1" diff --git a/rad/src/operators/string.rs b/rad/src/operators/string.rs index 85b711f1d..e23914e99 100644 --- a/rad/src/operators/string.rs +++ b/rad/src/operators/string.rs @@ -5,6 +5,7 @@ use std::{ }; use serde_cbor::value::{from_value, Value}; +use serde_json::Value as JsonValue; use crate::{ error::RadError, @@ -21,9 +22,10 @@ const DEFAULT_DECIMAL_SEPARATOR: &str = "."; /// Parse `RadonTypes` from a JSON-encoded `RadonString`. pub fn parse_json(input: &RadonString) -> Result { - let json_value = json::parse(&input.value()).map_err(|err| RadError::JsonParse { - description: err.to_string(), - })?; + let json_value: JsonValue = + serde_json::from_str(&input.value()).map_err(|err| RadError::JsonParse { + description: err.to_string(), + })?; RadonTypes::try_from(json_value) } @@ -532,7 +534,7 @@ mod tests { let output = parse_json_map(&invalid_json).unwrap_err(); let expected_err = RadError::JsonParse { - description: "Unexpected character: } at (1:13)".to_string(), + description: "expected value at line 1 column 13".to_string(), }; assert_eq!(output, expected_err); @@ -580,7 +582,7 @@ mod tests { let output = parse_json_array(&invalid_json).unwrap_err(); let expected_err = RadError::JsonParse { - description: "Unexpected character: } at (1:13)".to_string(), + description: "expected value at line 1 column 13".to_string(), }; assert_eq!(output, expected_err); @@ -1133,44 +1135,44 @@ mod tests { #[test] fn test_json_numbers_to_radon_numbers() { - use json::{number::Number, JsonValue}; + use serde_json::{value::Number, Value as JsonValue}; - let json = JsonValue::Number(Number::from(2.0)); + let json = JsonValue::Number(Number::from_f64(2.0).unwrap()); let resulting_radon = RadonTypes::try_from(json).unwrap(); let expected_radon = RadonInteger::from(2).into(); assert_eq!(resulting_radon, expected_radon); - let json = JsonValue::Number(Number::from(20.0)); + let json = JsonValue::Number(Number::from_f64(20.0).unwrap()); let resulting_radon = RadonTypes::try_from(json).unwrap(); let expected_radon = RadonInteger::from(20).into(); assert_eq!(resulting_radon, expected_radon); - let json = JsonValue::Number(Number::from(2_000.0)); + let json = JsonValue::Number(Number::from_f64(2_000.0).unwrap()); let resulting_radon = RadonTypes::try_from(json).unwrap(); let expected_radon = RadonInteger::from(2_000).into(); assert_eq!(resulting_radon, expected_radon); - let json = JsonValue::Number(Number::from(2_000_000.0)); + let json = JsonValue::Number(Number::from_f64(2_000_000.0).unwrap()); let resulting_radon = RadonTypes::try_from(json).unwrap(); let expected_radon = RadonInteger::from(2_000_000).into(); assert_eq!(resulting_radon, expected_radon); - let json = JsonValue::Number(Number::from(std::f64::consts::PI)); + let json = JsonValue::Number(Number::from_f64(std::f64::consts::PI).unwrap()); let resulting_radon = RadonTypes::try_from(json).unwrap(); let expected_radon = RadonFloat::from(std::f64::consts::PI).into(); assert_eq!(resulting_radon, expected_radon); - let json = JsonValue::Number(Number::from(1e100)); + let json = JsonValue::Number(Number::from_f64(1e100).unwrap()); let resulting_radon = RadonTypes::try_from(json).unwrap(); let expected_radon = RadonFloat::from(1e100).into(); assert_eq!(resulting_radon, expected_radon); - let json = JsonValue::Number(Number::from(4.0)); + let json = JsonValue::Number(Number::from_f64(4.0).unwrap()); let resulting_radon = RadonTypes::try_from(json).unwrap(); let expected_radon = RadonInteger::from(4).into(); assert_eq!(resulting_radon, expected_radon); - let json = JsonValue::Number(Number::from(4.1)); + let json = JsonValue::Number(Number::from_f64(4.1).unwrap()); let resulting_radon = RadonTypes::try_from(json).unwrap(); let expected_radon = RadonFloat::from(4.1).into(); assert_eq!(resulting_radon, expected_radon); @@ -1183,16 +1185,12 @@ mod tests { // does not cause any overflows // Parse the number using json::parse because otherwise it is just converted to 0.0 - let json = json::parse("0.1E-99999").unwrap(); - // The exponent is too small to fit in a i16, so the json library saturates the value to - // i16::MIN: - let (sign, mantissa, exponent) = json.as_number().unwrap().as_parts(); - assert_eq!((sign, mantissa, exponent), (true, 1, i16::MIN)); + let number: serde_json::Number = serde_json::from_str("0.1E-99999").unwrap(); // This number is rounded to exactly 0.0 when converted to f64 - assert_eq!(json.as_f64().unwrap(), 0.0); + assert_eq!(number.as_f64().unwrap(), 0.0); // Convert to RadonTypes - let resulting_radon = RadonTypes::try_from(json).unwrap(); + let resulting_radon = RadonTypes::try_from(JsonValue::Number(number)).unwrap(); // This exponent is too small to fit in a f64, so expected_f64 is equal to 0.0 let expected_f64 = 0.1E-99999; diff --git a/rad/src/types/mod.rs b/rad/src/types/mod.rs index 917f5e61e..0a5f94824 100644 --- a/rad/src/types/mod.rs +++ b/rad/src/types/mod.rs @@ -4,11 +4,10 @@ use std::{ fmt, panic, }; -use json::JsonValue; - use cbor::value::Value as CborValue; use serde::{Serialize, Serializer}; use serde_cbor::{to_vec, Value}; +use serde_json::Value as JsonValue; use witnet_crypto::hash::calculate_sha256; use witnet_data_structures::{ chain::{tapi::ActiveWips, Hash}, @@ -471,22 +470,31 @@ impl TryFrom for RadonTypes { from: "JsonValue::Null", to: "RadonTypes", }), - JsonValue::Short(short) => Ok(RadonString::from(short.as_str()).into()), - JsonValue::String(string) => Ok(RadonString::from(string).into()), + JsonValue::Bool(boolean) => Ok(RadonBoolean::from(boolean).into()), JsonValue::Number(number) => { - let (positive, mantissa, exponent) = number.as_parts(); - let floating = f64::from(number); - // Cast the float into an integer if it has no fractional part and its value will fit - // into the range of `i128` (38 is the biggest power of 10 that `i128` can safely hold) - if floating.fract() == 0.0 && exponent >= 0 && exponent.unsigned_abs() < 38 { - let abs = i128::from(mantissa) * 10i128.pow(exponent.unsigned_abs().into()); - Ok(RadonInteger::from(if positive { abs } else { -abs }).into()) + if number.is_i64() { + Ok(RadonInteger::from(i128::from(number.as_i64().expect("i64"))).into()) + } else if number.is_u64() { + Ok(RadonInteger::from(i128::from(number.as_u64().expect("u64"))).into()) } else { - // All other numbers will be treated as a float - Ok(RadonFloat::from(floating).into()) + // Floats that can be safely represented as i128 are converted + let floating = number.as_f64().expect("f64"); + if floating.is_normal() && floating.fract() == 0.0 && floating.log10() < 38.0 { + Ok(RadonInteger::from(floating as i128).into()) + } else { + Ok(RadonFloat::from(floating).into()) + } } } - JsonValue::Boolean(boolean) => Ok(RadonBoolean::from(boolean).into()), + JsonValue::String(string) => Ok(RadonString::from(string).into()), + JsonValue::Array(array) => Ok(RadonArray::from( + array + .into_iter() + // Skip null values + .filter_map(|value| RadonTypes::try_from(value).ok()) + .collect::>(), + ) + .into()), JsonValue::Object(object) => Ok(RadonMap::from( object .iter() @@ -499,14 +507,6 @@ impl TryFrom for RadonTypes { .collect::>(), ) .into()), - JsonValue::Array(array) => Ok(RadonArray::from( - array - .into_iter() - // Skip null values - .filter_map(|value| RadonTypes::try_from(value).ok()) - .collect::>(), - ) - .into()), } } } @@ -694,14 +694,6 @@ mod tests { assert_eq!(rad_decode_error_as_result, vec![malformed_reveal]); } - #[test] - fn test_json_short_to_radon_types() { - let text = "Short"; - let json_value = JsonValue::Short(unsafe { json::short::Short::from_slice(text) }); - let radon_types = RadonTypes::try_from(json_value).unwrap(); - assert_eq!(radon_types, RadonString::from(text).into()); - } - #[test] fn test_json_string_to_radon_types() { let text = "This is a proper string because it contains more than 30 characters"; @@ -712,83 +704,68 @@ mod tests { #[test] fn test_json_number_to_radon_types() { - let json_value = JsonValue::Number(json::number::Number::from(u8::MAX)); + let json_value = JsonValue::Number(serde_json::Number::from(u8::MAX)); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!(radon_types, RadonInteger::from(i128::from(u8::MAX)).into()); - let json_value = JsonValue::Number(json::number::Number::from(u16::MAX)); + let json_value = JsonValue::Number(serde_json::Number::from(u16::MAX)); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!(radon_types, RadonInteger::from(i128::from(u16::MAX)).into()); - let json_value = JsonValue::Number(json::number::Number::from(u32::MAX)); + let json_value = JsonValue::Number(serde_json::Number::from(u32::MAX)); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!(radon_types, RadonInteger::from(i128::from(u32::MAX)).into()); // Not using u64::MAX because of precision loss - let json_value = JsonValue::Number(json::number::Number::from(u64::MAX / 100)); + let json_value = JsonValue::Number(serde_json::Number::from(u64::MAX / 100)); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!( radon_types, RadonInteger::from(i128::from(u64::MAX / 100)).into() ); - // `Number::from` won't take negative numbers, so these are using a workaround - let json_value = JsonValue::Number(-json::number::Number::from(i8::MAX)); + let json_value = JsonValue::Number(serde_json::Number::from(i8::MIN)); let radon_types = RadonTypes::try_from(json_value).unwrap(); - assert_eq!( - radon_types, - RadonInteger::from(i128::from(i8::MIN + 1)).into() - ); + assert_eq!(radon_types, RadonInteger::from(i128::from(i8::MIN)).into()); - let json_value = JsonValue::Number(-json::number::Number::from(i16::MAX)); + let json_value = JsonValue::Number(serde_json::Number::from(i16::MIN)); let radon_types = RadonTypes::try_from(json_value).unwrap(); - assert_eq!( - radon_types, - RadonInteger::from(i128::from(i16::MIN + 1)).into() - ); + assert_eq!(radon_types, RadonInteger::from(i128::from(i16::MIN)).into()); - let json_value = JsonValue::Number(-json::number::Number::from(i32::MAX)); + let json_value = JsonValue::Number(serde_json::Number::from(i32::MIN)); let radon_types = RadonTypes::try_from(json_value).unwrap(); - assert_eq!( - radon_types, - RadonInteger::from(i128::from(i32::MIN + 1)).into() - ); + assert_eq!(radon_types, RadonInteger::from(i128::from(i32::MIN)).into()); - let json_value = JsonValue::Number(-json::number::Number::from(i64::MAX)); + let json_value = JsonValue::Number(serde_json::Number::from(i64::MIN)); let radon_types = RadonTypes::try_from(json_value).unwrap(); - assert_eq!( - radon_types, - RadonInteger::from(i128::from(i64::MIN + 1)).into() - ); + assert_eq!(radon_types, RadonInteger::from(i128::from(i64::MIN)).into()); - let json_value = JsonValue::Number(json::number::Number::from(std::f64::consts::PI)); + let json_value = + JsonValue::Number(serde_json::Number::from_f64(std::f64::consts::PI).unwrap()); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!(radon_types, RadonFloat::from(std::f64::consts::PI).into()); - let json_value = JsonValue::Number(-json::number::Number::from(std::f64::consts::PI)); + let json_value = + JsonValue::Number(serde_json::Number::from_f64(-std::f64::consts::PI).unwrap()); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!(radon_types, RadonFloat::from(-std::f64::consts::PI).into()); - - let json_value = JsonValue::Number(json::number::Number::from(f64::NAN)); - let radon_types = RadonTypes::try_from(json_value).unwrap(); - assert_eq!(radon_types, RadonFloat::from(f64::NAN).into()); } #[test] fn test_json_boolean_to_radon_types() { - let json_value = JsonValue::Boolean(true); + let json_value = JsonValue::Bool(true); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!(radon_types, RadonBoolean::from(true).into()); - let json_value = JsonValue::Boolean(false); + let json_value = JsonValue::Bool(false); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!(radon_types, RadonBoolean::from(false).into()); } #[test] fn test_json_object_to_radon_types() { - let data = Vec::from([("foo", "bar"), ("fee", "beer")]); - let json_value = JsonValue::Object(json::object::Object::from_iter(data.iter().cloned())); + let data = Vec::from([("foo".into(), "bar".into()), ("fee".into(), "beer".into())]); + let json_value = JsonValue::Object(serde_json::Map::from_iter(data.iter().cloned())); let radon_types = RadonTypes::try_from(json_value).unwrap(); assert_eq!( radon_types, @@ -803,7 +780,7 @@ mod tests { #[test] fn test_json_array_to_radon_types() { let json_value = JsonValue::Array(Vec::from([ - JsonValue::Boolean(true), + JsonValue::Bool(true), JsonValue::String("awesomeness".into()), ])); let radon_types = RadonTypes::try_from(json_value).unwrap(); From 081dac6af472c468b4f244500844631e02c2e072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ad=C3=A1n=20SDPC?= Date: Thu, 27 Jul 2023 13:05:44 +0200 Subject: [PATCH 3/3] chore: update codebase to latest Rust version (1.71) --- data_structures/src/chain/tapi.rs | 4 ++-- node/src/actors/inventory_manager/handlers.rs | 2 +- node/src/actors/node.rs | 4 ++-- partial_struct/tests/partial_struct_derive.rs | 2 +- wallet/src/actors/app/handlers/refresh_session.rs | 2 +- wallet/src/repository/wallet/mod.rs | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/data_structures/src/chain/tapi.rs b/data_structures/src/chain/tapi.rs index de1dd515d..ec000ec1e 100644 --- a/data_structures/src/chain/tapi.rs +++ b/data_structures/src/chain/tapi.rs @@ -138,7 +138,7 @@ impl TapiEngine { } } for n in 0..self.bit_tapi_counter.len() { - if let Some(mut bit_counter) = self.bit_tapi_counter.get_mut(n, &epoch_to_update) { + if let Some(bit_counter) = self.bit_tapi_counter.get_mut(n, &epoch_to_update) { if !self.wip_activation.contains_key(&bit_counter.wip) && !avoid_wip_list.contains(&bit_counter.wip) { @@ -639,7 +639,7 @@ mod tests { assert_eq!(tapi_counter.current_length, 1); assert_eq!(tapi_counter.get(0, &100).unwrap().votes, 0); - let mut votes_counter = tapi_counter.get_mut(0, &100).unwrap(); + let votes_counter = tapi_counter.get_mut(0, &100).unwrap(); votes_counter.votes += 1; assert_eq!(tapi_counter.get(0, &100).unwrap().votes, 1); diff --git a/node/src/actors/inventory_manager/handlers.rs b/node/src/actors/inventory_manager/handlers.rs index 2bc85836e..7b6484104 100644 --- a/node/src/actors/inventory_manager/handlers.rs +++ b/node/src/actors/inventory_manager/handlers.rs @@ -512,7 +512,7 @@ mod tests { // Start relevant actors config_mngr::start(config); storage_mngr::start(); - let inventory_manager = InventoryManager::default().start(); + let inventory_manager = InventoryManager.start(); // Create first block with value transfer transactions let block = build_block_with_vt_transactions(1); diff --git a/node/src/actors/node.rs b/node/src/actors/node.rs index 9db6d4de7..088b9c0f9 100644 --- a/node/src/actors/node.rs +++ b/node/src/actors/node.rs @@ -51,7 +51,7 @@ pub fn run(config: Arc, ops: NodeOps, callback: fn()) -> Result<(), fail SystemRegistry::set(peers_manager_addr); // Start ConnectionsManager actor - let connections_manager_addr = ConnectionsManager::default().start(); + let connections_manager_addr = ConnectionsManager.start(); SystemRegistry::set(connections_manager_addr); // Start SessionManager actor @@ -69,7 +69,7 @@ pub fn run(config: Arc, ops: NodeOps, callback: fn()) -> Result<(), fail SystemRegistry::set(chain_manager_addr); // Start InventoryManager actor - let inventory_manager_addr = InventoryManager::default().start(); + let inventory_manager_addr = InventoryManager.start(); SystemRegistry::set(inventory_manager_addr); // Start RadManager actor diff --git a/partial_struct/tests/partial_struct_derive.rs b/partial_struct/tests/partial_struct_derive.rs index 7723c90ad..8f987cbd5 100644 --- a/partial_struct/tests/partial_struct_derive.rs +++ b/partial_struct/tests/partial_struct_derive.rs @@ -81,5 +81,5 @@ fn test_partial_attr_partial() { let p = PartialObj::default(); - assert_eq!(p.f, PartialAnotherObj::default()); + assert_eq!(p.f, PartialAnotherObj); } diff --git a/wallet/src/actors/app/handlers/refresh_session.rs b/wallet/src/actors/app/handlers/refresh_session.rs index 04368f6f6..df9902f9a 100644 --- a/wallet/src/actors/app/handlers/refresh_session.rs +++ b/wallet/src/actors/app/handlers/refresh_session.rs @@ -21,7 +21,7 @@ impl Handler for app::App { type Result = ::Result; fn handle(&mut self, msg: RefreshSessionRequest, ctx: &mut Self::Context) -> Self::Result { - let mut session = self + let session = self .state .sessions .get_mut(&msg.session_id) diff --git a/wallet/src/repository/wallet/mod.rs b/wallet/src/repository/wallet/mod.rs index 0ccb1aefc..e9862ef3e 100644 --- a/wallet/src/repository/wallet/mod.rs +++ b/wallet/src/repository/wallet/mod.rs @@ -1441,7 +1441,7 @@ where let mut state = self.state.write()?; // This line is needed because of this error: // - Cannot borrow `state` as mutable because it is also borrowed as immutable - let mut state = &mut *state; + let state = &mut *state; // Mark UTXOs as used so we don't double spend // Save the timestamp to after which the UTXO can be spent again