// Copyright 2026 Noverberrain.
// SPDX-License-Identifier: Apache-2.0
///|
// Lexical preflight, not a JSON parser: bound nesting before core parsing.
// For VALID JSON each colon outside strings denotes one raw object member.
// Core JSON collapses duplicate keys, so compare this count with the parsed
// tree only AFTER parsing succeeds to reject duplicates (even escaped keys).
fn schema_member_tokens(text : StringView) -> Int32 raise SchemaError {
let mut in_string = false
let mut escaped = false
let mut members = 0
let mut depth = 0
for ch in text {
if in_string {
if escaped {
escaped = false
} else if ch == '\\' {
escaped = true
} else if ch == '"' {
in_string = false
}
} else if ch == '"' {
in_string = true
} else if ch == ':' {
members += 1
} else if ch == '{' || ch == '[' {
depth += 1
guard depth <= 16 else {
raise SchemaLimitExceeded(path="$", reason="JSON nesting exceeds 16")
}
} else if ch == '}' || ch == ']' {
depth -= 1
}
}
members
}
///|
// Preflight already bounded nesting depth to 16; core parsing succeeded.
fn schema_member_count(value : Json) -> Int32 {
match value {
Object(object) => {
let mut count = object.length()
for _, child in object {
count += schema_member_count(child)
}
count
}
Array(array) => {
let mut count = 0
for child in array {
count += schema_member_count(child)
}
count
}
_ => 0
}
}