Skip to content
Closed
Show file tree
Hide file tree
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
39 changes: 18 additions & 21 deletions sjsonnet/src/sjsonnet/BaseByteRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -319,9 +319,8 @@ class BaseByteRenderer[T <: java.io.OutputStream](
arr(pos + 1 + bLen) = '"'.toByte
elemBuilder.length = pos + bLen + 2
} else {
val escapedLen = escapedStringLength(bytes, bLen, firstEscape)
elemBuilder.ensureLength(escapedLen)
val arr = elemBuilder.arr
elemBuilder.ensureLength(bLen + 2 + (bLen >>> 5))
var arr = elemBuilder.arr
var outPos = elemBuilder.length
arr(outPos) = '"'.toByte
outPos += 1
Expand All @@ -330,41 +329,39 @@ class BaseByteRenderer[T <: java.io.OutputStream](
while (escPos >= 0) {
if (escPos > from) {
val chunkLen = escPos - from
elemBuilder.length = outPos
elemBuilder.ensureLength(chunkLen + 6)
arr = elemBuilder.arr
outPos = elemBuilder.length
System.arraycopy(bytes, from, arr, outPos, chunkLen)
outPos += chunkLen
}
elemBuilder.length = outPos
elemBuilder.ensureLength(6)
arr = elemBuilder.arr
outPos = elemBuilder.length
outPos = escapeByteInline(bytes(escPos) & 0xff, arr, outPos)
from = escPos + 1
escPos = if (from < bLen) CharSWAR.findFirstEscapeChar(bytes, from, bLen) else -1
}
if (from < bLen) {
val tailLen = bLen - from
elemBuilder.length = outPos
elemBuilder.ensureLength(tailLen + 1)
arr = elemBuilder.arr
outPos = elemBuilder.length
System.arraycopy(bytes, from, arr, outPos, tailLen)
outPos += tailLen
}
elemBuilder.length = outPos
elemBuilder.ensureLength(1)
arr = elemBuilder.arr
outPos = elemBuilder.length
arr(outPos) = '"'.toByte
elemBuilder.length = outPos + 1
}
}

private def escapedStringLength(bytes: Array[Byte], bLen: Int, firstEscape: Int): Int = {
var len = bLen + 2
var from = firstEscape
var escPos = firstEscape
while (escPos >= 0) {
len += escapeExtraLength(bytes(escPos) & 0xff)
from = escPos + 1
escPos = if (from < bLen) CharSWAR.findFirstEscapeChar(bytes, from, bLen) else -1
}
len
}

@inline private def escapeExtraLength(b: Int): Int =
(b: @scala.annotation.switch) match {
case '"' | '\\' | '\b' | '\f' | '\n' | '\r' | '\t' => 1
case _ => 5
}

/** Inline JSON escape for one byte that is known to require escaping. */
@inline private def escapeByteInline(b: Int, arr: Array[Byte], outPos0: Int): Int = {
val outPos = outPos0
Expand Down
8 changes: 8 additions & 0 deletions sjsonnet/test/src/sjsonnet/RendererTests.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package sjsonnet

import java.io.ByteArrayOutputStream
import utest._

object RendererTests extends TestSuite {
Expand Down Expand Up @@ -65,6 +66,13 @@ object RendererTests extends TestSuite {
ujson.transform(ujson.Num(1e15), new Renderer()).toString ==> "1000000000000000"
}

test("byteRendererLongEscapedString") {
val s = ("abc\n\"\\\t\u0001" * 40) + "tail"
val out = new ByteArrayOutputStream
ujson.transform(ujson.Str(s), new ByteRenderer(out))
out.toString("UTF-8") ==> ujson.transform(ujson.Str(s), new Renderer()).toString
}

test("indentZero") {
// indent=0 should produce newlines but no spaces
ujson.transform(ujson.Arr(1, 2), new Renderer(indent = 0)).toString ==>
Expand Down
Loading