Skip to content

Commit

Permalink
Implement rendering as a Pull
Browse files Browse the repository at this point in the history
This avoids many closure and tuple creations, that are costly.
  • Loading branch information
satabin committed Dec 10, 2024
1 parent 1701f28 commit 8358065
Show file tree
Hide file tree
Showing 5 changed files with 340 additions and 173 deletions.
41 changes: 40 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,46 @@ lazy val text = crossProject(JVMPlatform, JSPlatform, NativePlatform)
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.data.text.CharLikeStringChunks.pullNext"),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.data.text.CharLikeStringChunks.advance"),
ProblemFilters.exclude[IncompatibleMethTypeProblem]("fs2.data.text.CharLikeStringChunks.current"),
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.CharLikeStringChunks$StringContext")
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.CharLikeStringChunks$StringContext"),
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$AlignBegin"),
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$AlignBegin$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#AlignBegin.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#AlignBegin.unapply"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.data.text.render.internal.Annotated#AlignBegin.fromProduct"),
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$AlignEnd"),
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$AlignEnd$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#AlignEnd.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#AlignEnd.unapply"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.data.text.render.internal.Annotated#AlignEnd.fromProduct"),
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$GroupEnd"),
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$GroupEnd$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#GroupEnd.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#GroupEnd.unapply"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.data.text.render.internal.Annotated#GroupEnd.fromProduct"),
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$IndentBegin"),
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$IndentBegin$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#IndentBegin.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#IndentBegin.unapply"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.data.text.render.internal.Annotated#IndentBegin.fromProduct"),
ProblemFilters.exclude[MissingClassProblem]("fs2.data.text.render.internal.Annotated$IndentEnd"),
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$IndentEnd$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#IndentEnd.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#IndentEnd.unapply"),
ProblemFilters.exclude[IncompatibleResultTypeProblem](
"fs2.data.text.render.internal.Annotated#IndentEnd.fromProduct"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Line.hp"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#LineBreak.hp"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.hp"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.copy"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.copy$default$2"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.this"),
ProblemFilters.exclude[MissingTypesProblem]("fs2.data.text.render.internal.Annotated$Text$"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text.apply"),
ProblemFilters.exclude[DirectMissingMethodProblem]("fs2.data.text.render.internal.Annotated#Text._2")
)
)
.nativeSettings(
Expand Down
66 changes: 65 additions & 1 deletion json/src/main/scala/fs2/data/json/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,71 @@ package object json {
* You can use this to write the Json stream to a file.
*/
def compact[F[_]]: Pipe[F, Token, String] =
_.through(fs2.data.text.render.pretty(width = Int.MaxValue)(Token.compact))
_.scanChunks((0, false)) { case (state, chunk) =>
val builder = new StringBuilder
val state1 =
chunk.foldLeft(state) {
case ((level, comma), Token.StartObject) =>
if (comma) {
builder.append(',')
}
builder.append(('{'))
(level + 1, false)
case ((level, _), Token.EndObject) =>
builder.append('}')
(level - 1, level > 1)
case ((level, comma), Token.StartArray) =>
if (comma) {
builder.append(',')
}
builder.append(('['))
(level + 1, false)
case ((level, _), Token.EndArray) =>
builder.append(']')
(level - 1, level > 1)
case ((level, comma), Token.Key(key)) =>
if (comma) {
builder.append(',')
}
builder.append('"')
Token.renderString(key, 0, builder)
builder.append("\":")
(level, false)
case ((level, comma), Token.StringValue(key)) =>
if (comma) {
builder.append(',')
}
builder.append('"')
Token.renderString(key, 0, builder)
builder.append('"')
(level, level > 0)
case ((level, comma), Token.NumberValue(n)) =>
if (comma) {
builder.append(',')
}
builder.append(n)
(level, level > 0)
case ((level, comma), Token.TrueValue) =>
if (comma) {
builder.append(',')
}
builder.append("true")
(level, level > 0)
case ((level, comma), Token.FalseValue) =>
if (comma) {
builder.append(',')
}
builder.append("false")
(level, level > 0)
case ((level, comma), Token.NullValue) =>
if (comma) {
builder.append(',')
}
builder.append("null")
(level, level > 0)
}
(state1, Chunk.singleton(builder.result()))
}

/** Renders a pretty-printed representation of the token stream with the given
* indentation size.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ package fs2.data.text.render.internal

private sealed trait Annotated
private object Annotated {
case class Text(text: String, hp: Int) extends Annotated
case class Line(hp: Int) extends Annotated
case class LineBreak(hp: Int) extends Annotated
case class Text(text: String) extends Annotated
case class Line(pos: Int) extends Annotated
case class LineBreak(pos: Int) extends Annotated
case class GroupBegin(hpl: Position) extends Annotated
case class GroupEnd(hp: Int) extends Annotated
case class IndentBegin(hp: Int) extends Annotated
case class IndentEnd(hp: Int) extends Annotated
case class AlignBegin(hp: Int) extends Annotated
case class AlignEnd(hp: Int) extends Annotated
case object GroupEnd extends Annotated
case object IndentBegin extends Annotated
case object IndentEnd extends Annotated
case object AlignBegin extends Annotated
case object AlignEnd extends Annotated
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2024 fs2-data Project
*
* 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.
*/

package fs2.data.text.render.internal

private sealed trait NonEmptyIntList {
def head: Int
def ::(i: Int): NonEmptyIntList =
More(i, this)
def incHead: NonEmptyIntList
def decHead: NonEmptyIntList
def pop: NonEmptyIntList
}
private final case class One(head: Int) extends NonEmptyIntList {
override def incHead: NonEmptyIntList = One(head + 1)
override def decHead: NonEmptyIntList = One(head - 1)
override lazy val pop: NonEmptyIntList = One(0)
}
private final case class More(head: Int, tail: NonEmptyIntList) extends NonEmptyIntList {
override def incHead: NonEmptyIntList = More(head + 1, tail)
override def decHead: NonEmptyIntList = More(head - 1, tail)
override def pop: NonEmptyIntList = tail
}
Loading

0 comments on commit 8358065

Please sign in to comment.