diff --git a/json/json.mbt b/json/json.mbt index 75e843f9e..c1ebc4227 100644 --- a/json/json.mbt +++ b/json/json.mbt @@ -86,24 +86,17 @@ pub fn Json::value(self : Json, key : String) -> Json? { } ///| -fn indent_str(level : Int, indent : Int) -> String { - if indent == 0 { - "" - } else { - let spaces = indent * level - match spaces { - 0 => "\n" - 1 => "\n " - 2 => "\n " - 3 => "\n " - 4 => "\n " - 5 => "\n " - 6 => "\n " - 7 => "\n " - 8 => "\n " - _ => "\n" + " ".repeat(spaces) - } +#inline +fn write_indent( + buf : StringBuilder, + cache : Array[String], + level : Int, + indent : Int, +) -> Unit { + while cache.length() <= level { + cache.push("\n" + " ".repeat(indent * cache.length())) } + buf.write_string(cache[level]) } ///| @@ -280,6 +273,7 @@ pub fn Json::stringify( replacer? : Replacer, ) -> String { let buf = StringBuilder(size_hint=0) + let indent_cache : Array[String] = [] // Explicit stack to replace recursive calls let stack : Array[WriteFrame] = [] @@ -294,7 +288,9 @@ pub fn Json::stringify( } else { depth += 1 buf.write_char('{') - buf.write_string(indent_str(depth, indent)) + if indent > 0 { + write_indent(buf, indent_cache, depth, indent) + } // After child value printed, we resume from this frame stack.push(Object(members.iter(), first=true)) } @@ -304,7 +300,9 @@ pub fn Json::stringify( } else { depth += 1 buf.write_char('[') - buf.write_string(indent_str(depth, indent)) + if indent > 0 { + write_indent(buf, indent_cache, depth, indent) + } stack.push(Array(arr, i=0)) } String(s) => { @@ -333,13 +331,17 @@ pub fn Json::stringify( frame.i = i + 1 if i > 0 { buf.write_char(',') - buf.write_string(indent_str(depth, indent)) + if indent > 0 { + write_indent(buf, indent_cache, depth, indent) + } } continue Some(element) } else { depth -= 1 ignore(stack.pop()) - buf.write_string(indent_str(depth, indent)) + if indent > 0 { + write_indent(buf, indent_cache, depth, indent) + } buf.write_char(']') continue None } @@ -356,7 +358,9 @@ pub fn Json::stringify( } if !first { buf.write_char(',') - buf.write_string(indent_str(depth, indent)) + if indent > 0 { + write_indent(buf, indent_cache, depth, indent) + } } buf.write_char('\"') buf.write_string(escape(k, escape_slash~)) @@ -371,7 +375,9 @@ pub fn Json::stringify( None => { depth -= 1 ignore(stack.pop()) - buf.write_string(indent_str(depth, indent)) + if indent > 0 { + write_indent(buf, indent_cache, depth, indent) + } buf.write_char('}') continue None } diff --git a/json/stringify_indent_bench_test.mbt b/json/stringify_indent_bench_test.mbt new file mode 100644 index 000000000..7761c51cd --- /dev/null +++ b/json/stringify_indent_bench_test.mbt @@ -0,0 +1,28 @@ +// Copyright 2026 International Digital Economy Academy +// +// 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. + +///| +fn make_deep_wide_json() -> Json { + let mut value = Array::makei(10000, i => Json::number(i.to_double())).to_json() + for _ in 0..<6 { + value = Json::array([value]) + } + value +} + +///| +test "bench Json::stringify deep wide indent=2 n=10000" (it : @bench.T) { + let json = make_deep_wide_json() + it.bench(fn() { it.keep(json.stringify(indent=2).length()) }) +}