1use num_cmp::NumCmp;
2use serde_json::{Map, Value};
3
4macro_rules! num_cmp {
5 ($left:expr, $right:expr) => {
6 if let Some(b) = $right.as_u64() {
7 NumCmp::num_eq($left, b)
8 } else if let Some(b) = $right.as_i64() {
9 NumCmp::num_eq($left, b)
10 } else {
11 #[cfg(feature = "arbitrary-precision")]
12 {
13 use crate::numeric::bignum;
14 use fraction::BigFraction;
15
16 let left_frac = BigFraction::from($left);
17
18 if let Some(right_bigint) = bignum::try_parse_bigint($right) {
20 let right_frac = bignum::whole(right_bigint);
21 left_frac == right_frac
22 } else if let Some(right_frac) = bignum::try_parse_bigfraction($right) {
23 left_frac == right_frac
24 } else if let Some(b) = $right.as_f64() {
25 left_frac == BigFraction::from(b)
27 } else {
28 false
30 }
31 }
32 #[cfg(not(feature = "arbitrary-precision"))]
33 {
34 $right.as_f64().is_some_and(|b| NumCmp::num_eq($left, b))
37 }
38 }
39 };
40}
41
42#[inline]
44#[doc(hidden)]
45#[must_use]
46pub fn equal_numbers<L: crate::JsonNumber>(left: &L, right: &serde_json::Number) -> bool {
47 #[cfg(feature = "arbitrary-precision")]
48 {
49 use crate::numeric::bignum;
50 use fraction::BigFraction;
51
52 let (left_text, right_text) = (left.as_str(), right.as_str());
53 if bignum::exceeds_digit_budget(&left_text) || bignum::exceeds_digit_budget(right_text) {
54 return bignum::Decimal::parse(&left_text) == bignum::Decimal::parse(right_text);
55 }
56 if let Some(left_bigint) = bignum::try_parse_bigint(&left.to_number()) {
58 if let Some(right_bigint) = bignum::try_parse_bigint(right) {
59 left_bigint == right_bigint
60 } else if let Some(b) = right.as_u64() {
61 left_bigint == num_bigint::BigInt::from(b)
62 } else if let Some(b) = right.as_i64() {
63 left_bigint == num_bigint::BigInt::from(b)
64 } else if let Some(right_frac) = bignum::try_parse_bigfraction(right) {
65 bignum::whole(left_bigint) == right_frac
66 } else if let Some(b) = right.as_f64() {
67 bignum::whole(left_bigint) == BigFraction::from(b)
68 } else {
69 unreachable!("Right is not parseable as any numeric type - should not happen for valid JSON numbers")
70 }
71 } else if let Some(left_frac) = bignum::try_parse_bigfraction(&left.to_number()) {
72 if let Some(right_frac) = bignum::try_parse_bigfraction(right) {
73 left_frac == right_frac
74 } else if let Some(right_bigint) = bignum::try_parse_bigint(right) {
75 left_frac == bignum::whole(right_bigint)
76 } else if let Some(b) = right.as_u64() {
77 left_frac == BigFraction::from(b)
78 } else if let Some(b) = right.as_i64() {
79 left_frac == BigFraction::from(b)
80 } else if let Some(b) = right.as_f64() {
81 left_frac == BigFraction::from(b)
82 } else {
83 unreachable!("Right is not parseable as any numeric type - should not happen for valid JSON numbers")
84 }
85 } else if let Some(a) = left.as_u64() {
86 num_cmp!(a, right)
87 } else if let Some(a) = left.as_i64() {
88 num_cmp!(a, right)
89 } else if let Some(a) = left.as_f64() {
90 num_cmp!(a, right)
91 } else {
92 false
97 }
98 }
99 #[cfg(not(feature = "arbitrary-precision"))]
100 {
101 if let Some(a) = left.as_u64() {
102 num_cmp!(a, right)
103 } else if let Some(a) = left.as_i64() {
104 num_cmp!(a, right)
105 } else if let Some(a) = left.as_f64() {
106 num_cmp!(a, right)
107 } else {
108 false
110 }
111 }
112}
113
114#[must_use]
116#[allow(clippy::missing_panics_doc)]
117pub fn equal(left: &Value, right: &Value) -> bool {
118 match (left, right) {
119 (Value::String(left), Value::String(right)) => left == right,
120 (Value::Bool(left), Value::Bool(right)) => left == right,
121 (Value::Null, Value::Null) => true,
122 (Value::Number(left), Value::Number(right)) => equal_numbers(left, right),
123 (Value::Array(left), Value::Array(right)) => equal_arrays(left, right),
124 (Value::Object(left), Value::Object(right)) => equal_objects(left, right),
125 (_, _) => false,
126 }
127}
128
129#[inline]
130#[must_use]
131pub fn equal_arrays(left: &[Value], right: &[Value]) -> bool {
132 left.len() == right.len() && {
133 let mut idx = 0_usize;
134 while idx < left.len() {
135 if !equal(&left[idx], &right[idx]) {
136 return false;
137 }
138 idx += 1;
139 }
140 true
141 }
142}
143
144#[inline]
145#[must_use]
146pub fn equal_objects(left: &Map<String, Value>, right: &Map<String, Value>) -> bool {
147 if left.len() != right.len() {
148 return false;
149 }
150 for ((left_key, left_value), (right_key, right_value)) in left.iter().zip(right) {
151 if left_key != right_key {
152 return left
155 .iter()
156 .all(|(key, value)| right.get(key).is_some_and(|other| equal(value, other)));
157 }
158 if !equal(left_value, right_value) {
159 return false;
160 }
161 }
162 true
163}
164
165#[cfg(test)]
166mod tests {
167 use super::equal;
168 use serde_json::{json, Value};
169 use test_case::test_case;
170
171 #[test_case(&json!(1), &json!(1.0))]
172 #[test_case(&json!([2]), &json!([2.0]))]
173 #[test_case(&json!([-3]), &json!([-3.0]))]
174 #[test_case(&json!({"a": 1}), &json!({"a": 1.0}))]
175 fn are_equal(left: &Value, right: &Value) {
176 assert!(equal(left, right));
177 }
178
179 #[test_case(&json!(1), &json!(2.0))]
180 #[test_case(&json!([]), &json!(["foo"]))]
181 #[test_case(&json!([-3]), &json!([-4.0]))]
182 #[test_case(&json!({"a": 1}), &json!({"a": 1.0, "b": 2}))]
183 #[test_case(&json!({"a": 1, "b": 2}), &json!({"a": 1, "c": 2}))]
184 fn are_not_equal(left: &Value, right: &Value) {
185 assert!(!equal(left, right));
186 }
187
188 #[cfg(feature = "arbitrary-precision")]
189 mod arbitrary_precision {
190 use super::equal;
191 use serde_json::Value;
192 use test_case::test_case;
193
194 fn parse_json(s: &str) -> Value {
195 serde_json::from_str(s).unwrap()
196 }
197 #[test_case("0.1", "0.1", true; "exact decimal match")]
198 #[test_case("0.1", "0.10", true; "decimal with trailing zero")]
199 #[test_case("0.1", "0.100000", true; "decimal with many trailing zeros")]
200 #[test_case("0.1", "0.2", false; "different decimals")]
201 #[test_case("0.3", "0.30", true; "another trailing zero case")]
202 #[test_case("1.0", "1", true; "decimal vs integer")]
203 #[test_case("1.00", "1.0", true; "decimals with different trailing zeros")]
204 #[test_case(
205 "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.5",
206 "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.5",
207 true;
208 "huge decimal self equality"
209 )]
210 #[test_case(
211 "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.5",
212 "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.6",
213 false;
214 "huge decimals different"
215 )]
216 #[test_case(
217 "99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999.5",
218 "100",
219 false;
220 "huge decimal vs small integer"
221 )]
222 #[test_case("18446744073709551616", "18446744073709551616", true; "large integer self equality")]
223 #[test_case("18446744073709551616", "18446744073709551617", false; "large integers different")]
224 #[test_case("99999999999999999999999999999999999999", "99999999999999999999999999999999999999", true; "very large integer equality")]
225 #[test_case("99999999999999999999999999999999999999", "100", false; "very large vs small integer")]
226 #[test_case("100", "100.0", true; "small integer vs decimal")]
227 #[test_case("0.1", "1", false; "small decimal vs integer")]
228 #[test_case("1.0", "1", true; "decimal one vs integer one")]
229 #[test_case("18446744073709551616", "100.5", false; "large int vs decimal")]
230 #[test_case("18446744073709551616.0", "18446744073709551616", true; "large int as decimal vs large int")]
231 #[test_case("-0.1", "-0.1", true; "negative decimal equality")]
232 #[test_case("-0.1", "-0.10", true; "negative decimal trailing zero")]
233 #[test_case("-18446744073709551616", "-18446744073709551616", true; "negative large int")]
234 #[test_case("-18446744073709551616", "18446744073709551616", false; "negative vs positive large")]
235 #[test_case("-100.5", "-100.5", true; "negative decimal match")]
236 #[test_case("-100.5", "100.5", false; "negative vs positive decimal")]
237 #[test_case("0", "0.0", true; "zero integer vs decimal")]
238 #[test_case("0.0", "0.00", true; "zero decimals with different precision")]
239 #[test_case("-0.0", "0.0", true; "negative zero vs positive zero")]
240 #[test_case("1e10", "10000000000", true; "scientific notation vs integer")]
241 #[test_case("1e19", "10000000000000000000", true; "scientific integer beyond i64")]
242 #[test_case("1e19", "10000000000000000001", false; "scientific integer mismatch")]
243 #[test_case("1.5e2", "150", true; "decimal scientific vs integer")]
244 #[test_case("1.5e2", "150.0", true; "decimal scientific vs decimal")]
245 #[test_case(r"[0.1, 0.2, 0.3]", r"[0.1, 0.2, 0.3]", true; "array exact match")]
246 #[test_case(r"[0.1, 0.2]", r"[0.10, 0.20]", true; "array with trailing zeros")]
247 #[test_case(r"[18446744073709551616]", r"[18446744073709551616]", true; "array with large integer")]
248 #[test_case(r"[0.1, 0.2]", r"[0.1, 0.3]", false; "array different values")]
249 #[test_case(r#"{"value": 0.1}"#, r#"{"value": 0.1}"#, true; "object exact match")]
250 #[test_case(r#"{"value": 0.1}"#, r#"{"value": 0.10}"#, true; "object with trailing zero")]
251 #[test_case(r#"{"id": 18446744073709551616}"#, r#"{"id": 18446744073709551616}"#, true; "object with large integer")]
252 #[test_case(r#"{"value": 0.1}"#, r#"{"value": 0.2}"#, false; "object different values")]
253 #[test_case("18446744073709551616", "-1", false; "large positive bigint vs negative i64")]
254 #[test_case("18446744073709551616", "-100", false; "large positive bigint vs negative i64 small")]
255 #[test_case("-18446744073709551616", "-1", false; "large negative bigint vs small negative i64")]
256 #[test_case("18446744073709551616", "1e10", false; "large bigint vs scientific notation f64")]
257 #[test_case("10000000000", "1e10", true; "bigint vs scientific notation equal")]
258 #[test_case("-18446744073709551616", "-1.5e3", false; "negative bigint vs scientific notation")]
259 #[test_case("0.5", "5e-1", true; "bigfraction vs scientific notation equal")]
260 #[test_case("0.3", "3e-1", true; "bigfraction vs scientific equal exact")]
261 #[test_case("123.456", "1.23456e2", true; "bigfraction vs scientific notation")]
262 #[test_case("0.1", "1e-2", false; "bigfraction vs scientific not equal")]
263 #[test_case("1e309", "1e309", true; "huge scientific notation now handled")]
264 #[test_case("1e400", "1e400", true; "extreme scientific notation now handled")]
265 #[test_case("1e-400", "1e-400", true; "extreme small scientific notation self equality")]
266 #[test_case("1e309", "1", false; "huge scientific notation vs integer")]
267 fn arbitrary_precision_equality(left_str: &str, right_str: &str, should_equal: bool) {
268 let left = parse_json(left_str);
269 let right = parse_json(right_str);
270 assert_eq!(equal(&left, &right), should_equal);
271 }
272 }
273}