Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 38 additions & 6 deletions sjsonnet/src/sjsonnet/Renderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import java.io.{StringWriter, Writer}

import upickle.core.{ArrVisitor, ObjVisitor}

object Renderer {

/**
* Maximum nesting depth for pre-computed indent arrays. Depths beyond this fall back to
* per-character rendering. 16 covers the vast majority of real-world Jsonnet output.
*/
final val MaxCachedDepth = 16
}

/**
* Custom JSON renderer to try and match the behavior of google/jsonnet's render:
*
Expand All @@ -12,6 +21,7 @@ import upickle.core.{ArrVisitor, ObjVisitor}
*/
class Renderer(out: Writer = new java.io.StringWriter(), indent: Int = -1)
extends BaseCharRenderer(out, indent) {
import Renderer.MaxCachedDepth
var newlineBuffered = false
override def visitFloat64(d: Double, index: Int): Writer = {
val s = RenderUtils.renderDouble(d)
Expand All @@ -20,19 +30,41 @@ class Renderer(out: Writer = new java.io.StringWriter(), indent: Int = -1)
flushCharBuilder()
out
}

// Pre-computed indent arrays: indentCache(d) = '\n' + indent*d spaces
private val indentCache: Array[Array[Char]] =
if (indent <= 0) null
else {
val arr = new Array[Array[Char]](MaxCachedDepth)
var d = 0
while (d < MaxCachedDepth) {
val spaces = indent * d
val buf = Array.fill(spaces + 1)(' ')
buf(0) = '\n'
arr(d) = buf
d += 1
}
arr
}

override def flushBuffer(): Unit = {
if (commaBuffered) {
elemBuilder.append(',')
if (indent == -1) elemBuilder.append(' ')
}
if (indent == -1) ()
else if (commaBuffered || newlineBuffered) {
var i = indent * depth
elemBuilder.ensureLength(i + 1)
elemBuilder.append('\n')
while (i > 0) {
elemBuilder.append(' ')
i -= 1
if (indentCache != null && depth < MaxCachedDepth) {
val cached = indentCache(depth)
elemBuilder.appendAll(cached, cached.length)
} else {
var i = indent * depth
elemBuilder.ensureLength(i + 1)
elemBuilder.append('\n')
while (i > 0) {
elemBuilder.append(' ')
i -= 1
}
}
}
newlineBuffered = false
Expand Down