Skip to content

Commit 8358065

Browse files
committed
Implement rendering as a Pull
This avoids many closure and tuple creations, that are costly.
1 parent 1701f28 commit 8358065

File tree

5 files changed

+340
-173
lines changed

5 files changed

+340
-173
lines changed

build.sbt

+40-1
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,46 @@ lazy val text = crossProject(JVMPlatform, JSPlatform, NativePlatform)
143143
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.data.text.CharLikeStringChunks.pullNext"),
144144
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.data.text.CharLikeStringChunks.advance"),
145145
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.data.text.CharLikeStringChunks.current"),
146-
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.CharLikeStringChunks$StringContext")
146+
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.CharLikeStringChunks$StringContext"),
147+
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$AlignBegin"),
148+
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$AlignBegin$"),
149+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#AlignBegin.apply"),
150+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#AlignBegin.unapply"),
151+
ProblemFilters.exclude[IncompatibleResultTypeProblem](
152+
"fs2.data.text.render.internal.Annotated#AlignBegin.fromProduct"),
153+
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$AlignEnd"),
154+
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$AlignEnd$"),
155+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#AlignEnd.apply"),
156+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#AlignEnd.unapply"),
157+
ProblemFilters.exclude[IncompatibleResultTypeProblem](
158+
"fs2.data.text.render.internal.Annotated#AlignEnd.fromProduct"),
159+
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$GroupEnd"),
160+
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$GroupEnd$"),
161+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#GroupEnd.apply"),
162+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#GroupEnd.unapply"),
163+
ProblemFilters.exclude[IncompatibleResultTypeProblem](
164+
"fs2.data.text.render.internal.Annotated#GroupEnd.fromProduct"),
165+
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$IndentBegin"),
166+
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$IndentBegin$"),
167+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#IndentBegin.apply"),
168+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#IndentBegin.unapply"),
169+
ProblemFilters.exclude[IncompatibleResultTypeProblem](
170+
"fs2.data.text.render.internal.Annotated#IndentBegin.fromProduct"),
171+
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$IndentEnd"),
172+
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$IndentEnd$"),
173+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#IndentEnd.apply"),
174+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#IndentEnd.unapply"),
175+
ProblemFilters.exclude[IncompatibleResultTypeProblem](
176+
"fs2.data.text.render.internal.Annotated#IndentEnd.fromProduct"),
177+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Line.hp"),
178+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#LineBreak.hp"),
179+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.hp"),
180+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.copy"),
181+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.copy$default$2"),
182+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.this"),
183+
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$Text$"),
184+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.apply"),
185+
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text._2")
147186
)
148187
)
149188
.nativeSettings(

json/src/main/scala/fs2/data/json/package.scala

+65-1
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,71 @@ package object json {
154154
* You can use this to write the Json stream to a file.
155155
*/
156156
def compact[F[_]]: Pipe[F, Token, String] =
157-
_.through(fs2.data.text.render.pretty(width = Int.MaxValue)(Token.compact))
157+
_.scanChunks((0, false)) { case (state, chunk) =>
158+
val builder = new StringBuilder
159+
val state1 =
160+
chunk.foldLeft(state) {
161+
case ((level, comma), Token.StartObject) =>
162+
if (comma) {
163+
builder.append(',')
164+
}
165+
builder.append(('{'))
166+
(level + 1, false)
167+
case ((level, _), Token.EndObject) =>
168+
builder.append('}')
169+
(level - 1, level > 1)
170+
case ((level, comma), Token.StartArray) =>
171+
if (comma) {
172+
builder.append(',')
173+
}
174+
builder.append(('['))
175+
(level + 1, false)
176+
case ((level, _), Token.EndArray) =>
177+
builder.append(']')
178+
(level - 1, level > 1)
179+
case ((level, comma), Token.Key(key)) =>
180+
if (comma) {
181+
builder.append(',')
182+
}
183+
builder.append('"')
184+
Token.renderString(key, 0, builder)
185+
builder.append("\":")
186+
(level, false)
187+
case ((level, comma), Token.StringValue(key)) =>
188+
if (comma) {
189+
builder.append(',')
190+
}
191+
builder.append('"')
192+
Token.renderString(key, 0, builder)
193+
builder.append('"')
194+
(level, level > 0)
195+
case ((level, comma), Token.NumberValue(n)) =>
196+
if (comma) {
197+
builder.append(',')
198+
}
199+
builder.append(n)
200+
(level, level > 0)
201+
case ((level, comma), Token.TrueValue) =>
202+
if (comma) {
203+
builder.append(',')
204+
}
205+
builder.append("true")
206+
(level, level > 0)
207+
case ((level, comma), Token.FalseValue) =>
208+
if (comma) {
209+
builder.append(',')
210+
}
211+
builder.append("false")
212+
(level, level > 0)
213+
case ((level, comma), Token.NullValue) =>
214+
if (comma) {
215+
builder.append(',')
216+
}
217+
builder.append("null")
218+
(level, level > 0)
219+
}
220+
(state1, Chunk.singleton(builder.result()))
221+
}
158222

159223
/** Renders a pretty-printed representation of the token stream with the given
160224
* indentation size.

text/shared/src/main/scala/fs2/data/text/render/internal/Annotated.scala

+8-8
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ package fs2.data.text.render.internal
1818

1919
private sealed trait Annotated
2020
private object Annotated {
21-
case class Text(text: String, hp: Int) extends Annotated
22-
case class Line(hp: Int) extends Annotated
23-
case class LineBreak(hp: Int) extends Annotated
21+
case class Text(text: String) extends Annotated
22+
case class Line(pos: Int) extends Annotated
23+
case class LineBreak(pos: Int) extends Annotated
2424
case class GroupBegin(hpl: Position) extends Annotated
25-
case class GroupEnd(hp: Int) extends Annotated
26-
case class IndentBegin(hp: Int) extends Annotated
27-
case class IndentEnd(hp: Int) extends Annotated
28-
case class AlignBegin(hp: Int) extends Annotated
29-
case class AlignEnd(hp: Int) extends Annotated
25+
case object GroupEnd extends Annotated
26+
case object IndentBegin extends Annotated
27+
case object IndentEnd extends Annotated
28+
case object AlignBegin extends Annotated
29+
case object AlignEnd extends Annotated
3030
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright 2024 fs2-data Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package fs2.data.text.render.internal
18+
19+
private sealed trait NonEmptyIntList {
20+
def head: Int
21+
def ::(i: Int): NonEmptyIntList =
22+
More(i, this)
23+
def incHead: NonEmptyIntList
24+
def decHead: NonEmptyIntList
25+
def pop: NonEmptyIntList
26+
}
27+
private final case class One(head: Int) extends NonEmptyIntList {
28+
override def incHead: NonEmptyIntList = One(head + 1)
29+
override def decHead: NonEmptyIntList = One(head - 1)
30+
override lazy val pop: NonEmptyIntList = One(0)
31+
}
32+
private final case class More(head: Int, tail: NonEmptyIntList) extends NonEmptyIntList {
33+
override def incHead: NonEmptyIntList = More(head + 1, tail)
34+
override def decHead: NonEmptyIntList = More(head - 1, tail)
35+
override def pop: NonEmptyIntList = tail
36+
}

0 commit comments

Comments
 (0)