///|
priv enum JsonAction { Value(Json,Int); Text(String) }
///|
/// Stable, bounded JSON spelling for typed metadata. The explicit stack also
/// handles caller-constructed values without recursive host-stack traversal.
pub fn canonical_json(value : Json, depth_limit? : Int = 256, work_limit? : Int = 1000000, output_limit? : Int = 16777216) -> String? {
let stack=[JsonAction::Value(value,0)]; let out=StringBuilder(); let mut work=0; let mut output=0
while stack.pop() is Some(action) {
match action {
Text(text) => {
if text.length() > output_limit-output || text.length() > work_limit-work { return None }
output+=text.length(); work+=text.length(); out.write_string(text)
}
Value(value,depth) => {
if depth > depth_limit || work >= work_limit { return None }; work+=1
match value {
Array(values) => {
if values.length() > work_limit-work { return None }
stack.push(Text("]"))
for i=values.length()-1; i>=0; i=i-1 {
stack.push(Value(values[i],depth+1))
if i != 0 { stack.push(Text(",")) }
}
stack.push(Text("["))
}
Object(fields) => {
if fields.length() > work_limit-work { return None }
let keys=fields.keys().to_array(); keys.sort_by((a,b) => a.lexical_compare(b)); stack.push(Text("}"))
for i=keys.length()-1; i>=0; i=i-1 {
let key=keys[i]
if key.length() > work_limit-work { return None }
stack.push(Value(fields.get(key).unwrap(),depth+1)); stack.push(Text(":")); stack.push(Text(Json(key).stringify()))
if i != 0 { stack.push(Text(",")) }
}
stack.push(Text("{"))
}
String(text) => {
if text.length() > work_limit-work { return None }
stack.push(Text(value.stringify()))
}
_ => stack.push(Text(value.stringify()))
}
}
}
}
Some(out.to_string())
}