-
Notifications
You must be signed in to change notification settings - Fork 73
/
Renderer.scala
101 lines (91 loc) · 2.85 KB
/
Renderer.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package jawn
package ast
import scala.annotation.switch
import scala.collection.mutable
import scala.util.Sorting
sealed trait Renderer {
final def render(jv: JValue): String = {
val sb = new StringBuilder
render(sb, 0, jv)
sb.toString
}
final def render(sb: StringBuilder, depth: Int, jv: JValue): Unit =
jv match {
case JNull => sb.append("null")
case JTrue => sb.append("true")
case JFalse => sb.append("false")
case LongNum(n) => sb.append(n.toString)
case DoubleNum(n) => sb.append(n.toString)
case DeferNum(s) => sb.append(s)
case DeferLong(s) => sb.append(s)
case JString(s) => renderString(sb, s)
case JArray(vs) => renderArray(sb, depth, vs)
case JObject(vs) => renderObject(sb, depth, canonicalizeObject(vs))
}
def canonicalizeObject(vs: mutable.Map[String, JValue]): Iterator[(String, JValue)]
def renderString(sb: StringBuilder, s: String): Unit
final def renderArray(sb: StringBuilder, depth: Int, vs: Array[JValue]): Unit = {
if (vs.isEmpty) return { sb.append("[]"); () }
sb.append("[")
render(sb, depth + 1, vs(0))
var i = 1
while (i < vs.length) {
sb.append(",")
render(sb, depth + 1, vs(i))
i += 1
}
sb.append("]")
}
final def renderObject(sb: StringBuilder, depth: Int, it: Iterator[(String, JValue)]): Unit = {
if (!it.hasNext) return { sb.append("{}"); () }
val (k0, v0) = it.next
sb.append("{")
renderString(sb, k0)
sb.append(":")
render(sb, depth + 1, v0)
while (it.hasNext) {
val (k, v) = it.next
sb.append(",")
renderString(sb, k)
sb.append(":")
render(sb, depth + 1, v)
}
sb.append("}")
}
final def escape(sb: StringBuilder, s: String, unicode: Boolean): Unit = {
sb.append('"')
var i = 0
val len = s.length
while (i < len) {
(s.charAt(i): @switch) match {
case '"' => sb.append("\\\"")
case '\\' => sb.append("\\\\")
case '\b' => sb.append("\\b")
case '\f' => sb.append("\\f")
case '\n' => sb.append("\\n")
case '\r' => sb.append("\\r")
case '\t' => sb.append("\\t")
case c =>
if (c < ' ' || (c > '~' && unicode)) sb.append("\\u%04x" format c.toInt)
else sb.append(c)
}
i += 1
}
sb.append('"')
}
}
object CanonicalRenderer extends Renderer {
def canonicalizeObject(vs: mutable.Map[String, JValue]): Iterator[(String, JValue)] = {
val keys = vs.keys.toArray
Sorting.quickSort(keys)
keys.iterator.map(k => (k, vs(k)))
}
def renderString(sb: StringBuilder, s: String): Unit =
escape(sb, s, true)
}
object FastRenderer extends Renderer {
def canonicalizeObject(vs: mutable.Map[String, JValue]): Iterator[(String, JValue)] =
vs.iterator
def renderString(sb: StringBuilder, s: String): Unit =
escape(sb, s, false)
}