diff --git a/Cargo.toml b/Cargo.toml index bbe28b9..e48e4a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ ordered-float = { version = "5.0", default-features = false } rand = { version = "0.9.0", features = ["small_rng"] } ryu = "1.0" serde = "1.0" -serde_json = { version = "1.0", default-features = false, features = ["std"] } +serde_json = { version = "1.0", default-features = false, features = ["std", "arbitrary_precision"] } [dev-dependencies] goldenfile = "1.8" @@ -46,6 +46,7 @@ json-deserializer = "0.4.4" simd-json = "0.15.0" mockalloc = "0.1.2" criterion = "0.5.1" +proptest = "1.7" [features] default = ["databend", "serde_json/preserve_order"] diff --git a/src/constants.rs b/src/constants.rs index 7050e09..2c7fd7c 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -35,15 +35,16 @@ pub(crate) const TRUE_LEVEL: u8 = 3; pub(crate) const FALSE_LEVEL: u8 = 2; pub(crate) const EXTENSION_LEVEL: u8 = 1; -pub(crate) const TYPE_STRING: &str = "string"; -pub(crate) const TYPE_NULL: &str = "null"; -pub(crate) const TYPE_BOOLEAN: &str = "boolean"; -pub(crate) const TYPE_NUMBER: &str = "number"; -pub(crate) const TYPE_ARRAY: &str = "array"; -pub(crate) const TYPE_OBJECT: &str = "object"; -pub(crate) const TYPE_DECIMAL: &str = "decimal"; -pub(crate) const TYPE_BINARY: &str = "binary"; -pub(crate) const TYPE_DATE: &str = "date"; -pub(crate) const TYPE_TIMESTAMP: &str = "timestamp"; -pub(crate) const TYPE_TIMESTAMP_TZ: &str = "timestamp_tz"; -pub(crate) const TYPE_INTERVAL: &str = "interval"; +pub(crate) const TYPE_STRING: &str = "STRING"; +pub(crate) const TYPE_NULL: &str = "NULL_VALUE"; +pub(crate) const TYPE_BOOLEAN: &str = "BOOLEAN"; +pub(crate) const TYPE_INTEGER: &str = "INTEGER"; +pub(crate) const TYPE_ARRAY: &str = "ARRAY"; +pub(crate) const TYPE_OBJECT: &str = "OBJECT"; +pub(crate) const TYPE_DECIMAL: &str = "DECIMAL"; +pub(crate) const TYPE_DOUBLE: &str = "DOUBLE"; +pub(crate) const TYPE_BINARY: &str = "BINARY"; +pub(crate) const TYPE_DATE: &str = "DATE"; +pub(crate) const TYPE_TIMESTAMP: &str = "TIMESTAMP"; +pub(crate) const TYPE_TIMESTAMP_TZ: &str = "TIMESTAMP_TZ"; +pub(crate) const TYPE_INTERVAL: &str = "INTERVAL"; diff --git a/src/core/databend/de.rs b/src/core/databend/de.rs index a84ea1d..798b409 100644 --- a/src/core/databend/de.rs +++ b/src/core/databend/de.rs @@ -213,9 +213,10 @@ impl<'de> Deserializer<'de> { match num { Number::Int64(n) => T::from_i64(n).ok_or(Error::UnexpectedType), Number::UInt64(n) => T::from_u64(n).ok_or(Error::UnexpectedType), - Number::Float64(_) | Number::Decimal128(_) | Number::Decimal256(_) => { - Err(Error::UnexpectedType) - } + Number::Float64(_) + | Number::Decimal64(_) + | Number::Decimal128(_) + | Number::Decimal256(_) => Err(Error::UnexpectedType), } } @@ -228,6 +229,10 @@ impl<'de> Deserializer<'de> { Number::Int64(n) => T::from_i64(n).ok_or(Error::UnexpectedType), Number::UInt64(n) => T::from_u64(n).ok_or(Error::UnexpectedType), Number::Float64(n) => T::from_f64(n).ok_or(Error::UnexpectedType), + Number::Decimal64(v) => { + let n = v.to_float64(); + T::from_f64(n).ok_or(Error::UnexpectedType) + } Number::Decimal128(v) => { let n = v.to_float64(); T::from_f64(n).ok_or(Error::UnexpectedType) @@ -317,6 +322,10 @@ impl<'de> Deserializer<'de> { } } Number::Float64(i) => visitor.visit_f64(i), + Number::Decimal64(i) => { + let v = i.to_float64(); + visitor.visit_f64(v) + } Number::Decimal128(i) => { let v = i.to_float64(); visitor.visit_f64(v) diff --git a/src/core/databend/ser.rs b/src/core/databend/ser.rs index ffa9a3b..5470c55 100644 --- a/src/core/databend/ser.rs +++ b/src/core/databend/ser.rs @@ -487,20 +487,7 @@ impl Serialize for RawJsonb<'_> { NUMBER_TAG => { let num = Number::decode(&self.data[payload_start..payload_end]) .map_err(|e| ser::Error::custom(format!("{e}")))?; - - match num { - Number::Int64(i) => serializer.serialize_i64(i), - Number::UInt64(i) => serializer.serialize_u64(i), - Number::Float64(i) => serializer.serialize_f64(i), - Number::Decimal128(i) => { - let v = i.to_float64(); - serializer.serialize_f64(v) - } - Number::Decimal256(i) => { - let v = i.to_float64(); - serializer.serialize_f64(v) - } - } + num.serialize(serializer) } STRING_TAG => { let s = unsafe { @@ -539,19 +526,7 @@ impl Serialize for RawJsonb<'_> { NUMBER_TAG => { let num = Number::decode(&self.data[payload_start..payload_end]) .map_err(|e| ser::Error::custom(format!("{e}")))?; - match num { - Number::Int64(i) => serialize_seq.serialize_element(&i)?, - Number::UInt64(i) => serialize_seq.serialize_element(&i)?, - Number::Float64(i) => serialize_seq.serialize_element(&i)?, - Number::Decimal128(i) => { - let v = i.to_float64(); - serialize_seq.serialize_element(&v)? - } - Number::Decimal256(i) => { - let v = i.to_float64(); - serialize_seq.serialize_element(&v)? - } - } + serialize_seq.serialize_element(&num)?; } STRING_TAG => { let s = unsafe { @@ -624,19 +599,7 @@ impl Serialize for RawJsonb<'_> { NUMBER_TAG => { let num = Number::decode(&self.data[payload_start..payload_end]) .map_err(|e| ser::Error::custom(format!("{e}")))?; - match num { - Number::Int64(i) => serialize_map.serialize_entry(&k, &i)?, - Number::UInt64(i) => serialize_map.serialize_entry(&k, &i)?, - Number::Float64(i) => serialize_map.serialize_entry(&k, &i)?, - Number::Decimal128(i) => { - let v = i.to_float64(); - serialize_map.serialize_entry(&k, &v)? - } - Number::Decimal256(i) => { - let v = i.to_float64(); - serialize_map.serialize_entry(&k, &v)? - } - } + serialize_map.serialize_entry(&k, &num)?; } STRING_TAG => { let s = unsafe { diff --git a/src/core/databend/util.rs b/src/core/databend/util.rs index 7af3a51..bf441eb 100644 --- a/src/core/databend/util.rs +++ b/src/core/databend/util.rs @@ -32,6 +32,7 @@ use crate::extension::Timestamp; use crate::extension::TimestampTz; use crate::number::Decimal128; use crate::number::Decimal256; +use crate::number::Decimal64; use crate::Number; use crate::OwnedJsonb; use crate::RawJsonb; @@ -304,19 +305,23 @@ impl Number { writer.write_all(&v.to_be_bytes())?; Ok(9) } + Self::Decimal64(v) => { + writer.write_all(&[NUMBER_DECIMAL])?; + writer.write_all(&v.value.to_be_bytes())?; + writer.write_all(&v.scale.to_be_bytes())?; + Ok(10) + } Self::Decimal128(v) => { writer.write_all(&[NUMBER_DECIMAL])?; writer.write_all(&v.value.to_be_bytes())?; - writer.write_all(&v.precision.to_be_bytes())?; writer.write_all(&v.scale.to_be_bytes())?; - Ok(19) + Ok(18) } Self::Decimal256(v) => { writer.write_all(&[NUMBER_DECIMAL])?; writer.write_all(&v.value.to_be_bytes())?; - writer.write_all(&v.precision.to_be_bytes())?; writer.write_all(&v.scale.to_be_bytes())?; - Ok(35) + Ok(34) } } } @@ -353,26 +358,36 @@ impl Number { }, NUMBER_FLOAT => Number::Float64(f64::from_be_bytes(bytes[1..].try_into().unwrap())), NUMBER_DECIMAL => match len { + 9 => { + let value = i64::from_be_bytes(bytes[1..9].try_into().unwrap()); + let scale = u8::from_be_bytes(bytes[9..10].try_into().unwrap()); + let dec = Decimal64 { scale, value }; + Number::Decimal64(dec) + } + 17 => { + let value = i128::from_be_bytes(bytes[1..17].try_into().unwrap()); + let scale = u8::from_be_bytes(bytes[17..18].try_into().unwrap()); + let dec = Decimal128 { scale, value }; + Number::Decimal128(dec) + } 18 => { + // Compatible with deprecated Decimal128 formats, including precision let value = i128::from_be_bytes(bytes[1..17].try_into().unwrap()); - let precision = u8::from_be_bytes(bytes[17..18].try_into().unwrap()); let scale = u8::from_be_bytes(bytes[18..19].try_into().unwrap()); - let dec = Decimal128 { - precision, - scale, - value, - }; + let dec = Decimal128 { scale, value }; Number::Decimal128(dec) } + 33 => { + let value = i256::from_be_bytes(bytes[1..33].try_into().unwrap()); + let scale = u8::from_be_bytes(bytes[33..34].try_into().unwrap()); + let dec = Decimal256 { scale, value }; + Number::Decimal256(dec) + } 34 => { + // Compatible with deprecated Decimal256 formats, including precision let value = i256::from_be_bytes(bytes[1..33].try_into().unwrap()); - let precision = u8::from_be_bytes(bytes[33..34].try_into().unwrap()); let scale = u8::from_be_bytes(bytes[34..35].try_into().unwrap()); - let dec = Decimal256 { - precision, - scale, - value, - }; + let dec = Decimal256 { scale, value }; Number::Decimal256(dec) } _ => { diff --git a/src/extension.rs b/src/extension.rs index 101aa98..f1af94f 100644 --- a/src/extension.rs +++ b/src/extension.rs @@ -29,35 +29,76 @@ const MONTHS_PER_YEAR: i32 = 12; const TIMESTAMP_FORMAT: &str = "%Y-%m-%d %H:%M:%S%.6f"; +/// Represents extended JSON value types that are not supported in standard JSON. +/// +/// Standard JSON only supports strings, numbers, booleans, null, arrays, and objects. +/// This enum provides additional data types commonly needed in database systems and +/// other applications that require more specialized data representations. #[derive(Debug, Clone)] pub enum ExtensionValue<'a> { + /// Binary data (byte array), allowing efficient storage of binary content + /// that would otherwise require base64 encoding in standard JSON Binary(&'a [u8]), + /// Calendar date without time component (year, month, day) Date(Date), + /// Timestamp with microsecond precision but without timezone information Timestamp(Timestamp), + /// Timestamp with microsecond precision and timezone offset information TimestampTz(TimestampTz), + /// Time interval representation for duration calculations Interval(Interval), } +/// Represents a calendar date (year, month, day) without time component. +/// +/// The value is stored as days since the Unix epoch (January 1, 1970). +/// This allows for efficient date arithmetic and comparison operations. +/// Standard JSON has no native date type and typically uses ISO 8601 strings. #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct Date { + /// Days since Unix epoch (January 1, 1970) + /// Positive values represent dates after the epoch, negative values represent dates before pub value: i32, } +/// Represents a timestamp (date and time) without timezone information. +/// +/// The value is stored as microseconds since the Unix epoch (January 1, 1970 00:00:00 UTC). +/// This provides microsecond precision for timestamp operations. +/// Standard JSON has no native timestamp type and typically uses ISO 8601 strings. #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct Timestamp { + /// Microseconds since Unix epoch (January 1, 1970 00:00:00 UTC) pub value: i64, } +/// Represents a timestamp with timezone information. +/// +/// Combines a timestamp value with a timezone offset, allowing for +/// timezone-aware datetime operations. The timestamp is stored in UTC, +/// and the offset indicates the local timezone. +/// Standard JSON has no native timezone-aware timestamp type. #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct TimestampTz { + /// Timezone offset in hours from UTC pub offset: i8, + /// Microseconds since Unix epoch (January 1, 1970 00:00:00 UTC) pub value: i64, } +/// Represents a time interval or duration. +/// +/// This structure can represent complex time intervals with separate +/// components for months, days, and microseconds, allowing for precise +/// duration calculations that account for calendar irregularities. +/// Standard JSON has no native interval/duration type. #[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)] pub struct Interval { + /// Number of months in the interval pub months: i32, + /// Number of days in the interval pub days: i32, + /// Number of microseconds in the interval pub micros: i64, } diff --git a/src/from.rs b/src/from.rs index 232f450..e34cc3a 100644 --- a/src/from.rs +++ b/src/from.rs @@ -197,6 +197,9 @@ impl<'a> From> for JsonValue { Number::Int64(v) => JsonValue::Number(v.into()), Number::UInt64(v) => JsonValue::Number(v.into()), Number::Float64(v) => JsonValue::Number(JsonNumber::from_f64(v).unwrap()), + Number::Decimal64(v) => { + JsonValue::Number(JsonNumber::from_f64(v.to_float64()).unwrap()) + } Number::Decimal128(v) => { JsonValue::Number(JsonNumber::from_f64(v.to_float64()).unwrap()) } diff --git a/src/functions/operator.rs b/src/functions/operator.rs index ae242e5..d5cf68b 100644 --- a/src/functions/operator.rs +++ b/src/functions/operator.rs @@ -66,27 +66,27 @@ impl RawJsonb<'_> { /// // Type checking /// let arr_jsonb = "[1, 2, 3]".parse::().unwrap(); /// let raw_jsonb = arr_jsonb.as_raw(); - /// assert_eq!(raw_jsonb.type_of().unwrap(), "array"); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "ARRAY"); /// /// let obj_jsonb = r#"{"a": 1}"#.parse::().unwrap(); /// let raw_jsonb = obj_jsonb.as_raw(); - /// assert_eq!(raw_jsonb.type_of().unwrap(), "object"); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "OBJECT"); /// /// let num_jsonb = "1".parse::().unwrap(); /// let raw_jsonb = num_jsonb.as_raw(); - /// assert_eq!(raw_jsonb.type_of().unwrap(), "number"); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "INTEGER"); /// /// let string_jsonb = r#""hello""#.parse::().unwrap(); /// let raw_jsonb = string_jsonb.as_raw(); - /// assert_eq!(raw_jsonb.type_of().unwrap(), "string"); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "STRING"); /// /// let bool_jsonb = "true".parse::().unwrap(); /// let raw_jsonb = bool_jsonb.as_raw(); - /// assert_eq!(raw_jsonb.type_of().unwrap(), "boolean"); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "BOOLEAN"); /// /// let null_jsonb = "null".parse::().unwrap(); /// let raw_jsonb = null_jsonb.as_raw(); - /// assert_eq!(raw_jsonb.type_of().unwrap(), "null"); + /// assert_eq!(raw_jsonb.type_of().unwrap(), "NULL_VALUE"); /// ``` pub fn type_of(&self) -> Result<&'static str> { let jsonb_item_type = self.jsonb_item_type()?; @@ -99,9 +99,11 @@ impl RawJsonb<'_> { JsonbItem::Number(data) => { let val = Number::decode(data)?; match val { - Number::Decimal128(_v) => Ok(TYPE_DECIMAL), - Number::Decimal256(_v) => Ok(TYPE_DECIMAL), - _ => Ok(TYPE_NUMBER), + Number::UInt64(_) | Number::Int64(_) => Ok(TYPE_INTEGER), + Number::Decimal64(_) + | Number::Decimal128(_) + | Number::Decimal256(_) => Ok(TYPE_DECIMAL), + Number::Float64(_) => Ok(TYPE_DOUBLE), } } _ => Err(Error::InvalidJsonb), diff --git a/src/lib.rs b/src/lib.rs index b7d4a0e..cdc670c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,7 @@ pub use extension::*; pub use from::*; pub use number::Decimal128; pub use number::Decimal256; +pub use number::Decimal64; pub use number::Number; pub use owned::to_owned_jsonb; pub use owned::OwnedJsonb; diff --git a/src/number.rs b/src/number.rs index 8fd6295..c684cbf 100644 --- a/src/number.rs +++ b/src/number.rs @@ -28,46 +28,115 @@ use serde::de::Deserialize; use serde::de::Deserializer; use serde::de::Visitor; use serde::ser::Serialize; +use serde::ser::SerializeStruct; use serde::ser::Serializer; +const NUMBER_TOKEN: &str = "$serde_json::private::Number"; + +/// Represents a decimal number with 64-bit precision. +/// +/// This structure stores a decimal value as an integer with a scale factor, +/// allowing for precise representation of decimal numbers without floating-point errors. +/// The scale indicates how many decimal places the value has (e.g., scale=2 means 2 decimal places). +#[derive(Debug, Clone)] +pub struct Decimal64 { + /// Number of decimal places (e.g., 2 for values like 123.45) + pub scale: u8, + /// The actual value, scaled by 10^scale (e.g., 12345 for 123.45 with scale=2) + pub value: i64, +} + +impl Decimal64 { + /// Converts the decimal value to a floating-point representation. + /// + /// This is useful when you need to perform floating-point operations, + /// but note that it may introduce precision loss inherent to floating-point arithmetic. + pub fn to_float64(&self) -> f64 { + let div = 10_f64.powi(self.scale as i32); + self.value as f64 / div + } +} + +/// Represents a decimal number with 128-bit precision. +/// +/// Similar to Decimal64 but with a larger range, this structure can represent +/// very large decimal numbers with high precision, suitable for financial calculations +/// and other domains requiring exact decimal arithmetic beyond 64-bit limits. #[derive(Debug, Clone)] pub struct Decimal128 { - pub precision: u8, + /// Number of decimal places pub scale: u8, + /// The actual value, scaled by 10^scale pub value: i128, } impl Decimal128 { + /// Converts the decimal value to a floating-point representation. + /// + /// Note that for very large values, this conversion may lose precision + /// as f64 has limited precision compared to i128. pub fn to_float64(&self) -> f64 { let div = 10_f64.powi(self.scale as i32); self.value as f64 / div } } +/// Represents a decimal number with 256-bit precision. +/// +/// This structure provides the highest level of precision for decimal numbers, +/// capable of representing extremely large values with exact decimal precision. +/// Useful for cryptographic applications, high-precision scientific calculations, +/// or any domain requiring arithmetic beyond 128-bit precision. #[derive(Debug, Clone)] pub struct Decimal256 { - pub precision: u8, + /// Number of decimal places pub scale: u8, + /// The actual value, scaled by 10^scale pub value: i256, } impl Decimal256 { + /// Converts the decimal value to a floating-point representation. + /// + /// For extremely large values, significant precision loss may occur + /// as f64 has much lower precision than i256. pub fn to_float64(&self) -> f64 { let div = 10_f64.powi(self.scale as i32); self.value.as_f64() / div } } +/// Represents a JSON number with multiple possible internal representations. +/// +/// This enum provides a unified type for JSON numbers while supporting various +/// internal representations to optimize for different use cases: +/// - Integer types (signed/unsigned) for whole numbers +/// - Floating-point for scientific notation or fractional values +/// - Decimal types for exact decimal representation with different precision levels +/// +/// The parser automatically selects the most appropriate representation based on +/// the input value's characteristics, allowing for both performance and precision. #[derive(Debug, Clone)] pub enum Number { + /// 64-bit signed integer, suitable for most whole numbers Int64(i64), + /// 64-bit unsigned integer, for large positive whole numbers UInt64(u64), + /// 64-bit floating-point, for scientific notation and approximate decimals Float64(f64), + /// 64-bit decimal with exact precision, for financial calculations + Decimal64(Decimal64), + /// 128-bit decimal with extended precision, for larger exact decimals Decimal128(Decimal128), + /// 256-bit decimal with maximum precision, for extremely large exact decimals Decimal256(Decimal256), } impl<'de> Deserialize<'de> for Number { + /// Deserializes a JSON number into the Number enum. + /// + /// This implementation supports deserialization from JSON integers and floats. + /// It automatically selects the most suitable internal representation based on the input value. fn deserialize(deserializer: D) -> std::result::Result where D: Deserializer<'de>, @@ -77,10 +146,12 @@ impl<'de> Deserialize<'de> for Number { impl Visitor<'_> for NumberVisitor { type Value = Number; + /// Returns a string describing the expected input format. fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("a number (int64, uint64, or float64)") } + /// Visits an i64 value and returns a Number::Int64 variant. fn visit_i64(self, v: i64) -> std::result::Result where E: de::Error, @@ -88,6 +159,7 @@ impl<'de> Deserialize<'de> for Number { Ok(Number::Int64(v)) } + /// Visits a u64 value and returns a Number::UInt64 variant. fn visit_u64(self, v: u64) -> std::result::Result where E: de::Error, @@ -95,6 +167,7 @@ impl<'de> Deserialize<'de> for Number { Ok(Number::UInt64(v)) } + /// Visits an f64 value and returns a Number::Float64 variant. fn visit_f64(self, v: f64) -> std::result::Result where E: de::Error, @@ -107,6 +180,10 @@ impl<'de> Deserialize<'de> for Number { } impl Serialize for Number { + /// Serializes the Number enum into a JSON number. + /// + /// This implementation supports serialization to JSON integers and floats. + /// It automatically selects the most suitable output format based on the internal representation. fn serialize(&self, serializer: S) -> std::result::Result where S: Serializer, @@ -115,19 +192,20 @@ impl Serialize for Number { Number::Int64(v) => serializer.serialize_i64(*v), Number::UInt64(v) => serializer.serialize_u64(*v), Number::Float64(v) => serializer.serialize_f64(*v), - Number::Decimal128(v) => { - let val = v.to_float64(); - serializer.serialize_f64(val) - } - Number::Decimal256(v) => { - let val = v.to_float64(); - serializer.serialize_f64(val) + Number::Decimal64(_) | Number::Decimal128(_) | Number::Decimal256(_) => { + let mut serialize_struct = serializer.serialize_struct(NUMBER_TOKEN, 0)?; + let val = format!("{}", self); + serialize_struct.serialize_field(NUMBER_TOKEN, val.as_str())?; + serialize_struct.end() } } } } impl Number { + /// Returns the i64 representation of the number, if possible. + /// + /// This method returns None if the number cannot be represented as an i64. pub fn as_i64(&self) -> Option { match self { Number::Int64(v) => Some(*v), @@ -138,10 +216,16 @@ impl Number { None } } - Number::Float64(_) | Number::Decimal128(_) | Number::Decimal256(_) => None, + Number::Float64(_) + | Number::Decimal64(_) + | Number::Decimal128(_) + | Number::Decimal256(_) => None, } } + /// Returns the u64 representation of the number, if possible. + /// + /// This method returns None if the number cannot be represented as a u64. pub fn as_u64(&self) -> Option { match self { Number::Int64(v) => { @@ -152,15 +236,25 @@ impl Number { } } Number::UInt64(v) => Some(*v), - Number::Float64(_) | Number::Decimal128(_) | Number::Decimal256(_) => None, + Number::Float64(_) + | Number::Decimal64(_) + | Number::Decimal128(_) + | Number::Decimal256(_) => None, } } + /// Returns the f64 representation of the number. + /// + /// This method always returns a value, but may lose precision for very large numbers. pub fn as_f64(&self) -> Option { match self { Number::Int64(v) => Some(*v as f64), Number::UInt64(v) => Some(*v as f64), Number::Float64(v) => Some(*v), + Number::Decimal64(v) => { + let val = v.to_float64(); + Some(val) + } Number::Decimal128(v) => { let val = v.to_float64(); Some(val) @@ -172,6 +266,9 @@ impl Number { } } + /// Returns the negation of the number. + /// + /// This method returns an error if the negation would overflow. pub fn neg(&self) -> Result { match self { Number::Int64(v) => v @@ -188,9 +285,15 @@ impl Number { } } Number::Float64(v) => Ok(Number::Float64(*v * -1.0)), + Number::Decimal64(v) => { + let neg_dec = Decimal64 { + scale: v.scale, + value: -v.value, + }; + Ok(Number::Decimal64(neg_dec)) + } Number::Decimal128(v) => { let neg_dec = Decimal128 { - precision: v.precision, scale: v.scale, value: -v.value, }; @@ -201,7 +304,6 @@ impl Number { return Err(Error::Message("Decimal256 overflow".to_string())); }; let neg_dec = Decimal256 { - precision: v.precision, scale: v.scale, value: neg_value, }; @@ -210,6 +312,9 @@ impl Number { } } + /// Returns the sum of the number and another number. + /// + /// This method returns an error if the sum would overflow. pub fn add(&self, other: Number) -> Result { match (self, other) { (Number::Int64(a), Number::Int64(b)) => a @@ -253,6 +358,9 @@ impl Number { } } + /// Returns the difference of the number and another number. + /// + /// This method returns an error if the difference would overflow. pub fn sub(&self, other: Number) -> Result { match (self, other) { (Number::Int64(a), Number::Int64(b)) => a @@ -280,6 +388,9 @@ impl Number { } } + /// Returns the product of the number and another number. + /// + /// This method returns an error if the product would overflow. pub fn mul(&self, other: Number) -> Result { match (self, other) { (Number::Int64(a), Number::Int64(b)) => a @@ -323,6 +434,9 @@ impl Number { } } + /// Returns the quotient of the number and another number. + /// + /// This method returns an error if the divisor is zero. pub fn div(&self, other: Number) -> Result { let a_float = self.as_f64().unwrap(); let b_float = other.as_f64().unwrap(); @@ -332,6 +446,9 @@ impl Number { Ok(Number::Float64(a_float / b_float)) } + /// Returns the remainder of the number divided by another number. + /// + /// This method returns an error if the divisor is zero. pub fn rem(&self, other: Number) -> Result { match (self, other) { (Number::Int64(a), Number::Int64(b)) => { @@ -399,6 +516,9 @@ impl Number { } impl Default for Number { + /// Returns the default value for the Number enum. + /// + /// The default value is Number::UInt64(0). #[inline] fn default() -> Self { Number::UInt64(0) @@ -406,6 +526,9 @@ impl Default for Number { } impl PartialEq for Number { + /// Returns true if the number is equal to another number. + /// + /// This method compares the numbers using their internal representations. #[inline] fn eq(&self, other: &Self) -> bool { self.cmp(other) == Ordering::Equal @@ -413,6 +536,9 @@ impl PartialEq for Number { } impl PartialEq<&Number> for Number { + /// Returns true if the number is equal to another number. + /// + /// This method compares the numbers using their internal representations. #[inline] fn eq(&self, other: &&Number) -> bool { self.eq(*other) @@ -420,6 +546,9 @@ impl PartialEq<&Number> for Number { } impl PartialEq for &Number { + /// Returns true if the number is equal to another number. + /// + /// This method compares the numbers using their internal representations. #[inline] fn eq(&self, other: &Number) -> bool { (*self).eq(other) @@ -429,6 +558,9 @@ impl PartialEq for &Number { impl Eq for Number {} impl PartialOrd for Number { + /// Returns the ordering of the number compared to another number. + /// + /// This method compares the numbers using their internal representations. #[inline] fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -436,6 +568,9 @@ impl PartialOrd for Number { } impl PartialOrd<&Number> for Number { + /// Returns the ordering of the number compared to another number. + /// + /// This method compares the numbers using their internal representations. #[inline] fn partial_cmp(&self, other: &&Number) -> Option { self.partial_cmp(*other) @@ -443,6 +578,9 @@ impl PartialOrd<&Number> for Number { } impl PartialOrd for &Number { + /// Returns the ordering of the number compared to another number. + /// + /// This method compares the numbers using their internal representations. #[inline] fn partial_cmp(&self, other: &Number) -> Option { (*self).partial_cmp(other) @@ -450,11 +588,83 @@ impl PartialOrd for &Number { } impl Ord for Number { + /// Returns the ordering of the number compared to another number. + /// + /// This method implements precise comparison between different number types: + /// - When comparing decimal types with other types, it converts the non-decimal type + /// to a decimal representation to preserve precision + /// - When comparing between decimal types of different precision, it upgrades the lower + /// precision decimal to match the higher precision one + /// - Only falls back to floating-point comparison as a last resort #[inline] fn cmp(&self, other: &Self) -> Ordering { match (self, other) { + // Same type comparisons - use native comparison (Number::Int64(l), Number::Int64(r)) => l.cmp(r), (Number::UInt64(l), Number::UInt64(r)) => l.cmp(r), + (Number::Float64(l), Number::Float64(r)) => OrderedFloat(*l).cmp(&OrderedFloat(*r)), + (Number::Decimal64(l), Number::Decimal64(r)) => { + // Compare decimal values with the same scale + if l.scale == r.scale { + l.value.cmp(&r.value) + } else { + // Adjust scales to match for proper comparison + if let Some((l_val, r_val)) = + adjust_decimal_scales(l.value as i128, l.scale, r.value as i128, r.scale) + { + l_val.cmp(&r_val) + } else { + let l = OrderedFloat(self.as_f64().unwrap()); + let r = OrderedFloat(other.as_f64().unwrap()); + l.cmp(&r) + } + } + } + (Number::Decimal128(l), Number::Decimal128(r)) => { + // Compare decimal values with the same scale + if l.scale == r.scale { + l.value.cmp(&r.value) + } else { + // Adjust scales to match for proper comparison + if let Some((l_val, r_val)) = + adjust_decimal_scales(l.value, l.scale, r.value, r.scale) + { + l_val.cmp(&r_val) + } else { + let l = OrderedFloat(self.as_f64().unwrap()); + let r = OrderedFloat(other.as_f64().unwrap()); + l.cmp(&r) + } + } + } + (Number::Decimal256(l), Number::Decimal256(r)) => { + // Compare decimal values with the same scale + if l.scale == r.scale { + l.value.cmp(&r.value) + } else { + // For i256 comparison with different scales, we need to adjust manually + let scale_diff = l.scale as i32 - r.scale as i32; + if scale_diff > 0 { + // l has more decimal places, scale up r + let scale_factor = i256::from(10).pow(scale_diff as u32); + if let Some(r_val) = r.value.checked_mul(scale_factor) { + return l.value.cmp(&r_val); + } + } else { + // r has more decimal places, scale up l + let scale_factor = i256::from(10).pow((-scale_diff) as u32); + if let Some(l_val) = l.value.checked_mul(scale_factor) { + return l_val.cmp(&r.value); + } + } + // multiply overflow, fallback to used float compare + let l = OrderedFloat(self.as_f64().unwrap()); + let r = OrderedFloat(other.as_f64().unwrap()); + l.cmp(&r) + } + } + + // Integer to integer comparisons (Number::Int64(l), Number::UInt64(r)) => { if *l < 0 { Ordering::Less @@ -469,6 +679,180 @@ impl Ord for Number { l.cmp(&(*r as u64)) } } + + // Decimal64 comparisons with other types + (Number::Decimal64(_), Number::Int64(r)) => { + // Convert Int64 to Decimal64 with scale 0 + let r_decimal = Decimal64 { + scale: 0, + value: *r, + }; + self.cmp(&Number::Decimal64(r_decimal)) + } + (Number::Int64(l), Number::Decimal64(_)) => { + // Convert Int64 to Decimal64 with scale 0 + let l_decimal = Decimal64 { + scale: 0, + value: *l, + }; + Number::Decimal64(l_decimal).cmp(other) + } + (Number::Decimal64(_), Number::UInt64(r)) => { + // Check if the value fits in i64 + if *r <= i64::MAX as u64 { + // Convert UInt64 to Decimal64 with scale 0 + let r_decimal = Decimal64 { + scale: 0, + value: *r as i64, + }; + self.cmp(&Number::Decimal64(r_decimal)) + } else { + // If it doesn't fit, Convert UInt64 to Decimal128 with scale 0 + let r_decimal = Decimal128 { + scale: 0, + value: *r as i128, + }; + self.cmp(&Number::Decimal128(r_decimal)) + } + } + (Number::UInt64(l), Number::Decimal64(_)) => { + // Check if the value fits in i64 + if *l <= i64::MAX as u64 { + // Convert UInt64 to Decimal64 with scale 0 + let l_decimal = Decimal64 { + scale: 0, + value: *l as i64, + }; + Number::Decimal64(l_decimal).cmp(other) + } else { + // If it doesn't fit, Convert UInt64 to Decimal128 with scale 0 + let l_decimal = Decimal128 { + scale: 0, + value: *l as i128, + }; + Number::Decimal128(l_decimal).cmp(other) + } + } + + // Decimal128 comparisons with other types + (Number::Decimal128(_), Number::Int64(r)) => { + // Convert Int64 to Decimal128 with scale 0 + let r_decimal = Decimal128 { + scale: 0, + value: *r as i128, + }; + self.cmp(&Number::Decimal128(r_decimal)) + } + (Number::Int64(l), Number::Decimal128(_)) => { + // Convert Int64 to Decimal128 with scale 0 + let l_decimal = Decimal128 { + scale: 0, + value: *l as i128, + }; + Number::Decimal128(l_decimal).cmp(other) + } + (Number::Decimal128(_), Number::UInt64(r)) => { + // Convert UInt64 to Decimal128 with scale 0 + let r_decimal = Decimal128 { + scale: 0, + value: *r as i128, + }; + self.cmp(&Number::Decimal128(r_decimal)) + } + (Number::UInt64(l), Number::Decimal128(_)) => { + // Convert UInt64 to Decimal128 with scale 0 + let l_decimal = Decimal128 { + scale: 0, + value: *l as i128, + }; + Number::Decimal128(l_decimal).cmp(other) + } + + // Decimal256 comparisons with other types + (Number::Decimal256(_), Number::Int64(r)) => { + // Convert Int64 to Decimal256 with scale 0 + let r_decimal = Decimal256 { + scale: 0, + value: i256::from(*r), + }; + self.cmp(&Number::Decimal256(r_decimal)) + } + (Number::Int64(l), Number::Decimal256(_)) => { + // Convert Int64 to Decimal256 with scale 0 + let l_decimal = Decimal256 { + scale: 0, + value: i256::from(*l), + }; + Number::Decimal256(l_decimal).cmp(other) + } + (Number::Decimal256(_), Number::UInt64(r)) => { + // Convert UInt64 to Decimal256 with scale 0 + let r_decimal = Decimal256 { + scale: 0, + value: i256::from(*r), + }; + self.cmp(&Number::Decimal256(r_decimal)) + } + (Number::UInt64(l), Number::Decimal256(_)) => { + // Convert UInt64 to Decimal256 with scale 0 + let l_decimal = Decimal256 { + scale: 0, + value: i256::from(*l), + }; + Number::Decimal256(l_decimal).cmp(other) + } + + // Cross-decimal comparisons - upgrade to the higher precision + (Number::Decimal64(l), Number::Decimal128(_)) => { + // Upgrade Decimal64 to Decimal128 + let l_decimal = Decimal128 { + scale: l.scale, + value: l.value as i128, + }; + Number::Decimal128(l_decimal).cmp(other) + } + (Number::Decimal128(_), Number::Decimal64(r)) => { + // Upgrade Decimal64 to Decimal128 + let r_decimal = Decimal128 { + scale: r.scale, + value: r.value as i128, + }; + self.cmp(&Number::Decimal128(r_decimal)) + } + (Number::Decimal64(l), Number::Decimal256(_)) => { + // Upgrade Decimal64 to Decimal256 + let l_decimal = Decimal256 { + scale: l.scale, + value: i256::from(l.value), + }; + Number::Decimal256(l_decimal).cmp(other) + } + (Number::Decimal256(_), Number::Decimal64(r)) => { + // Upgrade Decimal64 to Decimal256 + let r_decimal = Decimal256 { + scale: r.scale, + value: i256::from(r.value), + }; + self.cmp(&Number::Decimal256(r_decimal)) + } + (Number::Decimal128(l), Number::Decimal256(_)) => { + // Upgrade Decimal128 to Decimal256 + let l_decimal = Decimal256 { + scale: l.scale, + value: i256::from(l.value), + }; + Number::Decimal256(l_decimal).cmp(other) + } + (Number::Decimal256(_), Number::Decimal128(r)) => { + // Upgrade Decimal128 to Decimal256 + let r_decimal = Decimal256 { + scale: r.scale, + value: i256::from(r.value), + }; + self.cmp(&Number::Decimal256(r_decimal)) + } + + // Fall back to float comparison for any other combinations (_, _) => { let l = OrderedFloat(self.as_f64().unwrap()); let r = OrderedFloat(other.as_f64().unwrap()); @@ -478,7 +862,42 @@ impl Ord for Number { } } +/// Helper function to adjust decimal scales for comparison +/// +/// Given two decimal values with potentially different scales, +/// this function adjusts them to have the same scale for accurate comparison. +fn adjust_decimal_scales( + l_val: i128, + l_scale: u8, + r_val: i128, + r_scale: u8, +) -> Option<(i128, i128)> { + let scale_diff = l_scale as i32 - r_scale as i32; + + match scale_diff.cmp(&0) { + Ordering::Greater => { + // l has more decimal places, scale up r + let scale_factor = 10_i128.pow(scale_diff as u32); + let r_val = r_val.checked_mul(scale_factor)?; + Some((l_val, r_val)) + } + Ordering::Less => { + // r has more decimal places, scale up l + let scale_factor = 10_i128.pow((-scale_diff) as u32); + let l_val = l_val.checked_mul(scale_factor)?; + Some((l_val, r_val)) + } + Ordering::Equal => { + // Same scale, no adjustment needed + Some((l_val, r_val)) + } + } +} + impl Display for Number { + /// Formats the number as a string. + /// + /// This method returns a string representation of the number in its internal format. fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { match self { Number::Int64(v) => { @@ -496,6 +915,30 @@ impl Display for Number { let s = buffer.format(*v); write!(f, "{}", s) } + Number::Decimal64(v) => { + if v.scale == 0 { + write!(f, "{}", v.value) + } else { + let pow_scale = 10_i64.pow(v.scale as u32); + if v.value >= 0 { + write!( + f, + "{}.{:0>width$}", + v.value / pow_scale, + (v.value % pow_scale).abs(), + width = v.scale as usize + ) + } else { + write!( + f, + "-{}.{:0>width$}", + -v.value / pow_scale, + (v.value % pow_scale).abs(), + width = v.scale as usize + ) + } + } + } Number::Decimal128(v) => { if v.scale == 0 { write!(f, "{}", v.value) @@ -548,3 +991,297 @@ impl Display for Number { } } } + +#[cfg(test)] +mod tests { + use super::*; + use std::cmp::Ordering; + + #[test] + fn test_number_comparison() { + // Test same type comparisons + assert_eq!(Number::Int64(10).cmp(&Number::Int64(5)), Ordering::Greater); + assert_eq!(Number::Int64(5).cmp(&Number::Int64(10)), Ordering::Less); + assert_eq!(Number::Int64(5).cmp(&Number::Int64(5)), Ordering::Equal); + + assert_eq!( + Number::UInt64(10).cmp(&Number::UInt64(5)), + Ordering::Greater + ); + assert_eq!(Number::UInt64(5).cmp(&Number::UInt64(10)), Ordering::Less); + assert_eq!(Number::UInt64(5).cmp(&Number::UInt64(5)), Ordering::Equal); + + assert_eq!( + Number::Float64(10.0).cmp(&Number::Float64(5.0)), + Ordering::Greater + ); + assert_eq!( + Number::Float64(5.0).cmp(&Number::Float64(10.0)), + Ordering::Less + ); + assert_eq!( + Number::Float64(5.0).cmp(&Number::Float64(5.0)), + Ordering::Equal + ); + + // Test int64 and uint64 comparisons + assert_eq!(Number::Int64(10).cmp(&Number::UInt64(5)), Ordering::Greater); + assert_eq!(Number::Int64(5).cmp(&Number::UInt64(10)), Ordering::Less); + assert_eq!(Number::Int64(5).cmp(&Number::UInt64(5)), Ordering::Equal); + assert_eq!(Number::Int64(-5).cmp(&Number::UInt64(5)), Ordering::Less); + + assert_eq!(Number::UInt64(10).cmp(&Number::Int64(5)), Ordering::Greater); + assert_eq!(Number::UInt64(5).cmp(&Number::Int64(10)), Ordering::Less); + assert_eq!(Number::UInt64(5).cmp(&Number::Int64(5)), Ordering::Equal); + assert_eq!(Number::UInt64(5).cmp(&Number::Int64(-5)), Ordering::Greater); + + // Test decimal64 comparisons with same scale + let d1 = Decimal64 { + scale: 2, + value: 1234, + }; // 12.34 + let d2 = Decimal64 { + scale: 2, + value: 5678, + }; // 56.78 + assert_eq!( + Number::Decimal64(d1.clone()).cmp(&Number::Decimal64(d2.clone())), + Ordering::Less + ); + assert_eq!( + Number::Decimal64(d2.clone()).cmp(&Number::Decimal64(d1.clone())), + Ordering::Greater + ); + assert_eq!( + Number::Decimal64(d1.clone()).cmp(&Number::Decimal64(d1.clone())), + Ordering::Equal + ); + + // Test decimal64 comparisons with different scales + let d3 = Decimal64 { + scale: 3, + value: 12340, + }; // 12.340 + assert_eq!( + Number::Decimal64(d1.clone()).cmp(&Number::Decimal64(d3.clone())), + Ordering::Equal + ); + + let d4 = Decimal64 { + scale: 1, + value: 123, + }; // 12.3 + assert_eq!( + Number::Decimal64(d1.clone()).cmp(&Number::Decimal64(d4.clone())), + Ordering::Greater + ); + + // Test decimal128 comparisons + let d5 = Decimal128 { + scale: 2, + value: 1234, + }; // 12.34 + let d6 = Decimal128 { + scale: 2, + value: 5678, + }; // 56.78 + assert_eq!( + Number::Decimal128(d5.clone()).cmp(&Number::Decimal128(d6.clone())), + Ordering::Less + ); + + // Test decimal256 comparisons + let d7 = Decimal256 { + scale: 2, + value: i256::from(1234), + }; // 12.34 + let d8 = Decimal256 { + scale: 2, + value: i256::from(5678), + }; // 56.78 + assert_eq!( + Number::Decimal256(d7.clone()).cmp(&Number::Decimal256(d8.clone())), + Ordering::Less + ); + + // Test int64 to decimal64 comparisons + assert_eq!( + Number::Int64(12).cmp(&Number::Decimal64(Decimal64 { + scale: 0, + value: 12 + })), + Ordering::Equal + ); + assert_eq!( + Number::Int64(12).cmp(&Number::Decimal64(Decimal64 { + scale: 1, + value: 120 + })), + Ordering::Equal + ); + assert_eq!( + Number::Int64(12).cmp(&Number::Decimal64(Decimal64 { + scale: 1, + value: 121 + })), + Ordering::Less + ); + assert_eq!( + Number::Int64(12).cmp(&Number::Decimal64(Decimal64 { + scale: 1, + value: 119 + })), + Ordering::Greater + ); + + // Test uint64 to decimal64 comparisons + assert_eq!( + Number::UInt64(12).cmp(&Number::Decimal64(Decimal64 { + scale: 0, + value: 12 + })), + Ordering::Equal + ); + assert_eq!( + Number::UInt64(12).cmp(&Number::Decimal64(Decimal64 { + scale: 1, + value: 120 + })), + Ordering::Equal + ); + + // Test float64 to decimal64 comparisons + assert_eq!( + Number::Float64(12.34).cmp(&Number::Decimal64(Decimal64 { + scale: 2, + value: 1234 + })), + Ordering::Equal + ); + assert_eq!( + Number::Float64(12.34).cmp(&Number::Decimal64(Decimal64 { + scale: 2, + value: 1235 + })), + Ordering::Less + ); + + // Test cross-decimal comparisons + // Decimal64 vs Decimal128 + assert_eq!( + Number::Decimal64(Decimal64 { + scale: 2, + value: 1234 + }) + .cmp(&Number::Decimal128(Decimal128 { + scale: 2, + value: 1234 + })), + Ordering::Equal + ); + assert_eq!( + Number::Decimal64(Decimal64 { + scale: 2, + value: 1234 + }) + .cmp(&Number::Decimal128(Decimal128 { + scale: 2, + value: 5678 + })), + Ordering::Less + ); + + // Decimal64 vs Decimal256 + assert_eq!( + Number::Decimal64(Decimal64 { + scale: 2, + value: 1234 + }) + .cmp(&Number::Decimal256(Decimal256 { + scale: 2, + value: i256::from(1234) + })), + Ordering::Equal + ); + + // Decimal128 vs Decimal256 + assert_eq!( + Number::Decimal128(Decimal128 { + scale: 2, + value: 1234 + }) + .cmp(&Number::Decimal256(Decimal256 { + scale: 2, + value: i256::from(1234) + })), + Ordering::Equal + ); + + // Test with different scales across decimal types + assert_eq!( + Number::Decimal64(Decimal64 { + scale: 2, + value: 1234 + }) + .cmp(&Number::Decimal128(Decimal128 { + scale: 3, + value: 12340 + })), + Ordering::Equal + ); + + // Test edge cases + // Very large numbers + let large_int = i64::MAX; + let large_uint = u64::MAX; + let large_decimal = Decimal128 { + scale: 0, + value: i128::from(large_int), + }; + + assert_eq!( + Number::Int64(large_int).cmp(&Number::Decimal128(large_decimal.clone())), + Ordering::Equal + ); + assert_eq!( + Number::Decimal128(large_decimal.clone()).cmp(&Number::Int64(large_int)), + Ordering::Equal + ); + + assert_eq!( + Number::UInt64(large_uint).cmp(&Number::Decimal128(large_decimal.clone())), + Ordering::Greater + ); + assert_eq!( + Number::Decimal128(large_decimal).cmp(&Number::UInt64(large_uint)), + Ordering::Less + ); + + // Negative numbers + let neg_int = -100; + let neg_decimal = Decimal64 { + scale: 0, + value: -100, + }; + + assert_eq!( + Number::Int64(neg_int).cmp(&Number::Decimal64(neg_decimal.clone())), + Ordering::Equal + ); + assert_eq!( + Number::Decimal64(neg_decimal).cmp(&Number::UInt64(100)), + Ordering::Less + ); + + // Zero values + assert_eq!(Number::Int64(0).cmp(&Number::UInt64(0)), Ordering::Equal); + assert_eq!( + Number::Int64(0).cmp(&Number::Decimal64(Decimal64 { scale: 0, value: 0 })), + Ordering::Equal + ); + assert_eq!( + Number::Int64(0).cmp(&Number::Decimal64(Decimal64 { scale: 5, value: 0 })), + Ordering::Equal + ); + } +} diff --git a/src/parser.rs b/src/parser.rs index 5b7bd97..e1d033a 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -24,6 +24,26 @@ use super::value::Object; use super::value::Value; use crate::core::Decoder; +use std::str::FromStr; + +use crate::Decimal128; +use crate::Decimal256; +use crate::Decimal64; +use ethnum::i256; + +const MAX_DECIMAL64_PRECISION: usize = 18; +const MAX_DECIMAL128_PRECISION: usize = 38; +const MAX_DECIMAL256_PRECISION: usize = 76; + +const UINT64_MIN: i128 = 0i128; +const UINT64_MAX: i128 = 18_446_744_073_709_551_615i128; +const INT64_MIN: i128 = -9_223_372_036_854_775_808i128; +const INT64_MAX: i128 = 9_223_372_036_854_775_807i128; +const DECIMAL64_MIN: i128 = -999999999999999999i128; +const DECIMAL64_MAX: i128 = 999999999999999999i128; +const DECIMAL128_MIN: i128 = -99999999999999999999999999999999999999i128; +const DECIMAL128_MAX: i128 = 99999999999999999999999999999999999999i128; + /// The binary `JSONB` contains three parts, `Header`, `JEntry` and `RawData`. /// This structure can be nested. Each group of structures starts with a `Header`. /// The upper-level `Value` will store the `Header` length or offset of @@ -95,7 +115,7 @@ impl<'a> Parser<'a> { b'n' => self.parse_json_null(), b't' => self.parse_json_true(), b'f' => self.parse_json_false(), - b'0'..=b'9' | b'-' => self.parse_json_number(), + b'0'..=b'9' | b'-' | b'+' | b'.' => self.parse_json_number(), b'"' => self.parse_json_string(), b'[' => self.parse_json_array(), b'{' => self.parse_json_object(), @@ -106,6 +126,7 @@ impl<'a> Parser<'a> { } } + #[inline] fn next(&mut self) -> Result<&u8> { match self.buf.get(self.idx) { Some(c) => Ok(c), @@ -113,6 +134,7 @@ impl<'a> Parser<'a> { } } + #[inline] fn must_is(&mut self, c: u8) -> Result<()> { match self.buf.get(self.idx) { Some(v) => { @@ -127,6 +149,7 @@ impl<'a> Parser<'a> { } } + #[inline] fn check_next(&mut self, c: u8) -> bool { if self.idx < self.buf.len() { let v = self.buf.get(self.idx).unwrap(); @@ -137,6 +160,7 @@ impl<'a> Parser<'a> { false } + #[inline] fn check_next_either(&mut self, c1: u8, c2: u8) -> bool { if self.idx < self.buf.len() { let v = self.buf.get(self.idx).unwrap(); @@ -147,6 +171,7 @@ impl<'a> Parser<'a> { false } + #[inline] fn check_digit(&mut self) -> bool { if self.idx < self.buf.len() { let v = self.buf.get(self.idx).unwrap(); @@ -157,10 +182,8 @@ impl<'a> Parser<'a> { false } - fn step_digits(&mut self) -> Result { - if self.idx == self.buf.len() { - return Err(self.error(ParseErrorCode::InvalidEOF)); - } + #[inline] + fn step_digits(&mut self) -> usize { let mut len = 0; while self.idx < self.buf.len() { let c = self.buf.get(self.idx).unwrap(); @@ -170,7 +193,7 @@ impl<'a> Parser<'a> { len += 1; self.step(); } - Ok(len) + len } #[inline] @@ -191,28 +214,36 @@ impl<'a> Parser<'a> { #[inline] fn skip_unused(&mut self) { while self.idx < self.buf.len() { - let c = self.buf.get(self.idx).unwrap(); + let c = self.buf[self.idx]; + + // Fast path: handle common whitespace characters if c.is_ascii_whitespace() { - self.step(); + self.idx += 1; continue; } - // Allow parse escaped white space - if *c == b'\\' { - if self.idx + 1 < self.buf.len() - && matches!(self.buf[self.idx + 1], b'n' | b'r' | b't') - { - self.step_by(2); + + // Slow path: handle escape sequences + if c == b'\\' && self.idx + 1 < self.buf.len() { + let next_c = self.buf[self.idx + 1]; + + // Handle simple escapes \n, \r, \t + let simple_escape = matches!(next_c, b'n' | b'r' | b't'); + if simple_escape { + self.idx += 2; continue; } - if self.idx + 3 < self.buf.len() - && self.buf[self.idx + 1] == b'x' + + // Handle \x0C escape + let hex_escape = self.idx + 3 < self.buf.len() + && next_c == b'x' && self.buf[self.idx + 2] == b'0' - && self.buf[self.idx + 3] == b'C' - { - self.step_by(4); + && self.buf[self.idx + 3] == b'C'; + if hex_escape { + self.idx += 4; continue; } } + break; } } @@ -241,95 +272,236 @@ impl<'a> Parser<'a> { Ok(Value::Bool(false)) } + /// Parse a JSON number using a single-pass approach with multiple fallback strategies. + /// + /// This function implements a high-performance JSON number parsing algorithm that: + /// 1. First attempts to parse the number as an i128 (for Decimal128/Int64/UInt64) + /// 2. Falls back to i256 (for Decimal256) if precision exceeds i128 capacity + /// 3. Finally falls back to Float64 if all other methods fail + /// + /// Extended JSON number syntax support: + /// - Leading plus sign (e.g., +123) which standard JSON doesn't allow + /// - Multiple leading zeros (e.g., 000123) which standard JSON doesn't allow + /// - Decimal point without preceding digits (e.g., .123) which standard JSON requires at least one digit before decimal + /// - Decimal point without any digits (e.g., 123.) which standard JSON requires at least one digit after decimal fn parse_json_number(&mut self) -> Result> { + // Store the starting position for potential fallback parsing let start_idx = self.idx; + let mut negative = false; + let mut leading_zeros = false; - let mut has_fraction = false; - let mut has_exponent = false; - let mut negative: bool = false; - - if self.check_next(b'-') { + // Handle sign prefix (+ or -), extending JSON to support leading plus sign + let c = self.next()?; + if *c == b'-' { negative = true; self.step(); - } - if self.check_next(b'0') { + } else if *c == b'+' { + // Extended syntax: Support for leading plus sign self.step(); - if self.check_digit() { + } + + // Extended syntax: Support for multiple leading zeros (e.g., 000123) + loop { + if self.check_next(b'0') { + leading_zeros = true; self.step(); - return Err(self.error(ParseErrorCode::InvalidNumberValue)); + } else { + break; } - } else { - let len = self.step_digits()?; - if len == 0 { + } + + // Mark the position where actual digits start (after sign and leading zeros) + let num_start_idx = self.idx; + + // Initialize parsing state + let mut value = 0_i128; // Accumulates the numeric value + let mut scale = 0_u32; // Tracks decimal places + let mut fraction_offset = None; // Position of decimal point, if any + let mut has_exponent = false; // Whether the number has an exponent part + let mut precision = 0; // Count of significant digits + + // First parsing strategy: Try to parse as i128 with precision limit + while precision < MAX_DECIMAL128_PRECISION { + if self.check_digit() { + // Parse digit and accumulate value + let digit = (self.buf[self.idx] - b'0') as i128; + + // Use unchecked operations for performance (we control precision limits) + value = unsafe { value.unchecked_mul(10_i128) }; + value = unsafe { value.unchecked_add(digit) }; self.step(); - return Err(self.error(ParseErrorCode::InvalidNumberValue)); + } else if self.check_next(b'.') { + // Handle decimal point - can only appear once + if fraction_offset.is_some() { + return Err(self.error(ParseErrorCode::InvalidNumberValue)); + } + fraction_offset = Some(self.idx); + self.step(); + // Continue to next iteration without incrementing precision + continue; + } else { + // Not a digit or decimal point, exit the parsing loop + break; + } + precision += 1; + // Track scale (number of digits after decimal point) + if fraction_offset.is_some() { + scale += 1; } } - if self.check_next(b'.') { - has_fraction = true; - self.step(); - let len = self.step_digits()?; - if len == 0 { - self.step(); - return Err(self.error(ParseErrorCode::InvalidNumberValue)); + + // Handle numbers that exceed MAX_DECIMAL128_PRECISION + if precision == MAX_DECIMAL128_PRECISION { + // If we haven't seen a decimal point yet, continue parsing integer part + if fraction_offset.is_none() { + let len = self.step_digits(); + precision += len; + if self.check_next(b'.') { + fraction_offset = Some(self.idx); + self.step(); + } + } + // Parse fractional part if decimal point exists + if fraction_offset.is_some() { + let len = self.step_digits(); + precision += len; + scale += len as u32; } } + + // Handle empty precision + if !leading_zeros && precision == 0 { + return Err(self.error(ParseErrorCode::ExpectedSomeValue)); + } + // Handle exponent notation (e.g., 1e10, 1.5E-7) if self.check_next_either(b'E', b'e') { has_exponent = true; self.step(); + // Handle exponent sign if self.check_next_either(b'+', b'-') { self.step(); } - let len = self.step_digits()?; + // Parse exponent digits + let len = self.step_digits(); if len == 0 { - self.step(); return Err(self.error(ParseErrorCode::InvalidNumberValue)); } } - let s = unsafe { std::str::from_utf8_unchecked(&self.buf[start_idx..self.idx]) }; - if !has_fraction && !has_exponent { - if !negative { - if let Ok(v) = s.parse::() { - return Ok(Value::Number(Number::UInt64(v))); + // If no exponent and precision is within limits, try to return the most appropriate numeric type + if !has_exponent && precision <= MAX_DECIMAL128_PRECISION { + // Apply sign + if negative { + value = value.checked_neg().unwrap(); + } + + // Try to fit the value into the most appropriate numeric type + if scale == 0 && (UINT64_MIN..=UINT64_MAX).contains(&value) { + return Ok(Value::Number(Number::UInt64(u64::try_from(value).unwrap()))); + } else if scale == 0 && (INT64_MIN..=INT64_MAX).contains(&value) { + return Ok(Value::Number(Number::Int64(i64::try_from(value).unwrap()))); + } else if (DECIMAL64_MIN..=DECIMAL64_MAX).contains(&value) + && precision <= MAX_DECIMAL64_PRECISION + { + return Ok(Value::Number(Number::Decimal64(Decimal64 { + scale: scale as u8, + value: i64::try_from(value).unwrap(), + }))); + } else if (DECIMAL128_MIN..=DECIMAL128_MAX).contains(&value) { + return Ok(Value::Number(Number::Decimal128(Decimal128 { + scale: scale as u8, + value, + }))); + } + } + + // Second parsing strategy: Try to parse as i256 for very large numbers + if !has_exponent && precision <= MAX_DECIMAL256_PRECISION { + let end_idx = self.idx; + + // Reconstruct the string representation without the decimal point + let digit_str = if let Some(frac_idx) = fraction_offset { + let digit_len = end_idx - num_start_idx - 1; + let mut s = String::with_capacity(digit_len); + s.push_str(unsafe { + std::str::from_utf8_unchecked(&self.buf[num_start_idx..frac_idx]) + }); + s.push_str(unsafe { + std::str::from_utf8_unchecked(&self.buf[frac_idx + 1..end_idx]) + }); + s + } else { + unsafe { std::str::from_utf8_unchecked(&self.buf[num_start_idx..end_idx]) } + .to_string() + }; + + // Try to parse as i256 + if let Ok(mut value) = i256::from_str(&digit_str) { + if negative { + value = value.checked_neg().unwrap(); } - } else if let Ok(v) = s.parse::() { - return Ok(Value::Number(Number::Int64(v))); + return Ok(Value::Number(Number::Decimal256(Decimal256 { + scale: scale as u8, + value, + }))); } } + // Final fallback strategy: Parse as Float64 using fast_float2 library + // This handles cases like scientific notation and very large/small numbers + let s = unsafe { std::str::from_utf8_unchecked(&self.buf[start_idx..self.idx]) }; match fast_float2::parse(s) { Ok(v) => Ok(Value::Number(Number::Float64(v))), Err(_) => Err(self.error(ParseErrorCode::InvalidNumberValue)), } } + /// Parse a JSON string value with support for escape sequences. + /// + /// This function implements a high-performance JSON string parser that: + /// 1. Efficiently handles strings without escape sequences using direct memory access + /// 2. Falls back to a more complex parsing routine only when escape sequences are present + /// 3. Supports standard JSON escape sequences and Unicode escapes (\uXXXX and \u{XXXX}) + /// + /// The implementation uses a two-pass approach for strings with escapes: + /// - First pass: Count escapes and determine string boundaries + /// - Second pass: Process escape sequences only when necessary fn parse_json_string(&mut self) -> Result> { + // Ensure the string starts with a quote self.must_is(b'"')?; + // Mark the starting position (after the opening quote) let start_idx = self.idx; let mut escapes = 0; + + // First pass: Scan the string to find the end and count escape sequences loop { let c = self.next()?; match c { b'\\' => { + // Handle escape sequence self.step(); escapes += 1; let next_c = self.next()?; if *next_c == b'u' { + // Handle Unicode escape sequence self.step(); let next_c = self.next()?; if *next_c == b'{' { + // Extended Unicode format: \u{XXXX} self.step_by(UNICODE_LEN + 2); } else { + // Standard Unicode format: \uXXXX self.step_by(UNICODE_LEN); } } else { + // Simple escape sequence like \n, \t, etc. self.step(); } continue; } b'"' => { + // End of string found self.step(); break; } @@ -338,13 +510,18 @@ impl<'a> Parser<'a> { self.step(); } + // Get the string data (excluding quotes) let data = &self.buf[start_idx..self.idx - 1]; + + // Second pass: Process the string based on whether it contains escape sequences let val = if escapes > 0 { + // String contains escape sequences, need to process them let len = self.idx - 1 - start_idx - escapes; let mut idx = start_idx + 1; let s = parse_string(data, len, &mut idx)?; Cow::Owned(s) } else { + // Fast path: No escape sequences, can use the string as-is std::str::from_utf8(data) .map(Cow::Borrowed) .map_err(|_| self.error(ParseErrorCode::InvalidStringValue))? @@ -352,18 +529,35 @@ impl<'a> Parser<'a> { Ok(Value::String(val)) } + /// Parse a JSON array with extended syntax support. + /// + /// This function implements a JSON array parser that: + /// 1. Handles standard JSON arrays with comma-separated values + /// 2. Extends JSON syntax to support empty elements (e.g., [1,,3]) which are parsed as null values + /// 3. Efficiently processes arrays of any size with minimal allocations + /// + /// Extended JSON array syntax support: + /// - Empty elements between commas (e.g., [1,,3]) which standard JSON doesn't allow + /// - Empty elements at the end of arrays (e.g., [1,2,]) which standard JSON doesn't allow fn parse_json_array(&mut self) -> Result> { + // Ensure the array starts with an opening bracket self.must_is(b'[')?; let mut first = true; let mut values = Vec::new(); + + // Parse array elements until closing bracket is found loop { self.skip_unused(); let c = self.next()?; + + // Check for end of array if *c == b']' { self.step(); break; } + + // Handle comma separator between elements (not for the first element) if !first { if *c != b',' { return Err(self.error(ParseErrorCode::ExpectedArrayCommaOrEnd)); @@ -371,24 +565,54 @@ impl<'a> Parser<'a> { self.step(); } first = false; + + self.skip_unused(); + + // Extended syntax: Check for empty elements (consecutive commas or comma before closing bracket) + // This is where the parser extends standard JSON by allowing empty elements + if self.check_next_either(b',', b']') { + // Insert null for empty element + values.push(Value::Null); + continue; + } + + // Parse a regular array element let value = self.parse_json_value()?; values.push(value); } Ok(Value::Array(values)) } + /// Parse a JSON object with key-value pairs. + /// + /// This function implements a standard-compliant JSON object parser that: + /// 1. Handles objects with string keys and any valid JSON values + /// 2. Enforces that keys must be strings as per JSON specification + /// 3. Efficiently builds a hash map representation of the object + /// + /// The implementation follows standard JSON syntax requirements: + /// - Keys must be strings + /// - Keys and values are separated by colons + /// - Key-value pairs are separated by commas fn parse_json_object(&mut self) -> Result> { + // Ensure the object starts with an opening brace self.must_is(b'{')?; let mut first = true; let mut obj = Object::new(); + + // Parse key-value pairs until closing brace is found loop { self.skip_unused(); let c = self.next()?; + + // Check for end of object if *c == b'}' { self.step(); break; } + + // Handle comma separator between key-value pairs (not for the first pair) if !first { if *c != b',' { return Err(self.error(ParseErrorCode::ExpectedObjectCommaOrEnd)); @@ -396,21 +620,94 @@ impl<'a> Parser<'a> { self.step(); } first = false; + + // Parse the key (must be a string) let key = self.parse_json_value()?; if !key.is_string() { return Err(self.error(ParseErrorCode::KeyMustBeAString)); } + self.skip_unused(); + + // Ensure key and value are separated by a colon let c = self.next()?; if *c != b':' { return Err(self.error(ParseErrorCode::ExpectedColon)); } self.step(); + + // Parse the value let value = self.parse_json_value()?; + // Add the key-value pair to the object + // Note: This converts the key from a borrowed string to an owned string, + // which could be an optimization target for future improvements let k = key.as_str().unwrap(); obj.insert(k.to_string(), value); } Ok(Value::Object(obj)) } } + +#[cfg(test)] +mod tests { + use super::*; + use ethnum::i256; + use proptest::prelude::*; + + fn string_strategy() -> impl Strategy { + let ascii = '!'..='~'; + // CJK Unified Ideographs + let cjk = '\u{4E00}'..='\u{9FFF}'; + + let chars: Vec = ascii.chain(cjk).collect(); + prop::collection::vec(prop::sample::select(chars), 1..30) + .prop_map(|v| v.into_iter().collect()) + } + + fn json_strategy() -> impl Strategy> { + let leaf = prop_oneof![ + Just(Value::Null), + any::().prop_map(Value::Bool), + any::().prop_map(|v| Value::Number(Number::UInt64(v))), + any::().prop_map(|v| Value::Number(Number::Int64(v))), + any::().prop_filter("Exclude -0.0", |x| *x != -0.0).prop_map(|v| Value::Number(Number::Float64(v))), + (0u8..19u8, any::()).prop_map(|(scale, value)| Value::Number(Number::Decimal64(Decimal64 { scale, value }))), + (0u8..39u8, any::()).prop_map(|(scale, value)| Value::Number(Number::Decimal128(Decimal128 { scale, value }))), + (0u8..77u8, any::(), any::()).prop_filter("Exclude big i256", + |(_, hi, lo)| { + let val = i256::from_words(*hi, *lo); + val >= ethnum::int!("-9999999999999999999999999999999999999999999999999999999999999999999999999999") && + val <= ethnum::int!("9999999999999999999999999999999999999999999999999999999999999999999999999999") + }) + .prop_map(|(scale, hi, lo)| Value::Number(Number::Decimal256(Decimal256 { scale, value: i256::from_words(hi, lo) }))), + string_strategy().prop_map(|v| Value::String(Cow::Owned(v))), + ]; + + leaf.prop_recursive(8, 256, 30, |inner| { + prop_oneof![ + prop::collection::vec(inner.clone(), 0..10).prop_map(Value::Array), + prop::collection::btree_map(string_strategy(), inner, 0..20) + .prop_map(Value::Object), + ] + }) + } + + proptest! { + #[test] + fn test_json_parser(json in json_strategy()) { + let source = format!("{}", json); + println!("source={}", source); + + let res1 = serde_json::from_slice::(source.as_bytes()); + let res2 = parse_value(source.as_bytes()); + assert_eq!(res1.is_ok(), res2.is_ok()); + if res2.is_ok() { + let new_json = res2.unwrap(); + let result = format!("{}", new_json); + println!("result={}", result); + assert_eq!(source, result); + } + } + } +} diff --git a/src/value.rs b/src/value.rs index 68dea45..0825fc8 100644 --- a/src/value.rs +++ b/src/value.rs @@ -24,29 +24,54 @@ use rand::distr::SampleString; use rand::rng; use rand::Rng; -use super::extension::Date; -use super::extension::Interval; -use super::extension::Timestamp; -use super::extension::TimestampTz; -use super::number::Number; use crate::core::Encoder; +use crate::Date; +use crate::Decimal128; +use crate::Decimal256; +use crate::Decimal64; +use crate::Interval; +use crate::Number; +use crate::Timestamp; +use crate::TimestampTz; pub type Object<'a> = BTreeMap>; -// JSONB value +/// Represents a JSON or extended JSON value. +/// +/// This enum supports both standard JSON types (Null, Bool, String, Number, Array, Object) +/// and extended types for specialized data representation (Binary, Date, Timestamp, etc.). +/// The extended types provide additional functionality beyond the JSON specification, +/// making this implementation more suitable for database applications and other +/// systems requiring richer data type support. #[derive(Clone, PartialEq, Default, Eq)] pub enum Value<'a> { + /// Represents a JSON null value #[default] Null, + /// Represents a JSON boolean value (true or false) Bool(bool), + /// Represents a JSON string value String(Cow<'a, str>), + /// Represents a JSON number value with various internal representations Number(Number), + /// Extended type: Represents binary data not supported in standard JSON + /// Useful for storing raw bytes, images, or other binary content Binary(&'a [u8]), + /// Extended type: Represents a calendar date (year, month, day) + /// Stored as days since epoch for efficient comparison and manipulation Date(Date), + /// Extended type: Represents a timestamp without timezone information + /// Stored as microseconds since epoch Timestamp(Timestamp), + /// Extended type: Represents a timestamp with timezone information + /// Includes both timestamp and timezone offset TimestampTz(TimestampTz), + /// Extended type: Represents a time interval or duration + /// Useful for time difference calculations and scheduling Interval(Interval), + /// Represents a JSON array of values Array(Vec>), + /// Represents a JSON object as key-value pairs Object(Object<'a>), } @@ -390,19 +415,43 @@ impl<'a> Value<'a> { let s = Alphanumeric.sample_string(&mut rng, 5); Value::String(Cow::from(s)) } - 2 => match rng.random_range(0..=2) { - 0 => { + 2 => match rng.random_range(0..=20) { + 0..=5 => { let n: u64 = rng.random_range(0..=100000); Value::Number(Number::UInt64(n)) } - 1 => { + 6..=10 => { let n: i64 = rng.random_range(-100000..=100000); Value::Number(Number::Int64(n)) } - _ => { + 11..=15 => { let n: f64 = rng.random_range(-4000.0..1.3e5); Value::Number(Number::Float64(n)) } + 16..=17 => { + let scale: u8 = rng.random_range(0..=18); + let value: i64 = rng.random_range(-999999999999999999..=999999999999999999); + Value::Number(Number::Decimal64(Decimal64 { scale, value })) + } + 18..=19 => { + let scale: u8 = rng.random_range(0..=38); + let value: i128 = rng.random_range( + -99999999999999999999999999999999999999i128 + ..=99999999999999999999999999999999999999i128, + ); + Value::Number(Number::Decimal128(Decimal128 { scale, value })) + } + _ => { + let scale: u8 = rng.random_range(0..=76); + let lo: i128 = + rng.random_range(0i128..=99999999999999999999999999999999999999i128); + let hi: i128 = rng.random_range( + -999999999999999999999999999999999999i128 + ..=999999999999999999999999999999999999i128, + ); + let value = ethnum::i256::from_words(hi, lo); + Value::Number(Number::Decimal256(Decimal256 { scale, value })) + } }, _ => Value::Null, }; diff --git a/tests/it/decode.rs b/tests/it/decode.rs index b74b0bd..ce6b5cb 100644 --- a/tests/it/decode.rs +++ b/tests/it/decode.rs @@ -16,8 +16,8 @@ use std::borrow::Cow; use ethnum::I256; use jsonb::{ - from_slice, Date, Decimal128, Decimal256, Interval, Number, Object, Timestamp, TimestampTz, - Value, + from_slice, Date, Decimal128, Decimal256, Decimal64, Interval, Number, Object, Timestamp, + TimestampTz, Value, }; #[test] @@ -145,22 +145,44 @@ fn test_decode_float64() { } #[test] -fn test_decode_decimal() { +fn test_decode_deprected_decimal() { + // Compatible with deprecated Decimal128 and Decimal256 formats, including precision let tests = vec![ (b"\x20\0\0\0\x20\0\0\x13\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x26\x02".to_vec(), Number::Decimal128(Decimal128 { - precision: 38, scale: 2, value: 1234 })), (b"\x20\0\0\0\x20\0\0\x13\x70\0\0\0\0\0\0\0\0\0\0\x09\x18\x4E\x72\xA1\xE5\x26\x0A".to_vec(), Number::Decimal128(Decimal128 { - precision: 38, scale: 10, value: 10000000000485 })), (b"\x20\0\0\0\x20\0\0\x23\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x4C\x02".to_vec(), - Number::Decimal256(Decimal256 { precision: 76, scale: 2, value: I256::new(1234) })), + Number::Decimal256(Decimal256 { scale: 2, value: I256::new(1234) })), (b"\x20\0\0\0\x20\0\0\x23\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x09\x18\x4E\x72\xA1\xE5\x4C\x0A".to_vec(), - Number::Decimal256(Decimal256 { precision: 76, scale: 10, value: I256::new(10000000000485) })), + Number::Decimal256(Decimal256 { scale: 10, value: I256::new(10000000000485) })), + ]; + for (s, v) in tests { + let value = from_slice(s.as_slice()).unwrap(); + assert!(value.is_number()); + assert_eq!(value.as_number().unwrap(), v); + } +} + +#[test] +fn test_decode_decimal() { + let tests = vec![ + (b"\x20\0\0\0\x20\0\0\x0A\x70\0\0\0\0\0\0\x04\xD2\x02".to_vec(), Number::Decimal64(Decimal64 { + scale: 2, + value: 1234 + })), + (b"\x20\0\0\0\x20\0\0\x12\x70\0\0\0\0\0\0\0\0\0\0\x09\x18\x4E\x72\xA1\xE5\x0A".to_vec(), Number::Decimal128(Decimal128 { + scale: 10, + value: 10000000000485 + })), + (b"\x20\0\0\0\x20\0\0\x22\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x02".to_vec(), + Number::Decimal256(Decimal256 { scale: 2, value: I256::new(1234) })), + (b"\x20\0\0\0\x20\0\0\x22\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x09\x18\x4E\x72\xA1\xE5\x0A".to_vec(), + Number::Decimal256(Decimal256 { scale: 10, value: I256::new(10000000000485) })), ]; for (s, v) in tests { let value = from_slice(s.as_slice()).unwrap(); diff --git a/tests/it/encode.rs b/tests/it/encode.rs index 911e27c..8092c74 100644 --- a/tests/it/encode.rs +++ b/tests/it/encode.rs @@ -16,7 +16,8 @@ use std::borrow::Cow; use ethnum::I256; use jsonb::{ - Date, Decimal128, Decimal256, Interval, Number, Object, Timestamp, TimestampTz, Value, + Date, Decimal128, Decimal256, Decimal64, Interval, Number, Object, Timestamp, TimestampTz, + Value, }; #[test] @@ -137,31 +138,29 @@ fn test_encode_float64() { #[test] fn test_encode_decimal() { assert_eq!( - &Value::Number(Number::Decimal128(Decimal128 { - precision: 38, + &Value::Number(Number::Decimal64(Decimal64 { scale: 2, value: 1234 })) .to_vec(), - b"\x20\0\0\0\x20\0\0\x13\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x26\x02" + b"\x20\0\0\0\x20\0\0\x0A\x70\0\0\0\0\0\0\x04\xD2\x02" ); assert_eq!( &Value::Number(Number::Decimal128(Decimal128 { - precision: 38, scale: 10, value: 10000000000485 })) .to_vec(), - b"\x20\0\0\0\x20\0\0\x13\x70\0\0\0\0\0\0\0\0\0\0\x09\x18\x4E\x72\xA1\xE5\x26\x0A" + b"\x20\0\0\0\x20\0\0\x12\x70\0\0\0\0\0\0\0\0\0\0\x09\x18\x4E\x72\xA1\xE5\x0A" ); assert_eq!( - &Value::Number(Number::Decimal256(Decimal256 { precision: 76, scale: 2, value: I256::new(1234) })).to_vec(), - b"\x20\0\0\0\x20\0\0\x23\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x4C\x02" + &Value::Number(Number::Decimal256(Decimal256 { scale: 2, value: I256::new(1234) })).to_vec(), + b"\x20\0\0\0\x20\0\0\x22\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x02" ); assert_eq!( - &Value::Number(Number::Decimal256(Decimal256 { precision: 76, scale: 10, value: I256::new(10000000000485) })).to_vec(), - b"\x20\0\0\0\x20\0\0\x23\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x09\x18\x4E\x72\xA1\xE5\x4C\x0A" + &Value::Number(Number::Decimal256(Decimal256 { scale: 10, value: I256::new(10000000000485) })).to_vec(), + b"\x20\0\0\0\x20\0\0\x22\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x09\x18\x4E\x72\xA1\xE5\x0A" ); } @@ -184,9 +183,9 @@ fn test_encode_array() { Value::Timestamp(Timestamp { value: 1540230120000000 }), Value::TimestampTz(TimestampTz { offset: 8, value: 1670389100000000 }), Value::Interval(Interval { months: 2, days: 10, micros: 500000000 }), - Value::Number(Number::Decimal256(Decimal256 { precision: 76, scale: 2, value: I256::new(1234) })), + Value::Number(Number::Decimal256(Decimal256 { scale: 2, value: I256::new(1234) })), ]).to_vec(), - b"\x80\0\0\x07\x30\0\0\0\x60\0\0\x05\x60\0\0\x05\x60\0\0\x09\x60\0\0\x0A\x60\0\0\x11\x20\0\0\x23\0\x64\x65\x66\x67\x10\0\0\x4F\x9D\x20\0\x05\x78\xD4\xC5\x2C\xCA\0\x30\0\x05\xEF\x35\xC4\xF1\x33\0\x08\x40\0\0\0\x02\0\0\0\x0A\0\0\0\0\x1D\xCD\x65\0\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x4C\x02", + b"\x80\0\0\x07\x30\0\0\0\x60\0\0\x05\x60\0\0\x05\x60\0\0\x09\x60\0\0\x0A\x60\0\0\x11\x20\0\0\x22\0\x64\x65\x66\x67\x10\0\0\x4F\x9D\x20\0\x05\x78\xD4\xC5\x2C\xCA\0\x30\0\x05\xEF\x35\xC4\xF1\x33\0\x08\x40\0\0\0\x02\0\0\0\x0A\0\0\0\0\x1D\xCD\x65\0\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x02", ); } @@ -227,7 +226,6 @@ fn test_encode_object() { obj2.insert( "k7".to_string(), Value::Number(Number::Decimal256(Decimal256 { - precision: 76, scale: 2, value: I256::new(1234), })), @@ -235,7 +233,7 @@ fn test_encode_object() { assert_eq!( &Value::Object(obj2).to_vec(), - b"\x40\0\0\x07\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x60\0\0\x05\x60\0\0\x05\x60\0\0\x09\x60\0\0\x0A\x60\0\0\x11\x20\0\0\x23\x6B\x31\x6B\x32\x6B\x33\x6B\x34\x6B\x35\x6B\x36\x6B\x37\x76\x31\0\xC8\xC9\xCA\xCB\x10\0\0\x4F\x9D\x20\0\x05\x78\xD4\xC5\x2C\xCA\0\x30\0\x05\xEF\x35\xC4\xF1\x33\0\x08\x40\0\0\0\x02\0\0\0\x0A\0\0\0\0\x1D\xCD\x65\0\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x4C\x02" + b"\x40\0\0\x07\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x10\0\0\x02\x60\0\0\x05\x60\0\0\x05\x60\0\0\x09\x60\0\0\x0A\x60\0\0\x11\x20\0\0\x22\x6B\x31\x6B\x32\x6B\x33\x6B\x34\x6B\x35\x6B\x36\x6B\x37\x76\x31\0\xC8\xC9\xCA\xCB\x10\0\0\x4F\x9D\x20\0\x05\x78\xD4\xC5\x2C\xCA\0\x30\0\x05\xEF\x35\xC4\xF1\x33\0\x08\x40\0\0\0\x02\0\0\0\x0A\0\0\0\0\x1D\xCD\x65\0\x70\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x04\xD2\x02" ); } diff --git a/tests/it/functions.rs b/tests/it/functions.rs index 0ffe802..d9d0ddd 100644 --- a/tests/it/functions.rs +++ b/tests/it/functions.rs @@ -26,6 +26,7 @@ use jsonb::parse_value; use jsonb::Date; use jsonb::Decimal128; use jsonb::Decimal256; +use jsonb::Decimal64; use jsonb::Error; use jsonb::Interval; use jsonb::Number; @@ -327,7 +328,7 @@ fn test_select_by_path() { assert_eq!(owned_jsonbs.len(), expects.len()); for (owned_jsonb, expect) in owned_jsonbs.into_iter().zip(expects.iter()) { let expected_buf = parse_value(expect.as_bytes()).unwrap().to_vec(); - assert_eq!(owned_jsonb.to_vec(), expected_buf); + assert_eq!(owned_jsonb.as_raw(), RawJsonb::new(&expected_buf)); } } } @@ -842,7 +843,6 @@ fn test_to_string() { ), ( Value::Number(Number::Decimal128(Decimal128 { - precision: 38, scale: 2, value: 1234, })), @@ -850,7 +850,6 @@ fn test_to_string() { ), ( Value::Number(Number::Decimal256(Decimal256 { - precision: 76, scale: 2, value: I256::new(981724), })), @@ -873,12 +872,10 @@ fn test_to_string() { micros: 300000000, }), Value::Number(Number::Decimal128(Decimal128 { - precision: 38, scale: 2, value: 1234, })), Value::Number(Number::Decimal256(Decimal256 { - precision: 76, scale: 2, value: I256::new(981724), })), @@ -913,7 +910,6 @@ fn test_to_string() { ( "k6".to_string(), Value::Number(Number::Decimal128(Decimal128 { - precision: 38, scale: 2, value: 1234, })), @@ -921,7 +917,6 @@ fn test_to_string() { ( "k7".to_string(), Value::Number(Number::Decimal256(Decimal256 { - precision: 76, scale: 2, value: I256::new(981724), })), @@ -1063,12 +1058,13 @@ fn test_strip_nulls() { #[test] fn test_type_of() { let sources = vec![ - (r#"null"#, "null"), - (r#"1"#, "number"), - (r#"-1.2"#, "number"), - (r#""test""#, "string"), - (r#"[1,2,3,4,5]"#, "array"), - (r#"{"a":1,"b":2}"#, "object"), + (r#"null"#, "NULL_VALUE"), + (r#"1"#, "INTEGER"), + (r#"-1.2"#, "DECIMAL"), + (r#"1.912000000000000e+02"#, "DOUBLE"), + (r#""test""#, "STRING"), + (r#"[1,2,3,4,5]"#, "ARRAY"), + (r#"{"a":1,"b":2}"#, "OBJECT"), ]; for (s, expect) in sources { @@ -1080,20 +1076,20 @@ fn test_type_of() { } let extension_sources = vec![ - (Value::Binary(&[97, 98, 99]), "binary"), - (Value::Date(Date { value: 90570 }), "date"), + (Value::Binary(&[97, 98, 99]), "BINARY"), + (Value::Date(Date { value: 90570 }), "DATE"), ( Value::Timestamp(Timestamp { value: 190390000000, }), - "timestamp", + "TIMESTAMP", ), ( Value::TimestampTz(TimestampTz { offset: 8, value: 190390000000, }), - "timestamp_tz", + "TIMESTAMP_TZ", ), ( Value::Interval(Interval { @@ -1101,23 +1097,28 @@ fn test_type_of() { days: 20, micros: 300000000, }), - "interval", + "INTERVAL", + ), + ( + Value::Number(Number::Decimal64(Decimal64 { + scale: 5, + value: 111111111111, + })), + "DECIMAL", ), ( Value::Number(Number::Decimal128(Decimal128 { - precision: 38, scale: 2, - value: 1234, + value: 99999999999999999999999999999999999999, })), - "decimal", + "DECIMAL", ), ( Value::Number(Number::Decimal256(Decimal256 { - precision: 76, scale: 2, value: I256::new(981724), })), - "decimal", + "DECIMAL", ), ]; for (v, expect) in extension_sources { diff --git a/tests/it/main.rs b/tests/it/main.rs index 3a733c8..2efba5f 100644 --- a/tests/it/main.rs +++ b/tests/it/main.rs @@ -17,4 +17,5 @@ mod encode; mod functions; mod jsonpath_parser; mod keypath_parser; +mod number; mod parser; diff --git a/tests/it/number.rs b/tests/it/number.rs new file mode 100644 index 0000000..5ed958c --- /dev/null +++ b/tests/it/number.rs @@ -0,0 +1,2264 @@ +// MIT License +// +// Copyright (c) 2016 Paul Mason +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// Copyright 2023 Datafuse Labs. +// +// 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. + +use core::cmp::Ordering::*; + +use jsonb::RawJsonb; +use jsonb::{parse_value, Number, Value}; + +#[test] +fn it_cmps_decimals() { + fn cmp(a: &str, b: &str, c: core::cmp::Ordering) { + let v1 = parse_value(a.as_bytes()).unwrap(); + let v2 = parse_value(b.as_bytes()).unwrap(); + + let s1 = format!("{}", v1); + let s2 = format!("{}", v2); + + let buf1 = v1.to_vec(); + let buf2 = v2.to_vec(); + let r1 = RawJsonb::new(&buf1); + let r2 = RawJsonb::new(&buf2); + + let ss1 = r1.to_string(); + let ss2 = r2.to_string(); + + // ignore -0.0 assert + let zero_val = Value::Number(Number::UInt64(0)); + let zero_buf = zero_val.to_vec(); + let zero = RawJsonb::new(&zero_buf); + if r1.cmp(&zero) != Equal { + assert_eq!(a, s1); + assert_eq!(a, ss1); + } + if r2.cmp(&zero) != Equal { + assert_eq!(b, s2); + assert_eq!(b, ss2); + } + + assert_eq!( + c, + r1.cmp(&r2), + "{} {} {}", + a, + match c { + Less => "<", + Equal => "==", + Greater => ">", + }, + b + ); + } + + let tests = &[ + ("1", "1", Equal), + ("1", "-1", Greater), + ("1", "1.00", Equal), + ("1.2345000000000", "1.2345", Equal), + ( + "1.0000000000000000000000000001", + "1.0000000000000000000000000000", + Greater, + ), + ( + "1.0000000000000000000000000000", + "1.0000000000000000000000000001", + Less, + ), + ("-1", "100", Less), + ("-100", "1", Less), + ("0", "0.5", Less), + ("0.5", "0", Greater), + ("100", "0.0098", Greater), + ("1000000000000000", "999000000000000.0001", Greater), + ("2.0001", "2.0001", Equal), + ( + "11.815126050420168067226890757", + "0.6386554621848739495798319328", + Greater, + ), + ( + "0.6386554621848739495798319328", + "11.815126050420168067226890757", + Less, + ), + ("-0.5", "-0.01", Less), + ("-0.5", "-0.1", Less), + ("-0.01", "-0.5", Greater), + ("-0.1", "-0.5", Greater), + // 000 equality + ("0.00000000", "0.00000000", Equal), + // 000 000 same scale + ("0.00000000", "0.00000000", Equal), + ("-0.00000000", "0.00000000", Equal), + ("0.00000000", "-0.00000000", Equal), + ("-0.00000000", "-0.00000000", Equal), + // 000 000 different scale + ("0.000000000", "0.00000000000000000000000", Equal), + ("-0.000000000", "0.00000000000000000000000", Equal), + ("0.000000000", "-0.00000000000000000000000", Equal), + ("-0.000000000", "-0.00000000000000000000000", Equal), + // 000 100 same scale + ("0.00000000", "6.56792910", Less), + ("-0.00000000", "6.56792910", Less), + ("0.00000000", "-6.56792910", Greater), + ("-0.00000000", "-6.56792910", Greater), + // 000 100 different scale + ( + "0.0000000000000000000", + "0.00000000000000001916236746", + Less, + ), + ( + "-0.0000000000000000000", + "0.00000000000000001916236746", + Less, + ), + ( + "0.0000000000000000000", + "-0.00000000000000001916236746", + Greater, + ), + ( + "-0.0000000000000000000", + "-0.00000000000000001916236746", + Greater, + ), + // 000 010 same scale + ("0.00000000", "49037796231.72571136", Less), + ("-0.00000000", "49037796231.72571136", Less), + ("0.00000000", "-49037796231.72571136", Greater), + ("-0.00000000", "-49037796231.72571136", Greater), + // 000 010 different scale + ("0", "14459264155.12895488", Less), + ("-0", "14459264155.12895488", Less), + ("0", "-14459264155.12895488", Greater), + ("-0", "-14459264155.12895488", Greater), + // 000 110 same scale + ("0.00000000", "38675108055.09052783", Less), + ("-0.00000000", "38675108055.09052783", Less), + ("0.00000000", "-38675108055.09052783", Greater), + ("-0.00000000", "-38675108055.09052783", Greater), + // 000 110 different scale + ("0.00", "1495767034080324868", Less), + ("-0.00", "1495767034080324868", Less), + ("0.00", "-1495767034080324868", Greater), + ("-0.00", "-1495767034080324868", Greater), + // 000 001 same scale + ("0.00000000", "359299289270893106016.81305600", Less), + ("-0.00000000", "359299289270893106016.81305600", Less), + ("0.00000000", "-359299289270893106016.81305600", Greater), + ("-0.00000000", "-359299289270893106016.81305600", Greater), + // 000 001 different scale + ( + "0.00000000000000000000000000", + "261631091689.9518486763536384", + Less, + ), + ( + "-0.00000000000000000000000000", + "261631091689.9518486763536384", + Less, + ), + ( + "0.00000000000000000000000000", + "-261631091689.9518486763536384", + Greater, + ), + ( + "-0.00000000000000000000000000", + "-261631091689.9518486763536384", + Greater, + ), + // 000 101 same scale + ("0.00000000", "184137107696737410476.63166815", Less), + ("-0.00000000", "184137107696737410476.63166815", Less), + ("0.00000000", "-184137107696737410476.63166815", Greater), + ("-0.00000000", "-184137107696737410476.63166815", Greater), + // 000 101 different scale + ("0.000000000", "2286857871088.7514840434334478", Less), + ("-0.000000000", "2286857871088.7514840434334478", Less), + ("0.000000000", "-2286857871088.7514840434334478", Greater), + ("-0.000000000", "-2286857871088.7514840434334478", Greater), + // 000 011 same scale + ("0.00000000", "169194696640288819908.07715840", Less), + ("-0.00000000", "169194696640288819908.07715840", Less), + ("0.00000000", "-169194696640288819908.07715840", Greater), + ("-0.00000000", "-169194696640288819908.07715840", Greater), + // 000 011 different scale + ("0.00000000", "2757550691.7650076909569048576", Less), + ("-0.00000000", "2757550691.7650076909569048576", Less), + ("0.00000000", "-2757550691.7650076909569048576", Greater), + ("-0.00000000", "-2757550691.7650076909569048576", Greater), + // 000 111 same scale + ("0.00000000", "133610725292915899001.10059212", Less), + ("-0.00000000", "133610725292915899001.10059212", Less), + ("0.00000000", "-133610725292915899001.10059212", Greater), + ("-0.00000000", "-133610725292915899001.10059212", Greater), + // 000 111 different scale + ( + "0.00000000000000000000", + "86.25432767926620368822165265", + Less, + ), + ( + "-0.00000000000000000000", + "86.25432767926620368822165265", + Less, + ), + ( + "0.00000000000000000000", + "-86.25432767926620368822165265", + Greater, + ), + ( + "-0.00000000000000000000", + "-86.25432767926620368822165265", + Greater, + ), + // 100 equality + ("0.0000000000598992228", "0.0000000000598992228", Equal), + // 100 000 same scale + ("0.0000000000598992228", "0.0000000000000000000", Greater), + ("-0.0000000000598992228", "0.0000000000000000000", Less), + ("0.0000000000598992228", "-0.0000000000000000000", Greater), + ("-0.0000000000598992228", "-0.0000000000000000000", Less), + // 100 000 different scale + ("0.1797407597", "0.0000000000000000000", Greater), + ("-0.1797407597", "0.0000000000000000000", Less), + ("0.1797407597", "-0.0000000000000000000", Greater), + ("-0.1797407597", "-0.0000000000000000000", Less), + // 100 100 same scale + ("0.0000000000598992228", "0.0000000000064510789", Greater), + ("-0.0000000000598992228", "0.0000000000064510789", Less), + ("0.0000000000598992228", "-0.0000000000064510789", Greater), + ("-0.0000000000598992228", "-0.0000000000064510789", Less), + // 100 100 different scale + ("0.000000000000011217354", "0.0000000000217735186", Less), + ("-0.000000000000011217354", "0.0000000000217735186", Less), + ("0.000000000000011217354", "-0.0000000000217735186", Greater), + ( + "-0.000000000000011217354", + "-0.0000000000217735186", + Greater, + ), + // 100 010 same scale + ("0.0000000000598992228", "0.0659116848159129600", Less), + ("-0.0000000000598992228", "0.0659116848159129600", Less), + ("0.0000000000598992228", "-0.0659116848159129600", Greater), + ("-0.0000000000598992228", "-0.0659116848159129600", Greater), + // 100 010 different scale + ("0.00042035421", "0.004709460588143575040", Less), + ("-0.00042035421", "0.004709460588143575040", Less), + ("0.00042035421", "-0.004709460588143575040", Greater), + ("-0.00042035421", "-0.004709460588143575040", Greater), + // 100 110 same scale + ("0.0000000000598992228", "0.0755686585127091375", Less), + ("-0.0000000000598992228", "0.0755686585127091375", Less), + ("0.0000000000598992228", "-0.0755686585127091375", Greater), + ("-0.0000000000598992228", "-0.0755686585127091375", Greater), + // 100 110 different scale + ("14872.94465", "1284707831905.854085", Less), + ("-14872.94465", "1284707831905.854085", Less), + ("14872.94465", "-1284707831905.854085", Greater), + ("-14872.94465", "-1284707831905.854085", Greater), + // 100 001 same scale + ( + "0.0000000000598992228", + "888767595.6145376468836286464", + Less, + ), + ( + "-0.0000000000598992228", + "888767595.6145376468836286464", + Less, + ), + ( + "0.0000000000598992228", + "-888767595.6145376468836286464", + Greater, + ), + ( + "-0.0000000000598992228", + "-888767595.6145376468836286464", + Greater, + ), + // 100 001 different scale + ("0.0002108155975", "36.07555527243968476014968832", Less), + ("-0.0002108155975", "36.07555527243968476014968832", Less), + ("0.0002108155975", "-36.07555527243968476014968832", Greater), + ( + "-0.0002108155975", + "-36.07555527243968476014968832", + Greater, + ), + // 100 101 same scale + ( + "0.0000000000598992228", + "125730345.7412344676309569911", + Less, + ), + ( + "-0.0000000000598992228", + "125730345.7412344676309569911", + Less, + ), + ( + "0.0000000000598992228", + "-125730345.7412344676309569911", + Greater, + ), + ( + "-0.0000000000598992228", + "-125730345.7412344676309569911", + Greater, + ), + // 100 101 different scale + ( + "0.0000000000871576741", + "1.7283925558865766140690239662", + Less, + ), + ( + "-0.0000000000871576741", + "1.7283925558865766140690239662", + Less, + ), + ( + "0.0000000000871576741", + "-1.7283925558865766140690239662", + Greater, + ), + ( + "-0.0000000000871576741", + "-1.7283925558865766140690239662", + Greater, + ), + // 100 011 same scale + ( + "0.0000000000598992228", + "645513262.9193254090737451008", + Less, + ), + ( + "-0.0000000000598992228", + "645513262.9193254090737451008", + Less, + ), + ( + "0.0000000000598992228", + "-645513262.9193254090737451008", + Greater, + ), + ( + "-0.0000000000598992228", + "-645513262.9193254090737451008", + Greater, + ), + // 100 011 different scale + ( + "0.000000000000000760885021", + "3718370638.2004059326675681280", + Less, + ), + ( + "-0.000000000000000760885021", + "3718370638.2004059326675681280", + Less, + ), + ( + "0.000000000000000760885021", + "-3718370638.2004059326675681280", + Greater, + ), + ( + "-0.000000000000000760885021", + "-3718370638.2004059326675681280", + Greater, + ), + // 100 111 same scale + ( + "0.0000000000598992228", + "422482675.5515775479939306437", + Less, + ), + ( + "-0.0000000000598992228", + "422482675.5515775479939306437", + Less, + ), + ( + "0.0000000000598992228", + "-422482675.5515775479939306437", + Greater, + ), + ( + "-0.0000000000598992228", + "-422482675.5515775479939306437", + Greater, + ), + // 100 111 different scale + ("0.000000044182898", "25.452953262109919998605674045", Less), + ("-0.000000044182898", "25.452953262109919998605674045", Less), + ( + "0.000000044182898", + "-25.452953262109919998605674045", + Greater, + ), + ( + "-0.000000044182898", + "-25.452953262109919998605674045", + Greater, + ), + // 010 equality + ("423.1744746042687488", "423.1744746042687488", Equal), + // 010 000 same scale + ("423.1744746042687488", "0.0000000000000000", Greater), + ("-423.1744746042687488", "0.0000000000000000", Less), + ("423.1744746042687488", "-0.0000000000000000", Greater), + ("-423.1744746042687488", "-0.0000000000000000", Less), + // 010 000 different scale + ( + "9002354991.192604672", + "0.00000000000000000000000000", + Greater, + ), + ( + "-9002354991.192604672", + "0.00000000000000000000000000", + Less, + ), + ( + "9002354991.192604672", + "-0.00000000000000000000000000", + Greater, + ), + ( + "-9002354991.192604672", + "-0.00000000000000000000000000", + Less, + ), + // 010 100 same scale + ("423.1744746042687488", "0.0000000981820809", Greater), + ("-423.1744746042687488", "0.0000000981820809", Less), + ("423.1744746042687488", "-0.0000000981820809", Greater), + ("-423.1744746042687488", "-0.0000000981820809", Less), + // 010 100 different scale + ("4327019125101559.808", "0.00484846050", Greater), + ("-4327019125101559.808", "0.00484846050", Less), + ("4327019125101559.808", "-0.00484846050", Greater), + ("-4327019125101559.808", "-0.00484846050", Less), + // 010 010 same scale + ("423.1744746042687488", "786.9082854590775296", Less), + ("-423.1744746042687488", "786.9082854590775296", Less), + ("423.1744746042687488", "-786.9082854590775296", Greater), + ("-423.1744746042687488", "-786.9082854590775296", Greater), + // 010 010 different scale + ("793.0067125291450368", "0.0001587297248335626240", Greater), + ("-793.0067125291450368", "0.0001587297248335626240", Less), + ("793.0067125291450368", "-0.0001587297248335626240", Greater), + ("-793.0067125291450368", "-0.0001587297248335626240", Less), + // 010 110 same scale + ("423.1744746042687488", "300.0541360230572049", Greater), + ("-423.1744746042687488", "300.0541360230572049", Less), + ("423.1744746042687488", "-300.0541360230572049", Greater), + ("-423.1744746042687488", "-300.0541360230572049", Less), + // 010 110 different scale + ("90627.2042582540288", "4472414566654924.741", Less), + ("-90627.2042582540288", "4472414566654924.741", Less), + ("90627.2042582540288", "-4472414566654924.741", Greater), + ("-90627.2042582540288", "-4472414566654924.741", Greater), + // 010 001 same scale + ( + "423.1744746042687488", + "3960577151543.5796707636412416", + Less, + ), + ( + "-423.1744746042687488", + "3960577151543.5796707636412416", + Less, + ), + ( + "423.1744746042687488", + "-3960577151543.5796707636412416", + Greater, + ), + ( + "-423.1744746042687488", + "-3960577151543.5796707636412416", + Greater, + ), + // 010 001 different scale + ( + "0.000008867461591822499840", + "185286.04378228713249986052096", + Less, + ), + ( + "-0.000008867461591822499840", + "185286.04378228713249986052096", + Less, + ), + ( + "0.000008867461591822499840", + "-185286.04378228713249986052096", + Greater, + ), + ( + "-0.000008867461591822499840", + "-185286.04378228713249986052096", + Greater, + ), + // 010 101 same scale + ( + "423.1744746042687488", + "2825958416017.6213507229869501", + Less, + ), + ( + "-423.1744746042687488", + "2825958416017.6213507229869501", + Less, + ), + ( + "423.1744746042687488", + "-2825958416017.6213507229869501", + Greater, + ), + ( + "-423.1744746042687488", + "-2825958416017.6213507229869501", + Greater, + ), + // 010 101 different scale + ( + "0.01901870767742648320", + "37662082383021542232.529651212", + Less, + ), + ( + "-0.01901870767742648320", + "37662082383021542232.529651212", + Less, + ), + ( + "0.01901870767742648320", + "-37662082383021542232.529651212", + Greater, + ), + ( + "-0.01901870767742648320", + "-37662082383021542232.529651212", + Greater, + ), + // 010 011 same scale + ( + "423.1744746042687488", + "3628063966991.6759417059016704", + Less, + ), + ( + "-423.1744746042687488", + "3628063966991.6759417059016704", + Less, + ), + ( + "423.1744746042687488", + "-3628063966991.6759417059016704", + Greater, + ), + ( + "-423.1744746042687488", + "-3628063966991.6759417059016704", + Greater, + ), + // 010 011 different scale + ( + "45359904.32470925312", + "2.4203452488052342918570049536", + Greater, + ), + ( + "-45359904.32470925312", + "2.4203452488052342918570049536", + Less, + ), + ( + "45359904.32470925312", + "-2.4203452488052342918570049536", + Greater, + ), + ( + "-45359904.32470925312", + "-2.4203452488052342918570049536", + Less, + ), + // 010 111 same scale + ( + "423.1744746042687488", + "2629665172331.9610693820109120", + Less, + ), + ( + "-423.1744746042687488", + "2629665172331.9610693820109120", + Less, + ), + ( + "423.1744746042687488", + "-2629665172331.9610693820109120", + Greater, + ), + ( + "-423.1744746042687488", + "-2629665172331.9610693820109120", + Greater, + ), + // 010 111 different scale + ( + "0.0006420803252266205184", + "8172.032417576265900588945489", + Less, + ), + ( + "-0.0006420803252266205184", + "8172.032417576265900588945489", + Less, + ), + ( + "0.0006420803252266205184", + "-8172.032417576265900588945489", + Greater, + ), + ( + "-0.0006420803252266205184", + "-8172.032417576265900588945489", + Greater, + ), + // 110 equality + ("844.5530620517286511", "844.5530620517286511", Equal), + // 110 000 same scale + ("844.5530620517286511", "0.0000000000000000", Greater), + ("-844.5530620517286511", "0.0000000000000000", Less), + ("844.5530620517286511", "-0.0000000000000000", Greater), + ("-844.5530620517286511", "-0.0000000000000000", Less), + // 110 000 different scale + ("3285.530033386074797", "0.00000000000000000000", Greater), + ("-3285.530033386074797", "0.00000000000000000000", Less), + ("3285.530033386074797", "-0.00000000000000000000", Greater), + ("-3285.530033386074797", "-0.00000000000000000000", Less), + // 110 100 same scale + ("844.5530620517286511", "0.0000001953470063", Greater), + ("-844.5530620517286511", "0.0000001953470063", Less), + ("844.5530620517286511", "-0.0000001953470063", Greater), + ("-844.5530620517286511", "-0.0000001953470063", Less), + // 110 100 different scale + ( + "371284.0210972493371", + "0.000000000000001307794657", + Greater, + ), + ("-371284.0210972493371", "0.000000000000001307794657", Less), + ( + "371284.0210972493371", + "-0.000000000000001307794657", + Greater, + ), + ("-371284.0210972493371", "-0.000000000000001307794657", Less), + // 110 010 same scale + ("844.5530620517286511", "612.1542773033140224", Greater), + ("-844.5530620517286511", "612.1542773033140224", Less), + ("844.5530620517286511", "-612.1542773033140224", Greater), + ("-844.5530620517286511", "-612.1542773033140224", Less), + // 110 010 different scale + ("0.00004869219159821525572", "23341676485159.15776", Less), + ("-0.00004869219159821525572", "23341676485159.15776", Less), + ( + "0.00004869219159821525572", + "-23341676485159.15776", + Greater, + ), + ( + "-0.00004869219159821525572", + "-23341676485159.15776", + Greater, + ), + // 110 110 same scale + ("844.5530620517286511", "326.6132818317622015", Greater), + ("-844.5530620517286511", "326.6132818317622015", Less), + ("844.5530620517286511", "-326.6132818317622015", Greater), + ("-844.5530620517286511", "-326.6132818317622015", Less), + // 110 110 different scale + ("4752139369958.820619", "1330851022882027.972", Less), + ("-4752139369958.820619", "1330851022882027.972", Less), + ("4752139369958.820619", "-1330851022882027.972", Greater), + ("-4752139369958.820619", "-1330851022882027.972", Greater), + // 110 001 same scale + ( + "844.5530620517286511", + "3585610241942.1922435648192512", + Less, + ), + ( + "-844.5530620517286511", + "3585610241942.1922435648192512", + Less, + ), + ( + "844.5530620517286511", + "-3585610241942.1922435648192512", + Greater, + ), + ( + "-844.5530620517286511", + "-3585610241942.1922435648192512", + Greater, + ), + // 110 001 different scale + ( + "539313715923.4424678", + "12410950080603997079634706432", + Less, + ), + ( + "-539313715923.4424678", + "12410950080603997079634706432", + Less, + ), + ( + "539313715923.4424678", + "-12410950080603997079634706432", + Greater, + ), + ( + "-539313715923.4424678", + "-12410950080603997079634706432", + Greater, + ), + // 110 101 same scale + ( + "844.5530620517286511", + "1947825396031.6933708908343230", + Less, + ), + ( + "-844.5530620517286511", + "1947825396031.6933708908343230", + Less, + ), + ( + "844.5530620517286511", + "-1947825396031.6933708908343230", + Greater, + ), + ( + "-844.5530620517286511", + "-1947825396031.6933708908343230", + Greater, + ), + // 110 101 different scale + ( + "0.2301405445512525317", + "245433629587.71206426704897154", + Less, + ), + ( + "-0.2301405445512525317", + "245433629587.71206426704897154", + Less, + ), + ( + "0.2301405445512525317", + "-245433629587.71206426704897154", + Greater, + ), + ( + "-0.2301405445512525317", + "-245433629587.71206426704897154", + Greater, + ), + // 110 011 same scale + ( + "844.5530620517286511", + "3637850451015.0843464291450880", + Less, + ), + ( + "-844.5530620517286511", + "3637850451015.0843464291450880", + Less, + ), + ( + "844.5530620517286511", + "-3637850451015.0843464291450880", + Greater, + ), + ( + "-844.5530620517286511", + "-3637850451015.0843464291450880", + Greater, + ), + // 110 011 different scale + ( + "0.00000000717944802566514691", + "18143443615.480512395717115904", + Less, + ), + ( + "-0.00000000717944802566514691", + "18143443615.480512395717115904", + Less, + ), + ( + "0.00000000717944802566514691", + "-18143443615.480512395717115904", + Greater, + ), + ( + "-0.00000000717944802566514691", + "-18143443615.480512395717115904", + Greater, + ), + // 110 111 same scale + ( + "844.5530620517286511", + "2738424264600.4917875777303163", + Less, + ), + ( + "-844.5530620517286511", + "2738424264600.4917875777303163", + Less, + ), + ( + "844.5530620517286511", + "-2738424264600.4917875777303163", + Greater, + ), + ( + "-844.5530620517286511", + "-2738424264600.4917875777303163", + Greater, + ), + // 110 111 different scale + ( + "0.0000000007762706076409491335", + "2489879185787497651.6458518595", + Less, + ), + ( + "-0.0000000007762706076409491335", + "2489879185787497651.6458518595", + Less, + ), + ( + "0.0000000007762706076409491335", + "-2489879185787497651.6458518595", + Greater, + ), + ( + "-0.0000000007762706076409491335", + "-2489879185787497651.6458518595", + Greater, + ), + // 001 equality + ( + "316007568232.9263258873102336", + "316007568232.9263258873102336", + Equal, + ), + // 001 000 same scale + ( + "316007568232.9263258873102336", + "0.0000000000000000", + Greater, + ), + ("-316007568232.9263258873102336", "0.0000000000000000", Less), + ( + "316007568232.9263258873102336", + "-0.0000000000000000", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-0.0000000000000000", + Less, + ), + // 001 000 different scale + ("3522055990024364385815547084.8", "0.000000000000", Greater), + ("-3522055990024364385815547084.8", "0.000000000000", Less), + ("3522055990024364385815547084.8", "-0.000000000000", Greater), + ("-3522055990024364385815547084.8", "-0.000000000000", Less), + // 001 100 same scale + ( + "316007568232.9263258873102336", + "0.0000001073412971", + Greater, + ), + ("-316007568232.9263258873102336", "0.0000001073412971", Less), + ( + "316007568232.9263258873102336", + "-0.0000001073412971", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-0.0000001073412971", + Less, + ), + // 001 100 different scale + ( + "1319006.0491408208640440532992", + "0.00000000000000611866432", + Greater, + ), + ( + "-1319006.0491408208640440532992", + "0.00000000000000611866432", + Less, + ), + ( + "1319006.0491408208640440532992", + "-0.00000000000000611866432", + Greater, + ), + ( + "-1319006.0491408208640440532992", + "-0.00000000000000611866432", + Less, + ), + // 001 010 same scale + ( + "316007568232.9263258873102336", + "159.1054215143227392", + Greater, + ), + ( + "-316007568232.9263258873102336", + "159.1054215143227392", + Less, + ), + ( + "316007568232.9263258873102336", + "-159.1054215143227392", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-159.1054215143227392", + Less, + ), + // 001 010 different scale + ( + "2470144.7146711063666704252928", + "211.0186916505714688", + Greater, + ), + ( + "-2470144.7146711063666704252928", + "211.0186916505714688", + Less, + ), + ( + "2470144.7146711063666704252928", + "-211.0186916505714688", + Greater, + ), + ( + "-2470144.7146711063666704252928", + "-211.0186916505714688", + Less, + ), + // 001 110 same scale + ( + "316007568232.9263258873102336", + "15.1186658969096112", + Greater, + ), + ( + "-316007568232.9263258873102336", + "15.1186658969096112", + Less, + ), + ( + "316007568232.9263258873102336", + "-15.1186658969096112", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-15.1186658969096112", + Less, + ), + // 001 110 different scale + ( + "3840504199004148630832.3360768", + "7581138850996748864", + Greater, + ), + ( + "-3840504199004148630832.3360768", + "7581138850996748864", + Less, + ), + ( + "3840504199004148630832.3360768", + "-7581138850996748864", + Greater, + ), + ( + "-3840504199004148630832.3360768", + "-7581138850996748864", + Less, + ), + // 001 001 same scale + ( + "316007568232.9263258873102336", + "810157633226.6053390856880128", + Less, + ), + ( + "-316007568232.9263258873102336", + "810157633226.6053390856880128", + Less, + ), + ( + "316007568232.9263258873102336", + "-810157633226.6053390856880128", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-810157633226.6053390856880128", + Greater, + ), + // 001 001 different scale + ( + "1951046382014.4037956952260608", + "3626102772868740412010083.1232", + Less, + ), + ( + "-1951046382014.4037956952260608", + "3626102772868740412010083.1232", + Less, + ), + ( + "1951046382014.4037956952260608", + "-3626102772868740412010083.1232", + Greater, + ), + ( + "-1951046382014.4037956952260608", + "-3626102772868740412010083.1232", + Greater, + ), + // 001 101 same scale + ( + "316007568232.9263258873102336", + "3258394380359.1965879291312453", + Less, + ), + ( + "-316007568232.9263258873102336", + "3258394380359.1965879291312453", + Less, + ), + ( + "316007568232.9263258873102336", + "-3258394380359.1965879291312453", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-3258394380359.1965879291312453", + Greater, + ), + // 001 101 different scale + ( + "17580513970289834.943527780352", + "3.7977957031395371036126086595", + Greater, + ), + ( + "-17580513970289834.943527780352", + "3.7977957031395371036126086595", + Less, + ), + ( + "17580513970289834.943527780352", + "-3.7977957031395371036126086595", + Greater, + ), + ( + "-17580513970289834.943527780352", + "-3.7977957031395371036126086595", + Less, + ), + // 001 011 same scale + ( + "316007568232.9263258873102336", + "1154574080460.9867510617997312", + Less, + ), + ( + "-316007568232.9263258873102336", + "1154574080460.9867510617997312", + Less, + ), + ( + "316007568232.9263258873102336", + "-1154574080460.9867510617997312", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-1154574080460.9867510617997312", + Greater, + ), + // 001 011 different scale + ( + "2008379587.5525351789031325696", + "32824109460554.341800487157760", + Less, + ), + ( + "-2008379587.5525351789031325696", + "32824109460554.341800487157760", + Less, + ), + ( + "2008379587.5525351789031325696", + "-32824109460554.341800487157760", + Greater, + ), + ( + "-2008379587.5525351789031325696", + "-32824109460554.341800487157760", + Greater, + ), + // 001 111 same scale + ( + "316007568232.9263258873102336", + "2816795479724.6069787794805311", + Less, + ), + ( + "-316007568232.9263258873102336", + "2816795479724.6069787794805311", + Less, + ), + ( + "316007568232.9263258873102336", + "-2816795479724.6069787794805311", + Greater, + ), + ( + "-316007568232.9263258873102336", + "-2816795479724.6069787794805311", + Greater, + ), + // 001 111 different scale + ( + "3536806574745420013541890.4576", + "9793146.81730411145833529126", + Greater, + ), + ( + "-3536806574745420013541890.4576", + "9793146.81730411145833529126", + Less, + ), + ( + "3536806574745420013541890.4576", + "-9793146.81730411145833529126", + Greater, + ), + ( + "-3536806574745420013541890.4576", + "-9793146.81730411145833529126", + Less, + ), + // 101 equality + ( + "254208186622762823842.71629992", + "254208186622762823842.71629992", + Equal, + ), + // 101 000 same scale + ("254208186622762823842.71629992", "0.00000000", Greater), + ("-254208186622762823842.71629992", "0.00000000", Less), + ("254208186622762823842.71629992", "-0.00000000", Greater), + ("-254208186622762823842.71629992", "-0.00000000", Less), + // 101 000 different scale + ( + "975421950664039.3614304091804", + "0.0000000000000000", + Greater, + ), + ("-975421950664039.3614304091804", "0.0000000000000000", Less), + ( + "975421950664039.3614304091804", + "-0.0000000000000000", + Greater, + ), + ( + "-975421950664039.3614304091804", + "-0.0000000000000000", + Less, + ), + // 101 100 same scale + ("254208186622762823842.71629992", "10.74141379", Greater), + ("-254208186622762823842.71629992", "10.74141379", Less), + ("254208186622762823842.71629992", "-10.74141379", Greater), + ("-254208186622762823842.71629992", "-10.74141379", Less), + // 101 100 different scale + ( + "2552221405032275.8358630506229", + "0.000000000000000592656995", + Greater, + ), + ( + "-2552221405032275.8358630506229", + "0.000000000000000592656995", + Less, + ), + ( + "2552221405032275.8358630506229", + "-0.000000000000000592656995", + Greater, + ), + ( + "-2552221405032275.8358630506229", + "-0.000000000000000592656995", + Less, + ), + // 101 010 same scale + ( + "254208186622762823842.71629992", + "62767493748.48499712", + Greater, + ), + ( + "-254208186622762823842.71629992", + "62767493748.48499712", + Less, + ), + ( + "254208186622762823842.71629992", + "-62767493748.48499712", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-62767493748.48499712", + Less, + ), + // 101 010 different scale + ( + "197346074219.25327589999174264", + "0.7623493575178715136", + Greater, + ), + ( + "-197346074219.25327589999174264", + "0.7623493575178715136", + Less, + ), + ( + "197346074219.25327589999174264", + "-0.7623493575178715136", + Greater, + ), + ( + "-197346074219.25327589999174264", + "-0.7623493575178715136", + Less, + ), + // 101 110 same scale + ( + "254208186622762823842.71629992", + "76597126194.51389094", + Greater, + ), + ( + "-254208186622762823842.71629992", + "76597126194.51389094", + Less, + ), + ( + "254208186622762823842.71629992", + "-76597126194.51389094", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-76597126194.51389094", + Less, + ), + // 101 110 different scale + ( + "25899773651648.380071130043467", + "61306258142804903.38", + Less, + ), + ( + "-25899773651648.380071130043467", + "61306258142804903.38", + Less, + ), + ( + "25899773651648.380071130043467", + "-61306258142804903.38", + Greater, + ), + ( + "-25899773651648.380071130043467", + "-61306258142804903.38", + Greater, + ), + // 101 001 same scale + ( + "254208186622762823842.71629992", + "83547191565151621967.28086528", + Greater, + ), + ( + "-254208186622762823842.71629992", + "83547191565151621967.28086528", + Less, + ), + ( + "254208186622762823842.71629992", + "-83547191565151621967.28086528", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-83547191565151621967.28086528", + Less, + ), + // 101 001 different scale + ( + "244762.90302171293318719286219", + "9964072.600255221193011888128", + Less, + ), + ( + "-244762.90302171293318719286219", + "9964072.600255221193011888128", + Less, + ), + ( + "244762.90302171293318719286219", + "-9964072.600255221193011888128", + Greater, + ), + ( + "-244762.90302171293318719286219", + "-9964072.600255221193011888128", + Greater, + ), + // 101 101 same scale + ( + "254208186622762823842.71629992", + "106541875981662806348.63716235", + Greater, + ), + ( + "-254208186622762823842.71629992", + "106541875981662806348.63716235", + Less, + ), + ( + "254208186622762823842.71629992", + "-106541875981662806348.63716235", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-106541875981662806348.63716235", + Less, + ), + // 101 101 different scale + ( + "362319.18250030256385507568342", + "3619454249020577423546109236", + Less, + ), + ( + "-362319.18250030256385507568342", + "3619454249020577423546109236", + Less, + ), + ( + "362319.18250030256385507568342", + "-3619454249020577423546109236", + Greater, + ), + ( + "-362319.18250030256385507568342", + "-3619454249020577423546109236", + Greater, + ), + // 101 011 same scale + ( + "254208186622762823842.71629992", + "156781478378762688050.06557184", + Greater, + ), + ( + "-254208186622762823842.71629992", + "156781478378762688050.06557184", + Less, + ), + ( + "254208186622762823842.71629992", + "-156781478378762688050.06557184", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-156781478378762688050.06557184", + Less, + ), + // 101 011 different scale + ( + "2486073465266.0337130589931876", + "153874906950888902858.19691008", + Less, + ), + ( + "-2486073465266.0337130589931876", + "153874906950888902858.19691008", + Less, + ), + ( + "2486073465266.0337130589931876", + "-153874906950888902858.19691008", + Greater, + ), + ( + "-2486073465266.0337130589931876", + "-153874906950888902858.19691008", + Greater, + ), + // 101 111 same scale + ( + "254208186622762823842.71629992", + "10101645723744656462.08148676", + Greater, + ), + ( + "-254208186622762823842.71629992", + "10101645723744656462.08148676", + Less, + ), + ( + "254208186622762823842.71629992", + "-10101645723744656462.08148676", + Greater, + ), + ( + "-254208186622762823842.71629992", + "-10101645723744656462.08148676", + Less, + ), + // 101 111 different scale + ( + "14107601965.909635434653634526", + "111238758546502973973477.99454", + Less, + ), + ( + "-14107601965.909635434653634526", + "111238758546502973973477.99454", + Less, + ), + ( + "14107601965.909635434653634526", + "-111238758546502973973477.99454", + Greater, + ), + ( + "-14107601965.909635434653634526", + "-111238758546502973973477.99454", + Greater, + ), + // 011 equality + ( + "272322.48219624218537039495168", + "272322.48219624218537039495168", + Equal, + ), + // 011 000 same scale + ( + "272322.48219624218537039495168", + "0.00000000000000000000000", + Greater, + ), + ( + "-272322.48219624218537039495168", + "0.00000000000000000000000", + Less, + ), + ( + "272322.48219624218537039495168", + "-0.00000000000000000000000", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-0.00000000000000000000000", + Less, + ), + // 011 000 different scale + ( + "3214885516.0787854158246969344", + "0.00000000000000000", + Greater, + ), + ( + "-3214885516.0787854158246969344", + "0.00000000000000000", + Less, + ), + ( + "3214885516.0787854158246969344", + "-0.00000000000000000", + Greater, + ), + ( + "-3214885516.0787854158246969344", + "-0.00000000000000000", + Less, + ), + // 011 100 same scale + ( + "272322.48219624218537039495168", + "0.00000000000000379487994", + Greater, + ), + ( + "-272322.48219624218537039495168", + "0.00000000000000379487994", + Less, + ), + ( + "272322.48219624218537039495168", + "-0.00000000000000379487994", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-0.00000000000000379487994", + Less, + ), + // 011 100 different scale + ( + "388166715906.19371912596291584", + "0.000000000000001996700736", + Greater, + ), + ( + "-388166715906.19371912596291584", + "0.000000000000001996700736", + Less, + ), + ( + "388166715906.19371912596291584", + "-0.000000000000001996700736", + Greater, + ), + ( + "-388166715906.19371912596291584", + "-0.000000000000001996700736", + Less, + ), + // 011 010 same scale + ( + "272322.48219624218537039495168", + "0.00000175873997328613376", + Greater, + ), + ( + "-272322.48219624218537039495168", + "0.00000175873997328613376", + Less, + ), + ( + "272322.48219624218537039495168", + "-0.00000175873997328613376", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-0.00000175873997328613376", + Less, + ), + // 011 010 different scale + ( + "17963864.0946434527121637376", + "0.0000000001112699367808040960", + Greater, + ), + ( + "-17963864.0946434527121637376", + "0.0000000001112699367808040960", + Less, + ), + ( + "17963864.0946434527121637376", + "-0.0000000001112699367808040960", + Greater, + ), + ( + "-17963864.0946434527121637376", + "-0.0000000001112699367808040960", + Less, + ), + // 011 110 same scale + ( + "272322.48219624218537039495168", + "0.00009168252278596474115", + Greater, + ), + ( + "-272322.48219624218537039495168", + "0.00009168252278596474115", + Less, + ), + ( + "272322.48219624218537039495168", + "-0.00009168252278596474115", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-0.00009168252278596474115", + Less, + ), + // 011 110 different scale + ( + "3.1530040332824324172729548800", + "0.05636139104712652784", + Greater, + ), + ( + "-3.1530040332824324172729548800", + "0.05636139104712652784", + Less, + ), + ( + "3.1530040332824324172729548800", + "-0.05636139104712652784", + Greater, + ), + ( + "-3.1530040332824324172729548800", + "-0.05636139104712652784", + Less, + ), + // 011 001 same scale + ( + "272322.48219624218537039495168", + "78956.15667671475190631497728", + Greater, + ), + ( + "-272322.48219624218537039495168", + "78956.15667671475190631497728", + Less, + ), + ( + "272322.48219624218537039495168", + "-78956.15667671475190631497728", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-78956.15667671475190631497728", + Less, + ), + // 011 001 different scale + ( + "247159638929.90774677497446400", + "18573048.37697991870462820352", + Greater, + ), + ( + "-247159638929.90774677497446400", + "18573048.37697991870462820352", + Less, + ), + ( + "247159638929.90774677497446400", + "-18573048.37697991870462820352", + Greater, + ), + ( + "-247159638929.90774677497446400", + "-18573048.37697991870462820352", + Less, + ), + // 011 101 same scale + ( + "272322.48219624218537039495168", + "311953.28803357654172947915942", + Less, + ), + ( + "-272322.48219624218537039495168", + "311953.28803357654172947915942", + Less, + ), + ( + "272322.48219624218537039495168", + "-311953.28803357654172947915942", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-311953.28803357654172947915942", + Greater, + ), + // 011 101 different scale + ( + "2922696937234119470.0273745920", + "22441101906503785827606686629", + Less, + ), + ( + "-2922696937234119470.0273745920", + "22441101906503785827606686629", + Less, + ), + ( + "2922696937234119470.0273745920", + "-22441101906503785827606686629", + Greater, + ), + ( + "-2922696937234119470.0273745920", + "-22441101906503785827606686629", + Greater, + ), + // 011 011 same scale + ( + "272322.48219624218537039495168", + "348316.28306497394164006649856", + Less, + ), + ( + "-272322.48219624218537039495168", + "348316.28306497394164006649856", + Less, + ), + ( + "272322.48219624218537039495168", + "-348316.28306497394164006649856", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-348316.28306497394164006649856", + Greater, + ), + // 011 011 different scale + ( + "178190346.76624086261395619840", + "62208030746.22038852927225856", + Less, + ), + ( + "-178190346.76624086261395619840", + "62208030746.22038852927225856", + Less, + ), + ( + "178190346.76624086261395619840", + "-62208030746.22038852927225856", + Greater, + ), + ( + "-178190346.76624086261395619840", + "-62208030746.22038852927225856", + Greater, + ), + // 011 111 same scale + ( + "272322.48219624218537039495168", + "41534.52021391898039335355927", + Greater, + ), + ( + "-272322.48219624218537039495168", + "41534.52021391898039335355927", + Less, + ), + ( + "272322.48219624218537039495168", + "-41534.52021391898039335355927", + Greater, + ), + ( + "-272322.48219624218537039495168", + "-41534.52021391898039335355927", + Less, + ), + // 011 111 different scale + ( + "11.959910518519677083499626496", + "2844684364802261541879551.2259", + Less, + ), + ( + "-11.959910518519677083499626496", + "2844684364802261541879551.2259", + Less, + ), + ( + "11.959910518519677083499626496", + "-2844684364802261541879551.2259", + Greater, + ), + ( + "-11.959910518519677083499626496", + "-2844684364802261541879551.2259", + Greater, + ), + // 111 equality + ( + "3836286746260530032892706.6174", + "3836286746260530032892706.6174", + Equal, + ), + // 111 000 same scale + ("3836286746260530032892706.6174", "0.0000", Greater), + ("-3836286746260530032892706.6174", "0.0000", Less), + ("3836286746260530032892706.6174", "-0.0000", Greater), + ("-3836286746260530032892706.6174", "-0.0000", Less), + // 111 000 different scale + ("4401861854803552.033657814547", "0.0000000", Greater), + ("-4401861854803552.033657814547", "0.0000000", Less), + ("4401861854803552.033657814547", "-0.0000000", Greater), + ("-4401861854803552.033657814547", "-0.0000000", Less), + // 111 100 same scale + ("3836286746260530032892706.6174", "68758.6561", Greater), + ("-3836286746260530032892706.6174", "68758.6561", Less), + ("3836286746260530032892706.6174", "-68758.6561", Greater), + ("-3836286746260530032892706.6174", "-68758.6561", Less), + // 111 100 different scale + ( + "18794337354296131695536777.153", + "0.000000000000000001563875977", + Greater, + ), + ( + "-18794337354296131695536777.153", + "0.000000000000000001563875977", + Less, + ), + ( + "18794337354296131695536777.153", + "-0.000000000000000001563875977", + Greater, + ), + ( + "-18794337354296131695536777.153", + "-0.000000000000000001563875977", + Less, + ), + // 111 010 same scale + ( + "3836286746260530032892706.6174", + "439097665563236.7616", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "439097665563236.7616", + Less, + ), + ( + "3836286746260530032892706.6174", + "-439097665563236.7616", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-439097665563236.7616", + Less, + ), + // 111 010 different scale + ( + "219364497389.57405761662363679", + "0.6569644274462228480", + Greater, + ), + ( + "-219364497389.57405761662363679", + "0.6569644274462228480", + Less, + ), + ( + "219364497389.57405761662363679", + "-0.6569644274462228480", + Greater, + ), + ( + "-219364497389.57405761662363679", + "-0.6569644274462228480", + Less, + ), + // 111 110 same scale + ( + "3836286746260530032892706.6174", + "100013274294974.8269", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "100013274294974.8269", + Less, + ), + ( + "3836286746260530032892706.6174", + "-100013274294974.8269", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-100013274294974.8269", + Less, + ), + // 111 110 different scale + ( + "76072704083682.85472479207171", + "50.52437989651117182", + Greater, + ), + ( + "-76072704083682.85472479207171", + "50.52437989651117182", + Less, + ), + ( + "76072704083682.85472479207171", + "-50.52437989651117182", + Greater, + ), + ( + "-76072704083682.85472479207171", + "-50.52437989651117182", + Less, + ), + // 111 001 same scale + ( + "3836286746260530032892706.6174", + "2766133872545894272402142.0032", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "2766133872545894272402142.0032", + Less, + ), + ( + "3836286746260530032892706.6174", + "-2766133872545894272402142.0032", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-2766133872545894272402142.0032", + Less, + ), + // 111 001 different scale + ( + "38199979438250010.80610984395", + "31104752430710.408848162684928", + Greater, + ), + ( + "-38199979438250010.80610984395", + "31104752430710.408848162684928", + Less, + ), + ( + "38199979438250010.80610984395", + "-31104752430710.408848162684928", + Greater, + ), + ( + "-38199979438250010.80610984395", + "-31104752430710.408848162684928", + Less, + ), + // 111 101 same scale + ( + "3836286746260530032892706.6174", + "441847458119168110406908.6115", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "441847458119168110406908.6115", + Less, + ), + ( + "3836286746260530032892706.6174", + "-441847458119168110406908.6115", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-441847458119168110406908.6115", + Less, + ), + // 111 101 different scale + ( + "255945012905633524.15746865235", + "1005021647855597114428453997.8", + Less, + ), + ( + "-255945012905633524.15746865235", + "1005021647855597114428453997.8", + Less, + ), + ( + "255945012905633524.15746865235", + "-1005021647855597114428453997.8", + Greater, + ), + ( + "-255945012905633524.15746865235", + "-1005021647855597114428453997.8", + Greater, + ), + // 111 011 same scale + ( + "3836286746260530032892706.6174", + "1111481055212557787730018.3040", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "1111481055212557787730018.3040", + Less, + ), + ( + "3836286746260530032892706.6174", + "-1111481055212557787730018.3040", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-1111481055212557787730018.3040", + Less, + ), + // 111 011 different scale + ( + "79710135995301690627798250.27", + "4613684285077.479267304996864", + Greater, + ), + ( + "-79710135995301690627798250.27", + "4613684285077.479267304996864", + Less, + ), + ( + "79710135995301690627798250.27", + "-4613684285077.479267304996864", + Greater, + ), + ( + "-79710135995301690627798250.27", + "-4613684285077.479267304996864", + Less, + ), + // 111 111 same scale + ( + "3836286746260530032892706.6174", + "1881105048659612897896770.8539", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "1881105048659612897896770.8539", + Less, + ), + ( + "3836286746260530032892706.6174", + "-1881105048659612897896770.8539", + Greater, + ), + ( + "-3836286746260530032892706.6174", + "-1881105048659612897896770.8539", + Less, + ), + // 111 111 different scale + ( + "3879592276836332218003.2886500", + "35612499407667292.686490959658", + Greater, + ), + ( + "-3879592276836332218003.2886500", + "35612499407667292.686490959658", + Less, + ), + ( + "3879592276836332218003.2886500", + "-35612499407667292.686490959658", + Greater, + ), + ( + "-3879592276836332218003.2886500", + "-35612499407667292.686490959658", + Less, + ), + ]; + for &(a, b, c) in tests { + cmp(a, b, c); + } +} + +#[test] +fn test_parse_decimal() { + let tests = [ + ("-999999999999999999"), + ("999999999999999999"), + ("-999999999.99999999999999999999999999999"), + ("-99999999999999999999999999999999999999"), + ("9999999999999999999999999.9999999999999"), + ("99999999999999999999999999999999999999"), + ("-999999999999999999999999999999999999999999999999999.9999999999999999999999999"), + ("-9999999999999999999999999999999999999999999999999999999999999999999999999999"), + ("99999999999999999999999999999999999.99999999999999999999999999999999999999999"), + ("9999999999999999999999999999999999999999999999999999999999999999999999999999"), + "1.123456", + "-0.123456", + "-1.00", + "92233720368547758.07", + "-92233720368547758.08", + "233.323223", + "-233.43343", + "0.000001", + "-0.000001", + "79228162514264337593543950330", + "79.228162514264337593543950330", + "12.3456789", + "5233.9008808150288439427720175", + "-5233.9008808150288439427720175", + "-1.9393111376951473493673267553", + "-0.1939311137695147349367326755", + "-0.0193931113769514734936732676", + "-0.0019393111376951473493673268", + "3.1415926535897932384626433833", + "6.2831853071795864769252867666", + "1.5707963267948966192313216916", + "2.7182818284590452353602874714", + "0.3678794411714423215955237702", + "-714606667614253123173036.85120", + "-79228157791897.854723898738431", + "184512476.73336922111", + "0.1000000000000000055511151231257827021181583404541015625", + "0.333333333333333314829616256247390992939472198486328125", + "3.141592653589793115997963468544185161590576171875", + "3.000000000000000444089209850062616169452667236328125", + "0.001000000047497451305389404296875", + "0.033203125", + "1401757440", + "21509.19921875", + "2289619968", + "0.00000999999974737875163555145263671875", + "0.100000001490116119384765625", + "0.20000000298023223876953125", + "0.00000000023283064365386962890625", + "0.146938621997833251953125", + "100000002004087734272", + "1000000015047466219876688855040", + "99999996802856924650656260769173209088", + "317000006395220278118691742155288870912", + ]; + + for test in tests { + let v = parse_value(test.as_bytes()).unwrap(); + let s = format!("{}", v); + let buf = v.to_vec(); + let r = RawJsonb::new(&buf); + let ss = r.to_string(); + + assert_eq!(test, s); + assert_eq!(test, ss); + } +} + +#[test] +fn test_parse_float() { + let tests = [ + ("-1e77", "-99999999999999999999999999999999999999999999999999999999999999999999999999991"), + ("1e79", "9999999999999999999999999999999999999999999999999999999999999999999999999999123"), + ("2.350988981904268e-38", "2.35098898190426788090088725919040801362055736959656341832065776397049129686767088287524529732763767242431640625E-38"), + ("2.350987440475957e-38", "2.350987440475957123602109243087866394712812961308427354153308831195379018097479928428583662025630474090576171875E-38"), + ("3.1700000098946436e-38", "3.1700000098946435501119816090716154772221806896649747100732700841687651538425285480116144753992557525634765625E-38"), + ("1.401298464324817e-45", "1.40129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125E-45"), + ("1.0005271035279194e-42", "1.0005271035279193886395429224690001177341070264998322610345467546973108330377044694614596664905548095703125e-42"), + ("3.919999330594565e-39", "3.91999933059456489828739575494312783522406115751507460249208160269472102366083987590172910131514072418212890625E-39"), + ("8.544283616667655e-306", "8.5442836166676545758745469881475846986178991076220674838778719735182619591847930738097459423424470941335996703553180065389909675214026779902482660710563190540056652827644969523715287333767167538014707594736533997824798692690142890189753467148541192574394234161821394612038920127719106177776787375705338074667624093006332620080979623387970617655687653904110103913103933178304212511707769987213793880764157458662751217010283883439888757033430556011326632895537144105152597427684695380215955244686097497705226475608085097617996058799189036784865947060736971859470127760066696392182317083388979882704968230500619384728741377732016919538675848783600526390429792978252568964346334556191024880163233082812954995600973750951114861484914086986464099027216434478759765625e-306"), + ("3e300", "3000000000000000157514280765613260746113405743324477464747562346535407373966724587359114125241343592131113331498651634530827569706081291726934376554360120948545161602779727411213490701384364270178106859704912399835243357116902922640223958228340427483737776366460170528514347008416589160596378201620480"), + ("3.105036184601418e231", "3105036184601417870297958976925005110513772034233393222278104076052101905372753772661756817657292955900975461394262146412343160088229628782888574550082362278408909952041699811100530571263196889650525998387432937501785693707632115712"), + ("2.81341650018752e-308", "2.8134165001875198278759275525943498067505063001967969175506480744152639496835355462897889950138699429916690515722729976876607247658891051736045520063301219592298855232146428654590713004216312194773871772185068366206180596731958890086634117134422695105490626598276746331472433159429067991016548063113298957324839879447939977012897422163463450947345510093578791948321798481101505330952230105511530048812659083481787407026258844307461890753626327683153826358878159001221539330872743255707112001100520519610144879206546597846231715071742093092641158571855689231930930474890818690333095288369471228217443460522531282790309374378111440076317827545086535792316428407651758951233693496387904508572484340169054222573303301594335791590596740352481219815672375261783599853515625E-308"), + ("-1.7976931348623157e308", "-179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368"), + ("1.7976931348623157e308", "179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368"), + ]; + + for (expected, test) in tests { + let v = parse_value(test.as_bytes()).unwrap(); + let s = format!("{}", v); + let buf = v.to_vec(); + let r = RawJsonb::new(&buf); + let ss = r.to_string(); + + assert_eq!(expected, s); + assert_eq!(expected, ss); + } +} diff --git a/tests/it/parser.rs b/tests/it/parser.rs index 4b3bc51..3932a2a 100644 --- a/tests/it/parser.rs +++ b/tests/it/parser.rs @@ -64,17 +64,12 @@ fn test_parse_number_errors() { test_parse_err(&[ ("+", "expected value, pos 1"), (".", "expected value, pos 1"), - ("-", "EOF while parsing a value, pos 1"), - ("00", "invalid number, pos 2"), + ("-", "expected value, pos 1"), ("0x80", "trailing characters, pos 2"), ("\\0", "expected value, pos 1"), - (".0", "expected value, pos 1"), - ("0.", "EOF while parsing a value, pos 2"), - ("1.", "EOF while parsing a value, pos 2"), - ("1.a", "invalid number, pos 3"), - ("1.e1", "invalid number, pos 3"), - ("1e", "EOF while parsing a value, pos 2"), - ("1e+", "EOF while parsing a value, pos 3"), + ("1.a", "trailing characters, pos 3"), + ("1e", "invalid number, pos 2"), + ("1e+", "invalid number, pos 3"), ("1a", "trailing characters, pos 2"), ]); } @@ -242,6 +237,13 @@ fn test_parse_f64() { 000000000000000000e-10", Value::Number(Number::Float64(1e308)), ), + // Extended JSON number syntax + ("+1", Value::Number(Number::Int64(1))), + ("00", Value::Number(Number::UInt64(0))), + (".0", Value::Number(Number::UInt64(0))), + ("0.", Value::Number(Number::UInt64(0))), + ("1.", Value::Number(Number::UInt64(1))), + ("1.e1", Value::Number(Number::Float64(10.0))), ]); } @@ -313,7 +315,6 @@ fn test_parse_array() { ("[ ", "EOF while parsing a value, pos 2"), ("[1", "EOF while parsing a value, pos 2"), ("[1,", "EOF while parsing a value, pos 3"), - ("[1,]", "expected value, pos 4"), ("[1 2]", "expected `,` or `]`, pos 3"), ("[]a", "trailing characters, pos 3"), ]); @@ -367,6 +368,20 @@ fn test_parse_array() { ]), ]), ), + // Extended JSON array syntax + ( + "[1, ]", + Value::Array(vec![Value::Number(Number::UInt64(1)), Value::Null]), + ), + ( + "[ , 2, 3]", + Value::Array(vec![ + Value::Null, + Value::Number(Number::UInt64(2)), + Value::Number(Number::UInt64(3)), + ]), + ), + ("[ , ]", Value::Array(vec![Value::Null, Value::Null])), ]); }