///|
/// Compare decoded JSON values. Object member order is insignificant.
/// Numeric token spelling is significant unless `numeric` is selected.
pub fn Value::equals(
self : Value,
other : Value,
numeric~ : Bool = false,
limits~ : Limits = Limits::new(),
) -> Result[Bool, Diagnostic] {
let stack = [(self, other, 0)]
let mut reserved = 1
let mut left_text = 0
let mut right_text = 0
let charge_text = fn(a : String, b : String) -> Bool {
if a.length() > limits.max_source - left_text ||
b.length() > limits.max_source - right_text {
return false
}
left_text += a.length()
right_text += b.length()
true
}
if limits.max_nodes < 1 {
return Err(
Diagnostic::new(LimitExceeded, "JSON equality node limit exceeded"),
)
}
while stack.pop() is Some((left, right, depth)) {
match (left.kind, right.kind) {
(Null, Null) => ()
(Boolean(a), Boolean(b)) => if a != b { return Ok(false) }
(String(a), String(b)) => {
if !charge_text(a, b) {
return Err(
Diagnostic::new(LimitExceeded, "JSON equality text limit exceeded"),
)
}
if a != b {
return Ok(false)
}
}
(Numeric(a), Numeric(b)) => {
if a.token.length() > limits.max_number ||
b.token.length() > limits.max_number {
return Err(
Diagnostic::new(
LimitExceeded,
"JSON equality number token limit exceeded",
),
)
}
if !charge_text(a.token, b.token) {
return Err(
Diagnostic::new(LimitExceeded, "JSON equality text limit exceeded"),
)
}
if numeric {
if !a.numeric_equal(b) {
return Ok(false)
}
} else if a != b {
return Ok(false)
}
}
(Array(a), Array(b)) => {
if a.length() != b.length() {
return Ok(false)
}
if depth >= limits.max_depth {
return Err(
Diagnostic::new(
LimitExceeded,
"JSON equality nesting limit exceeded",
),
)
}
if a.length() > limits.max_nodes - reserved {
return Err(
Diagnostic::new(LimitExceeded, "JSON equality node limit exceeded"),
)
}
reserved += a.length()
for i in 0.. {
if a.length() != b.length() {
return Ok(false)
}
if depth >= limits.max_depth {
return Err(
Diagnostic::new(
LimitExceeded,
"JSON equality nesting limit exceeded",
),
)
}
if a.length() > limits.max_nodes - reserved {
return Err(
Diagnostic::new(LimitExceeded, "JSON equality node limit exceeded"),
)
}
reserved += a.length()
for (key, _) in a {
if !charge_text(key, "") {
return Err(
Diagnostic::new(
LimitExceeded,
"JSON equality key text limit exceeded",
),
)
}
}
for (key, _) in b {
if !charge_text("", key) {
return Err(
Diagnostic::new(
LimitExceeded,
"JSON equality key text limit exceeded",
),
)
}
}
let lookup = Map::from_array(b)
for (key, value) in a {
guard lookup.get(key) is Some(found) else { return Ok(false) }
stack.push((value, found, depth + 1))
}
}
_ => return Ok(false)
}
}
Ok(true)
}