diff --git a/.sbtopts b/.sbtopts
new file mode 100644
index 00000000..29810bec
--- /dev/null
+++ b/.sbtopts
@@ -0,0 +1 @@
+-Dsbt.io.implicit.relative.glob.conversion=allow
\ No newline at end of file
diff --git a/build.sbt b/build.sbt
index 004c1922..e4c6bd9d 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,4 +1,3 @@
-lazy val scala212 = "2.12.20"
lazy val scala213 = "2.13.16"
lazy val scala3 = "3.3.5"
@@ -61,7 +60,7 @@ def crossScalaSettings = {
}
Seq(
- crossScalaVersions := Seq(scala212, scala213, scala3),
+ crossScalaVersions := Seq(scala213, scala3),
Compile / unmanagedSourceDirectories ++= addDirsByScalaVersion(
"src/main"
).value,
@@ -225,7 +224,7 @@ lazy val `jackson-212` = project
"com.fasterxml.jackson.core" % "jackson-core" % "2.12.7"
)
)
- .dependsOn(core)
+ .dependsOn(core % "compile->compile;test->test")
lazy val `jackson-213` = project
.in(jackson / "jackson-213")
@@ -239,7 +238,7 @@ lazy val `jackson-213` = project
"com.fasterxml.jackson.core" % "jackson-core" % "2.13.5"
)
)
- .dependsOn(core)
+ .dependsOn(core % "compile->compile;test->test")
lazy val `jackson-214` = project
.in(jackson / "jackson-214")
@@ -253,7 +252,7 @@ lazy val `jackson-214` = project
"com.fasterxml.jackson.core" % "jackson-core" % "2.14.3"
)
)
- .dependsOn(core)
+ .dependsOn(core % "compile->compile;test->test")
lazy val `jackson-215` = project
.in(jackson / "jackson-215")
@@ -267,7 +266,7 @@ lazy val `jackson-215` = project
"com.fasterxml.jackson.core" % "jackson-core" % "2.15.4"
)
)
- .dependsOn(core)
+ .dependsOn(core % "compile->compile;test->test")
lazy val `jackson-216` = project
.in(jackson / "jackson-216")
@@ -281,7 +280,7 @@ lazy val `jackson-216` = project
"com.fasterxml.jackson.core" % "jackson-core" % "2.16.2"
)
)
- .dependsOn(core)
+ .dependsOn(core % "compile->compile;test->test")
lazy val `jackson-217` = project
.in(jackson / "jackson-217")
@@ -295,7 +294,7 @@ lazy val `jackson-217` = project
"com.fasterxml.jackson.core" % "jackson-core" % "2.17.3"
)
)
- .dependsOn(core)
+ .dependsOn(core % "compile->compile;test->test")
lazy val `jackson-218` = project
.in(jackson / "jackson-218")
@@ -309,7 +308,7 @@ lazy val `jackson-218` = project
"com.fasterxml.jackson.core" % "jackson-core" % "2.18.3"
)
)
- .dependsOn(core)
+ .dependsOn(core % "compile->compile;test->test")
lazy val benchmarks = project
.in(modules / "benchmarks")
@@ -327,6 +326,8 @@ lazy val benchmarks = project
"io.circe" %% "circe-jackson210" % "0.14.0",
"dev.zio" %% "zio-json" % "0.7.1",
"com.typesafe.play" %% "play-json" % "2.10.5",
+ "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-core" % "2.33.1",
+ "com.github.plokhotnyuk.jsoniter-scala" %% "jsoniter-scala-macros" % "2.33.1",
"org.knowm.xchart" % "xchart" % "3.8.2" exclude ("de.erichseifert.vectorgraphics2d", "VectorGraphics2D") withSources ()
),
scalacOptions ++= {
diff --git a/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenIterator.scala b/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenIterator.scala
index 3bbb60fa..a4075c3b 100644
--- a/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenIterator.scala
+++ b/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenIterator.scala
@@ -10,6 +10,7 @@ import scala.annotation.switch
final class JacksonTokenIterator(jsonParser: JsonParser)
extends BaseTokenIterator {
private[this] var token: Token = fromId(jsonParser.currentTokenId())
+
override def currentToken(): Token = token
override def nextToken(): Token = {
diff --git a/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenWriter.scala b/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenWriter.scala
index 53497f7f..681a966a 100644
--- a/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenWriter.scala
+++ b/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/JacksonTokenWriter.scala
@@ -92,4 +92,7 @@ class JacksonTokenWriter(jsonGenerator: JsonGenerator) extends TokenWriter {
override def close(): Unit = jsonGenerator.close()
override def flush(): Unit = jsonGenerator.flush()
+
+ override def result(): String =
+ jsonGenerator.getOutputTarget.toString
}
diff --git a/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/package.scala b/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/package.scala
index 78720394..58d7c831 100644
--- a/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/package.scala
+++ b/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/package.scala
@@ -1,36 +1,81 @@
package tethys
-import java.io.{Reader, Writer}
-
-import com.fasterxml.jackson.core.JsonFactory
+import java.io.Reader
+import com.fasterxml.jackson.core.{
+ JsonFactory,
+ JsonFactoryBuilder,
+ JsonGenerator
+}
import tethys.readers.{FieldName, ReaderError}
import tethys.readers.tokens.{TokenIterator, TokenIteratorProducer}
-import tethys.writers.tokens.{TokenWriter, TokenWriterProducer}
+import tethys.writers.tokens.{
+ TokenWriter,
+ TokenWriterConfig,
+ TokenWriterProducer
+}
package object jackson {
- lazy val defaultJsonFactory: JsonFactory = {
- val f = new JsonFactory()
- f.configure(JsonFactory.Feature.INTERN_FIELD_NAMES, false)
- f
+ lazy val defaultJsonFactory: JsonFactory =
+ new JsonFactoryBuilder()
+ .configure(JsonFactory.Feature.INTERN_FIELD_NAMES, false)
+ .build()
+
+ class JacksonTokenWriterProducer(
+ jsonFactory: JsonFactory,
+ // used for compatibility where tethys.jackson.pretty import is required
+ modifyConfig: TokenWriterConfig => TokenWriterConfig
+ ) extends TokenWriterProducer {
+
+ private def updateGeneratorFromConfig(
+ generator: JsonGenerator,
+ config: TokenWriterConfig
+ ): JsonGenerator = {
+ if (config == TokenWriterConfig.default)
+ generator
+ else {
+ val first =
+ if (config.indentionStep == 2)
+ generator.useDefaultPrettyPrinter()
+ else
+ generator
+
+ val second =
+ if (config.escapeUnicode)
+ first.setHighestNonEscapedChar(127)
+ else
+ first
+ second
+ }
+ }
+
+ override def produce(config: TokenWriterConfig): TokenWriter = {
+ val generator = updateGeneratorFromConfig(
+ jsonFactory.createGenerator(new java.io.StringWriter()),
+ modifyConfig(config)
+ )
+ new JacksonTokenWriter(generator)
+ }
}
implicit def jacksonTokenWriterProducer(implicit
jsonFactory: JsonFactory = defaultJsonFactory
- ): TokenWriterProducer = new TokenWriterProducer {
- override def forWriter(writer: Writer): TokenWriter = {
- new JacksonTokenWriter(jsonFactory.createGenerator(writer))
- }
- }
+ ): JacksonTokenWriterProducer =
+ new JacksonTokenWriterProducer(jsonFactory, identity)
implicit def jacksonTokenIteratorProducer(implicit
jsonFactory: JsonFactory = defaultJsonFactory
): TokenIteratorProducer = new TokenIteratorProducer {
+ override def produce(json: String): Either[ReaderError, TokenIterator] =
+ ReaderError.catchNonFatal(
+ JacksonTokenIterator.fromFreshParser(jsonFactory.createParser(json))
+ )(FieldName.Root)
+
override def fromReader(
reader: Reader
): Either[ReaderError, TokenIterator] = {
ReaderError.catchNonFatal(
JacksonTokenIterator.fromFreshParser(jsonFactory.createParser(reader))
- )(FieldName())
+ )(FieldName.Root)
}
}
}
diff --git a/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/pretty/package.scala b/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/pretty/package.scala
index 6bb86bd3..7ce322ae 100644
--- a/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/pretty/package.scala
+++ b/modules/backend/jackson/jackson-backend/src/main/scala/tethys/jackson/pretty/package.scala
@@ -1,21 +1,18 @@
package tethys.jackson
-import java.io.Writer
-
import com.fasterxml.jackson.core.JsonFactory
import tethys.readers.tokens.TokenIteratorProducer
-import tethys.writers.tokens.{TokenWriter, TokenWriterProducer}
package object pretty {
+
+ @deprecated("Provide implicit TokenWriterConfig to `asJson` instead")
implicit def prettyJacksonTokenWriterProducer(implicit
jsonFactory: JsonFactory = defaultJsonFactory
- ): TokenWriterProducer = new TokenWriterProducer {
- override def forWriter(writer: Writer): TokenWriter = {
- new JacksonTokenWriter(
- jsonFactory.createGenerator(writer).useDefaultPrettyPrinter()
- )
- }
- }
+ ): JacksonTokenWriterProducer =
+ new tethys.jackson.JacksonTokenWriterProducer(
+ jsonFactory,
+ modifyConfig = _.withDefaultPrettyPrinter
+ )
implicit def jacksonTokenIteratorProducer(implicit
jsonFactory: JsonFactory = defaultJsonFactory
diff --git a/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonJsonReaderSpec.scala b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonJsonReaderSpec.scala
new file mode 100644
index 00000000..4cedca23
--- /dev/null
+++ b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonJsonReaderSpec.scala
@@ -0,0 +1,8 @@
+package tethys.jackson
+
+import tethys.JsonReaderSpec
+import tethys.readers.tokens.TokenIteratorProducer
+
+class JacksonJsonReaderSpec extends JsonReaderSpec {
+ def producer: TokenIteratorProducer = jacksonTokenIteratorProducer
+}
diff --git a/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonJsonWriterSpec.scala b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonJsonWriterSpec.scala
new file mode 100644
index 00000000..7b8365ca
--- /dev/null
+++ b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonJsonWriterSpec.scala
@@ -0,0 +1,169 @@
+package tethys.jackson
+
+import tethys.writers.tokens.{
+ TokenWriterProducer,
+ TokenWriterConfig,
+ JsonWriterSpec
+}
+import tethys._
+
+class JacksonJsonWriterSpec extends JsonWriterSpec {
+ {
+ import tethys.jackson.pretty._
+ import tethys.writers.tokens.TestModels._
+
+ configurableTestCase(
+ description = "pretty print complex structures",
+ config = TokenWriterConfig.default.withDefaultPrettyPrinter,
+ value = Department(
+ "Global Engineering",
+ List(
+ Employee(
+ 1L,
+ Person("John Doe", 30, Some("john@example.com")),
+ Address("123 Main St", "New York", "USA", Some("10001")),
+ "Backend",
+ BigDecimal("100000.00")
+ ),
+ Employee(
+ 2L,
+ Person("Jane Smith", 28, None),
+ Address("456 Park Ave", "Boston", "USA", None),
+ "Frontend",
+ BigDecimal("95000.50")
+ )
+ ),
+ Set("global", "engineering", "tech"),
+ Map(
+ "headquarters" -> "New York",
+ "founded" -> "2020",
+ "status" -> "active"
+ )
+ ),
+ json = """{
+ "name" : "Global Engineering",
+ "employees" : [ {
+ "id" : 1,
+ "person" : {
+ "name" : "John Doe",
+ "age" : 30,
+ "email" : "john@example.com"
+ },
+ "address" : {
+ "street" : "123 Main St",
+ "city" : "New York",
+ "country" : "USA",
+ "postalCode" : "10001"
+ },
+ "department" : "Backend",
+ "salary" : 100000.00
+ }, {
+ "id" : 2,
+ "person" : {
+ "name" : "Jane Smith",
+ "age" : 28
+ },
+ "address" : {
+ "street" : "456 Park Ave",
+ "city" : "Boston",
+ "country" : "USA"
+ },
+ "department" : "Frontend",
+ "salary" : 95000.50
+ } ],
+ "tags" : [ "global", "engineering", "tech" ],
+ "metadata" : {
+ "headquarters" : "New York",
+ "founded" : "2020",
+ "status" : "active"
+ }
+}"""
+
+ )
+
+ configurableTestCase(
+ description = "escape unicode",
+ config = TokenWriterConfig.default.withEscapeUnicode(true),
+ value = "Unicode: \u00a9 \u20ac \u2603",
+ json = "\"Unicode: \\u00A9 \\u20AC \\u2603\""
+ )
+
+ configurableTestCase(
+ description = "escape unicode emoji",
+ config = TokenWriterConfig.default.withEscapeUnicode(true),
+ value = "Emoji: 😀 🚀 🌍",
+ json = "\"Emoji: \\uD83D\\uDE00 \\uD83D\\uDE80 \\uD83C\\uDF0D\""
+ )
+
+ configurableTestCase(
+ description = "unicode characters in object keys",
+ config = TokenWriterConfig.default.withEscapeUnicode(true),
+ value = Map("café" -> "coffee", "résumé" -> "CV", "über" -> "super"),
+ json = "{\"caf\\u00E9\":\"coffee\",\"r\\u00E9sum\\u00E9\":\"CV\",\"\\u00FCber\":\"super\"}"
+ )
+
+ case class MenuItem(name: String, price: Double)
+ case class Menu(items: List[MenuItem])
+ case class Restaurant(menu: Menu)
+
+ implicit val menuItemWriter: JsonWriter[MenuItem] = JsonWriter.obj[MenuItem]
+ .addField("name")(_.name)
+ .addField("price")(_.price)
+
+ implicit val menuWriter: JsonWriter[Menu] = JsonWriter.obj[Menu]
+ .addField("items")(_.items)
+
+ implicit val restaurantWriter: JsonWriter[Restaurant] = JsonWriter.obj[Restaurant]
+ .addField("menu")(_.menu)
+
+ configurableTestCase(
+ description = "unicode characters in nested objects",
+ config = TokenWriterConfig.default.withEscapeUnicode(true),
+ value = Restaurant(Menu(List(
+ MenuItem("Café au lait", 3.50),
+ MenuItem("Crème brûlée", 5.75)
+ ))),
+ json = "{\"menu\":{\"items\":[{\"name\":\"Caf\\u00E9 au lait\",\"price\":3.5},{\"name\":\"Cr\\u00E8me br\\u00FBl\\u00E9e\",\"price\":5.75}]}}"
+ )
+
+ configurableTestCase(
+ description = "mixed ASCII and unicode in arrays",
+ config = TokenWriterConfig.default.withEscapeUnicode(true),
+ value = List("hello", "世界", "こんにちは", "안녕하세요"),
+ json = "[\"hello\",\"\\u4E16\\u754C\",\"\\u3053\\u3093\\u306B\\u3061\\u306F\",\"\\uC548\\uB155\\uD558\\uC138\\uC694\"]"
+ )
+
+ configurableTestCase(
+ description = "control characters that should always be escaped",
+ config = TokenWriterConfig.default.withEscapeUnicode(false),
+ value = "Control chars: \u0000\u0001\u0008\u0009\u000A\u000C\u000D\u001F",
+ json = "\"Control chars: \\u0000\\u0001\\b\\t\\n\\f\\r\\u001F\""
+ )
+
+ configurableTestCase(
+ description = "unicode characters with escapeUnicode set to false",
+ config = TokenWriterConfig.default.withEscapeUnicode(false),
+ value = "Unicode: \u00a9 \u20ac \u2603",
+ json = "\"Unicode: © € ☃\""
+ )
+
+ case class SurrogatePairsTest(text: String, list: List[String], keyValue: (String, String))
+
+ implicit val surrogatePairsTestWriter: JsonWriter[SurrogatePairsTest] = JsonWriter.obj[SurrogatePairsTest]
+ .addField("text")(_.text)
+ .addField("list")(_.list)
+ .addField("nested")(test => Map(test.keyValue))
+
+ configurableTestCase(
+ description = "surrogate pairs in different contexts",
+ config = TokenWriterConfig.default.withEscapeUnicode(true),
+ value = SurrogatePairsTest(
+ "Surrogate pairs: 𝄞 𝌆 𝓐",
+ List("𝄞", "𝌆", "𝓐"),
+ ("key𝄞", "value𝌆")
+ ),
+ json = "{\"text\":\"Surrogate pairs: \\uD834\\uDD1E \\uD834\\uDF06 \\uD835\\uDCD0\",\"list\":[\"\\uD834\\uDD1E\",\"\\uD834\\uDF06\",\"\\uD835\\uDCD0\"],\"nested\":{\"key\\uD834\\uDD1E\":\"value\\uD834\\uDF06\"}}"
+ )
+ }
+
+}
diff --git a/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenIteratorSpec.scala b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenIteratorSpec.scala
new file mode 100644
index 00000000..2c821afa
--- /dev/null
+++ b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenIteratorSpec.scala
@@ -0,0 +1,8 @@
+package tethys.jackson
+
+import tethys.readers.tokens.{TokenIteratorProducer, TokenIteratorSpec}
+
+class JacksonTokenIteratorSpec extends TokenIteratorSpec {
+ override def producer: TokenIteratorProducer =
+ jacksonTokenIteratorProducer
+}
diff --git a/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenIteratorTest.scala b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenIteratorTest.scala
deleted file mode 100644
index 060b7b89..00000000
--- a/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenIteratorTest.scala
+++ /dev/null
@@ -1,129 +0,0 @@
-package tethys.jackson
-
-import tethys._
-import tethys.commons.TokenNode._
-import org.scalatest.matchers.should.Matchers
-import org.scalatest.flatspec.AnyFlatSpec
-
-class JacksonTokenIteratorTest extends AnyFlatSpec with Matchers {
-
- behavior of "JacksonTokenIterator"
-
- it should "properly iterate over json string" in {
- val json = """{"a":1,"b":["s",true,{"a":null},1.0,false]}"""
- val it = json.toTokenIterator.fold(throw _, identity)
- it.currentToken().isObjectStart shouldBe true
-
- it.nextToken().isFieldName shouldBe true
- it.fieldName() shouldBe "a"
- it.nextToken().isNumberValue shouldBe true
- it.number() shouldBe 1
-
- it.nextToken().isFieldName shouldBe true
- it.fieldName() shouldBe "b"
- it.nextToken().isArrayStart shouldBe true
-
- it.nextToken().isStringValue shouldBe true
- it.string() shouldBe "s"
-
- it.nextToken().isBooleanValue shouldBe true
- it.boolean() shouldBe true
-
- it.nextToken().isObjectStart shouldBe true
- it.nextToken().isFieldName shouldBe true
- it.fieldName() shouldBe "a"
- it.nextToken().isNullValue shouldBe true
- it.nextToken().isObjectEnd shouldBe true
-
- it.nextToken().isNumberValue shouldBe true
- it.number() shouldBe 1.0
-
- it.nextToken().isBooleanValue shouldBe true
- it.boolean() shouldBe false
-
- it.nextToken().isArrayEnd shouldBe true
- it.nextToken().isObjectEnd shouldBe true
-
- it.nextToken().isEmpty shouldBe true
- }
-
- it should "correctly skip next expressions" in {
- val json = """{"a":1,"b":["s",true,{"a":null},1.0,false]}"""
- val it = json.toTokenIterator.fold(throw _, identity)
- it.currentToken().isObjectStart shouldBe true
-
- it.nextToken().isFieldName shouldBe true
- it.fieldName() shouldBe "a"
- it.next().skipExpression()
-
- it.currentToken().isFieldName shouldBe true
- it.fieldName() shouldBe "b"
- it.next().skipExpression()
- it.currentToken().isObjectEnd shouldBe true
-
- it.nextToken().isEmpty shouldBe true
- }
-
- it should "correctly collect expressions" in {
- val json = """{"a":1,"b":["s",true,{"a":null},1.0,false]}"""
- val it = json.toTokenIterator.fold(throw _, identity)
- it.currentToken().isObjectStart shouldBe true
-
- it.nextToken().isFieldName shouldBe true
- it.fieldName() shouldBe "a"
-
- val a = it.next().collectExpression()
- a.currentToken().isNumberValue shouldBe true
- a.number() shouldBe 1
- a.nextToken().isEmpty shouldBe true
-
- it.currentToken().isFieldName shouldBe true
- it.fieldName() shouldBe "b"
-
- val b = it.next().collectExpression()
- b.currentToken().isArrayStart shouldBe true
-
- b.nextToken().isStringValue shouldBe true
- b.string() shouldBe "s"
-
- b.nextToken().isBooleanValue shouldBe true
- b.boolean() shouldBe true
-
- b.nextToken().isObjectStart shouldBe true
- b.nextToken().isFieldName shouldBe true
- b.fieldName() shouldBe "a"
- b.nextToken().isNullValue shouldBe true
- b.nextToken().isObjectEnd shouldBe true
-
- b.nextToken().isNumberValue shouldBe true
- b.number() shouldBe 1.0
-
- b.nextToken().isBooleanValue shouldBe true
- b.boolean() shouldBe false
-
- b.nextToken().isArrayEnd shouldBe true
- b.nextToken().isEmpty shouldBe true
-
- it.currentToken().isObjectEnd shouldBe true
-
- it.nextToken().isEmpty shouldBe true
-
- }
-
- it should "generate proper tokens seq" in {
- val json = """{"a":1,"b":["s",true,{"a":null},1.0,false]}"""
-
- json.jsonAsTokensList shouldBe obj(
- "a" -> 1,
- "b" -> arr(
- "s",
- true,
- obj(
- "a" -> null
- ),
- 1.0,
- false
- )
- )
- }
-}
diff --git a/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterSpec.scala b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterSpec.scala
new file mode 100644
index 00000000..9fbe9a0b
--- /dev/null
+++ b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterSpec.scala
@@ -0,0 +1,6 @@
+package tethys.jackson
+
+import tethys._
+import tethys.writers.tokens.{TokenWriterSpec, TokenWriterProducer}
+
+class JacksonTokenWriterSpec extends TokenWriterSpec
diff --git a/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterTest.scala b/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterTest.scala
deleted file mode 100644
index 290a05d6..00000000
--- a/modules/backend/jackson/jackson-backend/src/test/scala/tethys/jackson/JacksonTokenWriterTest.scala
+++ /dev/null
@@ -1,105 +0,0 @@
-package tethys.jackson
-
-import java.io.StringWriter
-
-import org.scalatest.matchers.should.Matchers
-import org.scalatest.flatspec.AnyFlatSpec
-import tethys._
-import tethys.writers.tokens.TokenWriter
-
-class JacksonTokenWriterTest extends AnyFlatSpec with Matchers {
-
- def iterate(fun: (TokenWriter) => Unit): String = {
- val sw = new StringWriter()
- val tokenWriter = sw.toTokenWriter
- fun(tokenWriter)
- tokenWriter.close()
- sw.toString
- }
-
- behavior of "JacksonTokenWriter"
-
- it should "write String value" in {
- iterate(_.writeString("string")) shouldBe """"string""""
- }
-
- it should "write Byte value" in {
- iterate(_.writeNumber(1: Byte)) shouldBe """1"""
- }
-
- it should "write Short value" in {
- iterate(_.writeNumber(1: Short)) shouldBe """1"""
- }
-
- it should "write Int value" in {
- iterate(_.writeNumber(1: Int)) shouldBe """1"""
- }
-
- it should "write Long value" in {
- iterate(_.writeNumber(1: Long)) shouldBe """1"""
- }
-
- it should "write Float value" in {
- iterate(_.writeNumber(1: Float)) shouldBe """1.0"""
- }
-
- it should "write Double value" in {
- iterate(_.writeNumber(1: Double)) shouldBe """1.0"""
- }
-
- it should "write BitInt value" in {
- iterate(_.writeNumber(1: BigInt)) shouldBe """1"""
- }
-
- it should "write BigDecimal value" in {
- iterate(_.writeNumber(1: BigDecimal)) shouldBe """1"""
- }
-
- it should "write true value" in {
- iterate(_.writeBoolean(true)) shouldBe """true"""
- }
-
- it should "write false value" in {
- iterate(_.writeBoolean(false)) shouldBe """false"""
- }
-
- it should "write null value" in {
- iterate(_.writeNull()) shouldBe """null"""
- }
-
- it should "write object structure" in {
- iterate(_.writeObjectStart().writeObjectEnd()) shouldBe """{}"""
- }
-
- it should "write array structure" in {
- iterate(_.writeArrayStart().writeArrayEnd()) shouldBe """[]"""
- }
-
- it should "write raw json" in {
- iterate(
- _.writeRawJson("""{"some" : "raw json"}""")
- ) shouldBe """{"some" : "raw json"}"""
- }
-
- it should "write complex object structure" in {
- iterate {
- _.writeObjectStart()
- .writeFieldName("a")
- .writeNumber(1)
- .writeFieldName("b")
- .writeArrayStart()
- .writeString("s")
- .writeRawJson("false")
- .writeBoolean(true)
- .writeObjectStart()
- .writeFieldName("a")
- .writeNull()
- .writeObjectEnd()
- .writeArrayEnd()
- .writeFieldName("c")
- .writeRawJson("""{"some" : "raw json"}""")
- .writeObjectEnd()
- } shouldBe """{"a":1,"b":["s",false,true,{"a":null}],"c":{"some" : "raw json"}}"""
- }
-
-}
diff --git a/modules/benchmarks/README.md b/modules/benchmarks/README.md
index 5bb18392..d4042824 100644
--- a/modules/benchmarks/README.md
+++ b/modules/benchmarks/README.md
@@ -20,12 +20,14 @@ zio-json|2001448.661|255068.591|2001.826|237.499|3.439
name \ size|128b|1kb|128kb|1mb|32mb
---|---|---|---|---|---
-tethys-jackson|1819887.866|403267.403|3265.018|300.502|11.786
-pure-jackson|1109155.693|427421.925|3096.09|392.062|10.441
-circe|1348593.267|177497.011|1255.093|157.518|2.706
-play-json|327191.603|47736.655|360.734|49.417|1.064
-spray-json|1172370.195|148580.554|1053.782|142.164|3.425
-zio-json|2191564.981|266636.44|2092.48|239.598|7.347
+tethys|5623657.356|549741.974|4728.468|582.912|16.223
+tethys-jackson|1778419.803|434317.737|3577.799|499.213|11.343
+pure-jackson|1153651.397|420722.96|3177.921|394.1|9.972
+circe|1393264.287|176604.569|1279.973|156.184|2.192
+jsoniter|6036735.563|892874.569|5576.712|727.844|19.491
+play-json|329380.553|48283.895|375.691|49.423|1.065
+spray-json|1167053.231|149003.266|1109.504|138.309|2.95
+zio-json|2034331.785|277147.012|1934.274|238.862|7.332

diff --git a/modules/benchmarks/images/WritingPerformance.png b/modules/benchmarks/images/WritingPerformance.png
index a9da918d..d4dfbe88 100644
Binary files a/modules/benchmarks/images/WritingPerformance.png and b/modules/benchmarks/images/WritingPerformance.png differ
diff --git a/modules/benchmarks/jmh-reader.json b/modules/benchmarks/jmh-reader.json
index 41473838..f39feebb 100644
--- a/modules/benchmarks/jmh-reader.json
+++ b/modules/benchmarks/jmh-reader.json
@@ -1,1964 +1,4 @@
-[
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128b",
- "processorName" : "tethys-jackson"
- },
- "primaryMetric" : {
- "score" : 1442640.7423825143,
- "scoreError" : 78952.05954556566,
- "scoreConfidence" : [
- 1363688.6828369487,
- 1521592.80192808
- ],
- "scorePercentiles" : {
- "0.0" : 1424673.1377489888,
- "50.0" : 1447698.8286684984,
- "90.0" : 1450492.1744440713,
- "95.0" : 1450492.1744440713,
- "99.0" : 1450492.1744440713,
- "99.9" : 1450492.1744440713,
- "99.99" : 1450492.1744440713,
- "99.999" : 1450492.1744440713,
- "99.9999" : 1450492.1744440713,
- "100.0" : 1450492.1744440713
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 1424673.1377489888,
- 1445230.597521309,
- 1450492.1744440713,
- 1450167.059815688
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128b",
- "processorName" : "pure-jackson"
- },
- "primaryMetric" : {
- "score" : 1189799.5938527994,
- "scoreError" : 55190.13562201335,
- "scoreConfidence" : [
- 1134609.458230786,
- 1244989.7294748127
- ],
- "scorePercentiles" : {
- "0.0" : 1181260.403353757,
- "50.0" : 1188999.6753648564,
- "90.0" : 1199938.621327727,
- "95.0" : 1199938.621327727,
- "99.0" : 1199938.621327727,
- "99.9" : 1199938.621327727,
- "99.99" : 1199938.621327727,
- "99.999" : 1199938.621327727,
- "99.9999" : 1199938.621327727,
- "100.0" : 1199938.621327727
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 1181260.403353757,
- 1199938.621327727,
- 1184426.1072955476,
- 1193573.2434341651
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128b",
- "processorName" : "circe-jawn"
- },
- "primaryMetric" : {
- "score" : 866471.5537783348,
- "scoreError" : 5292.831154030518,
- "scoreConfidence" : [
- 861178.7226243042,
- 871764.3849323653
- ],
- "scorePercentiles" : {
- "0.0" : 865306.6927368287,
- "50.0" : 866678.8023450469,
- "90.0" : 867221.9176864165,
- "95.0" : 867221.9176864165,
- "99.0" : 867221.9176864165,
- "99.9" : 867221.9176864165,
- "99.99" : 867221.9176864165,
- "99.999" : 867221.9176864165,
- "99.9999" : 867221.9176864165,
- "100.0" : 867221.9176864165
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 866620.5653716372,
- 866737.0393184565,
- 867221.9176864165,
- 865306.6927368287
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128b",
- "processorName" : "circe-jackson"
- },
- "primaryMetric" : {
- "score" : 348613.15950133896,
- "scoreError" : 4090.8100080596914,
- "scoreConfidence" : [
- 344522.3494932793,
- 352703.9695093986
- ],
- "scorePercentiles" : {
- "0.0" : 347717.6375497792,
- "50.0" : 348790.33516368154,
- "90.0" : 349154.3301282136,
- "95.0" : 349154.3301282136,
- "99.0" : 349154.3301282136,
- "99.9" : 349154.3301282136,
- "99.99" : 349154.3301282136,
- "99.999" : 349154.3301282136,
- "99.9999" : 349154.3301282136,
- "100.0" : 349154.3301282136
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 347717.6375497792,
- 348640.8739915605,
- 348939.79633580265,
- 349154.3301282136
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128b",
- "processorName" : "play-json"
- },
- "primaryMetric" : {
- "score" : 534060.399904814,
- "scoreError" : 6799.148614768325,
- "scoreConfidence" : [
- 527261.2512900457,
- 540859.5485195823
- ],
- "scorePercentiles" : {
- "0.0" : 533261.5614296852,
- "50.0" : 533703.7411456113,
- "90.0" : 535572.5558983481,
- "95.0" : 535572.5558983481,
- "99.0" : 535572.5558983481,
- "99.9" : 535572.5558983481,
- "99.99" : 535572.5558983481,
- "99.999" : 535572.5558983481,
- "99.9999" : 535572.5558983481,
- "100.0" : 535572.5558983481
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 533437.2341277611,
- 533261.5614296852,
- 535572.5558983481,
- 533970.2481634615
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128b",
- "processorName" : "spray-json"
- },
- "primaryMetric" : {
- "score" : 971689.2127926768,
- "scoreError" : 18617.12137459307,
- "scoreConfidence" : [
- 953072.0914180836,
- 990306.3341672699
- ],
- "scorePercentiles" : {
- "0.0" : 970126.0228253725,
- "50.0" : 970311.7247863426,
- "90.0" : 976007.3787726497,
- "95.0" : 976007.3787726497,
- "99.0" : 976007.3787726497,
- "99.9" : 976007.3787726497,
- "99.99" : 976007.3787726497,
- "99.999" : 976007.3787726497,
- "99.9999" : 976007.3787726497,
- "100.0" : 976007.3787726497
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 976007.3787726497,
- 970222.9429407229,
- 970400.5066319621,
- 970126.0228253725
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128b",
- "processorName" : "zio-json"
- },
- "primaryMetric" : {
- "score" : 2001448.6610900809,
- "scoreError" : 7651.916564135762,
- "scoreConfidence" : [
- 1993796.744525945,
- 2009100.5776542167
- ],
- "scorePercentiles" : {
- "0.0" : 1999755.8992002185,
- "50.0" : 2001784.124021053,
- "90.0" : 2002470.497117998,
- "95.0" : 2002470.497117998,
- "99.0" : 2002470.497117998,
- "99.9" : 2002470.497117998,
- "99.99" : 2002470.497117998,
- "99.999" : 2002470.497117998,
- "99.9999" : 2002470.497117998,
- "100.0" : 2002470.497117998
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 2002470.497117998,
- 2001973.7530752632,
- 2001594.494966843,
- 1999755.8992002185
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1kb",
- "processorName" : "tethys-jackson"
- },
- "primaryMetric" : {
- "score" : 160561.91404439212,
- "scoreError" : 4174.087416426447,
- "scoreConfidence" : [
- 156387.82662796567,
- 164736.00146081857
- ],
- "scorePercentiles" : {
- "0.0" : 160034.28939428605,
- "50.0" : 160370.037503851,
- "90.0" : 161473.29177558052,
- "95.0" : 161473.29177558052,
- "99.0" : 161473.29177558052,
- "99.9" : 161473.29177558052,
- "99.99" : 161473.29177558052,
- "99.999" : 161473.29177558052,
- "99.9999" : 161473.29177558052,
- "100.0" : 161473.29177558052
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 160034.28939428605,
- 161473.29177558052,
- 160184.15701617362,
- 160555.91799152835
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1kb",
- "processorName" : "pure-jackson"
- },
- "primaryMetric" : {
- "score" : 212591.26322673875,
- "scoreError" : 820.8636873381179,
- "scoreConfidence" : [
- 211770.39953940065,
- 213412.12691407686
- ],
- "scorePercentiles" : {
- "0.0" : 212410.35066246407,
- "50.0" : 212623.53671140777,
- "90.0" : 212707.62882167546,
- "95.0" : 212707.62882167546,
- "99.0" : 212707.62882167546,
- "99.9" : 212707.62882167546,
- "99.99" : 212707.62882167546,
- "99.999" : 212707.62882167546,
- "99.9999" : 212707.62882167546,
- "100.0" : 212707.62882167546
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 212628.8313956589,
- 212410.35066246407,
- 212618.2420271566,
- 212707.62882167546
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1kb",
- "processorName" : "circe-jawn"
- },
- "primaryMetric" : {
- "score" : 112596.09569979832,
- "scoreError" : 3097.504155707831,
- "scoreConfidence" : [
- 109498.59154409048,
- 115693.59985550615
- ],
- "scorePercentiles" : {
- "0.0" : 112013.72842408167,
- "50.0" : 112603.01135833334,
- "90.0" : 113164.63165844487,
- "95.0" : 113164.63165844487,
- "99.0" : 113164.63165844487,
- "99.9" : 113164.63165844487,
- "99.99" : 113164.63165844487,
- "99.999" : 113164.63165844487,
- "99.9999" : 113164.63165844487,
- "100.0" : 113164.63165844487
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 112718.82671181016,
- 112487.19600485654,
- 112013.72842408167,
- 113164.63165844487
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1kb",
- "processorName" : "circe-jackson"
- },
- "primaryMetric" : {
- "score" : 48776.93307660853,
- "scoreError" : 1108.925216517014,
- "scoreConfidence" : [
- 47668.00786009152,
- 49885.858293125544
- ],
- "scorePercentiles" : {
- "0.0" : 48577.656124694724,
- "50.0" : 48767.20248107644,
- "90.0" : 48995.671219586526,
- "95.0" : 48995.671219586526,
- "99.0" : 48995.671219586526,
- "99.9" : 48995.671219586526,
- "99.99" : 48995.671219586526,
- "99.999" : 48995.671219586526,
- "99.9999" : 48995.671219586526,
- "100.0" : 48995.671219586526
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 48995.671219586526,
- 48784.52668914586,
- 48749.87827300703,
- 48577.656124694724
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1kb",
- "processorName" : "play-json"
- },
- "primaryMetric" : {
- "score" : 64804.73470720218,
- "scoreError" : 409.68292239984754,
- "scoreConfidence" : [
- 64395.05178480233,
- 65214.41762960203
- ],
- "scorePercentiles" : {
- "0.0" : 64745.4846006451,
- "50.0" : 64796.907385312996,
- "90.0" : 64879.639457537625,
- "95.0" : 64879.639457537625,
- "99.0" : 64879.639457537625,
- "99.9" : 64879.639457537625,
- "99.99" : 64879.639457537625,
- "99.999" : 64879.639457537625,
- "99.9999" : 64879.639457537625,
- "100.0" : 64879.639457537625
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 64834.42021945477,
- 64879.639457537625,
- 64759.39455117122,
- 64745.4846006451
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1kb",
- "processorName" : "spray-json"
- },
- "primaryMetric" : {
- "score" : 121363.84547637065,
- "scoreError" : 868.2404659021983,
- "scoreConfidence" : [
- 120495.60501046845,
- 122232.08594227285
- ],
- "scorePercentiles" : {
- "0.0" : 121173.45254790495,
- "50.0" : 121396.4259916749,
- "90.0" : 121489.07737422787,
- "95.0" : 121489.07737422787,
- "99.0" : 121489.07737422787,
- "99.9" : 121489.07737422787,
- "99.99" : 121489.07737422787,
- "99.999" : 121489.07737422787,
- "99.9999" : 121489.07737422787,
- "100.0" : 121489.07737422787
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 121403.61275627548,
- 121389.23922707431,
- 121489.07737422787,
- 121173.45254790495
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1kb",
- "processorName" : "zio-json"
- },
- "primaryMetric" : {
- "score" : 255068.59053340956,
- "scoreError" : 883.3864208655376,
- "scoreConfidence" : [
- 254185.20411254402,
- 255951.9769542751
- ],
- "scorePercentiles" : {
- "0.0" : 254882.52912064738,
- "50.0" : 255108.6583979822,
- "90.0" : 255174.51621702657,
- "95.0" : 255174.51621702657,
- "99.0" : 255174.51621702657,
- "99.9" : 255174.51621702657,
- "99.99" : 255174.51621702657,
- "99.999" : 255174.51621702657,
- "99.9999" : 255174.51621702657,
- "100.0" : 255174.51621702657
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 254882.52912064738,
- 255167.8818117706,
- 255049.43498419374,
- 255174.51621702657
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128kb",
- "processorName" : "tethys-jackson"
- },
- "primaryMetric" : {
- "score" : 1342.1847033298734,
- "scoreError" : 16.20391937035811,
- "scoreConfidence" : [
- 1325.9807839595153,
- 1358.3886227002315
- ],
- "scorePercentiles" : {
- "0.0" : 1338.5377154330088,
- "50.0" : 1342.9812388787618,
- "90.0" : 1344.2386201289612,
- "95.0" : 1344.2386201289612,
- "99.0" : 1344.2386201289612,
- "99.9" : 1344.2386201289612,
- "99.99" : 1344.2386201289612,
- "99.999" : 1344.2386201289612,
- "99.9999" : 1344.2386201289612,
- "100.0" : 1344.2386201289612
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 1344.2386201289612,
- 1343.1758619846332,
- 1342.7866157728904,
- 1338.5377154330088
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128kb",
- "processorName" : "pure-jackson"
- },
- "primaryMetric" : {
- "score" : 1721.6229988447876,
- "scoreError" : 146.59883259580164,
- "scoreConfidence" : [
- 1575.024166248986,
- 1868.2218314405893
- ],
- "scorePercentiles" : {
- "0.0" : 1699.2538695809183,
- "50.0" : 1721.8093755250118,
- "90.0" : 1743.6193747482089,
- "95.0" : 1743.6193747482089,
- "99.0" : 1743.6193747482089,
- "99.9" : 1743.6193747482089,
- "99.99" : 1743.6193747482089,
- "99.999" : 1743.6193747482089,
- "99.9999" : 1743.6193747482089,
- "100.0" : 1743.6193747482089
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 1699.2538695809183,
- 1705.0803445075178,
- 1738.5384065425058,
- 1743.6193747482089
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128kb",
- "processorName" : "circe-jawn"
- },
- "primaryMetric" : {
- "score" : 945.4187760649006,
- "scoreError" : 2.183751802419896,
- "scoreConfidence" : [
- 943.2350242624807,
- 947.6025278673205
- ],
- "scorePercentiles" : {
- "0.0" : 945.1250529808461,
- "50.0" : 945.3246784380651,
- "90.0" : 945.9006944026257,
- "95.0" : 945.9006944026257,
- "99.0" : 945.9006944026257,
- "99.9" : 945.9006944026257,
- "99.99" : 945.9006944026257,
- "99.999" : 945.9006944026257,
- "99.9999" : 945.9006944026257,
- "100.0" : 945.9006944026257
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 945.9006944026257,
- 945.2682026376795,
- 945.3811542384509,
- 945.1250529808461
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128kb",
- "processorName" : "circe-jackson"
- },
- "primaryMetric" : {
- "score" : 363.00530700456716,
- "scoreError" : 2.5284431265662755,
- "scoreConfidence" : [
- 360.47686387800087,
- 365.53375013113344
- ],
- "scorePercentiles" : {
- "0.0" : 362.7165362775112,
- "50.0" : 362.861798791369,
- "90.0" : 363.58109415801937,
- "95.0" : 363.58109415801937,
- "99.0" : 363.58109415801937,
- "99.9" : 363.58109415801937,
- "99.99" : 363.58109415801937,
- "99.999" : 363.58109415801937,
- "99.9999" : 363.58109415801937,
- "100.0" : 363.58109415801937
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 362.90173654915014,
- 362.8218610335879,
- 363.58109415801937,
- 362.7165362775112
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128kb",
- "processorName" : "play-json"
- },
- "primaryMetric" : {
- "score" : 499.5742919458588,
- "scoreError" : 6.093313640347425,
- "scoreConfidence" : [
- 493.48097830551137,
- 505.66760558620626
- ],
- "scorePercentiles" : {
- "0.0" : 498.7420007630053,
- "50.0" : 499.37242517065727,
- "90.0" : 500.81031667911543,
- "95.0" : 500.81031667911543,
- "99.0" : 500.81031667911543,
- "99.9" : 500.81031667911543,
- "99.99" : 500.81031667911543,
- "99.999" : 500.81031667911543,
- "99.9999" : 500.81031667911543,
- "100.0" : 500.81031667911543
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 500.81031667911543,
- 499.7999068452079,
- 498.9449434961067,
- 498.7420007630053
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128kb",
- "processorName" : "spray-json"
- },
- "primaryMetric" : {
- "score" : 1028.7599672304448,
- "scoreError" : 6.1122124547278345,
- "scoreConfidence" : [
- 1022.647754775717,
- 1034.8721796851726
- ],
- "scorePercentiles" : {
- "0.0" : 1027.8687142210626,
- "50.0" : 1028.5465020153126,
- "90.0" : 1030.0781506700912,
- "95.0" : 1030.0781506700912,
- "99.0" : 1030.0781506700912,
- "99.9" : 1030.0781506700912,
- "99.99" : 1030.0781506700912,
- "99.999" : 1030.0781506700912,
- "99.9999" : 1030.0781506700912,
- "100.0" : 1030.0781506700912
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 1028.7210660331664,
- 1028.3719379974589,
- 1027.8687142210626,
- 1030.0781506700912
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "128kb",
- "processorName" : "zio-json"
- },
- "primaryMetric" : {
- "score" : 2001.826408239838,
- "scoreError" : 42.480581859391826,
- "scoreConfidence" : [
- 1959.3458263804462,
- 2044.3069900992298
- ],
- "scorePercentiles" : {
- "0.0" : 1994.0433170035403,
- "50.0" : 2001.5805047947372,
- "90.0" : 2010.1013063663365,
- "95.0" : 2010.1013063663365,
- "99.0" : 2010.1013063663365,
- "99.9" : 2010.1013063663365,
- "99.99" : 2010.1013063663365,
- "99.999" : 2010.1013063663365,
- "99.9999" : 2010.1013063663365,
- "100.0" : 2010.1013063663365
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 2002.0692642253507,
- 2001.0917453641237,
- 1994.0433170035403,
- 2010.1013063663365
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1mb",
- "processorName" : "tethys-jackson"
- },
- "primaryMetric" : {
- "score" : 160.4477490800406,
- "scoreError" : 2.201194254702574,
- "scoreConfidence" : [
- 158.24655482533802,
- 162.6489433347432
- ],
- "scorePercentiles" : {
- "0.0" : 160.06205164274286,
- "50.0" : 160.4395764682343,
- "90.0" : 160.849791740951,
- "95.0" : 160.849791740951,
- "99.0" : 160.849791740951,
- "99.9" : 160.849791740951,
- "99.99" : 160.849791740951,
- "99.999" : 160.849791740951,
- "99.9999" : 160.849791740951,
- "100.0" : 160.849791740951
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 160.06205164274286,
- 160.3025239132917,
- 160.849791740951,
- 160.5766290231769
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1mb",
- "processorName" : "pure-jackson"
- },
- "primaryMetric" : {
- "score" : 184.57242130589123,
- "scoreError" : 32.95243699325334,
- "scoreConfidence" : [
- 151.61998431263788,
- 217.52485829914457
- ],
- "scorePercentiles" : {
- "0.0" : 176.9272357038962,
- "50.0" : 187.02461410877956,
- "90.0" : 187.31322130210955,
- "95.0" : 187.31322130210955,
- "99.0" : 187.31322130210955,
- "99.9" : 187.31322130210955,
- "99.99" : 187.31322130210955,
- "99.999" : 187.31322130210955,
- "99.9999" : 187.31322130210955,
- "100.0" : 187.31322130210955
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 187.31322130210955,
- 187.13662544786501,
- 186.9126027696941,
- 176.9272357038962
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1mb",
- "processorName" : "circe-jawn"
- },
- "primaryMetric" : {
- "score" : 124.76672484216905,
- "scoreError" : 0.33188848774535284,
- "scoreConfidence" : [
- 124.4348363544237,
- 125.09861332991441
- ],
- "scorePercentiles" : {
- "0.0" : 124.72972836530786,
- "50.0" : 124.7491752546027,
- "90.0" : 124.83882049416289,
- "95.0" : 124.83882049416289,
- "99.0" : 124.83882049416289,
- "99.9" : 124.83882049416289,
- "99.99" : 124.83882049416289,
- "99.999" : 124.83882049416289,
- "99.9999" : 124.83882049416289,
- "100.0" : 124.83882049416289
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 124.83882049416289,
- 124.73005609434921,
- 124.7682944148562,
- 124.72972836530786
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1mb",
- "processorName" : "circe-jackson"
- },
- "primaryMetric" : {
- "score" : 39.48507722977758,
- "scoreError" : 0.40852989142117696,
- "scoreConfidence" : [
- 39.076547338356406,
- 39.893607121198755
- ],
- "scorePercentiles" : {
- "0.0" : 39.41285789748825,
- "50.0" : 39.48438984253872,
- "90.0" : 39.55867133654463,
- "95.0" : 39.55867133654463,
- "99.0" : 39.55867133654463,
- "99.9" : 39.55867133654463,
- "99.99" : 39.55867133654463,
- "99.999" : 39.55867133654463,
- "99.9999" : 39.55867133654463,
- "100.0" : 39.55867133654463
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 39.55867133654463,
- 39.41285789748825,
- 39.510445623948755,
- 39.45833406112869
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1mb",
- "processorName" : "play-json"
- },
- "primaryMetric" : {
- "score" : 43.91015336909207,
- "scoreError" : 0.8530360585248596,
- "scoreConfidence" : [
- 43.057117310567214,
- 44.76318942761693
- ],
- "scorePercentiles" : {
- "0.0" : 43.76700960688348,
- "50.0" : 43.921196372174855,
- "90.0" : 44.03121112513508,
- "95.0" : 44.03121112513508,
- "99.0" : 44.03121112513508,
- "99.9" : 44.03121112513508,
- "99.99" : 44.03121112513508,
- "99.999" : 44.03121112513508,
- "99.9999" : 44.03121112513508,
- "100.0" : 44.03121112513508
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 43.76700960688348,
- 44.01309180369185,
- 43.82930094065786,
- 44.03121112513508
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1mb",
- "processorName" : "spray-json"
- },
- "primaryMetric" : {
- "score" : 123.77137034655759,
- "scoreError" : 1.3165189495625613,
- "scoreConfidence" : [
- 122.45485139699502,
- 125.08788929612015
- ],
- "scorePercentiles" : {
- "0.0" : 123.51933731134018,
- "50.0" : 123.79114484148198,
- "90.0" : 123.98385439192622,
- "95.0" : 123.98385439192622,
- "99.0" : 123.98385439192622,
- "99.9" : 123.98385439192622,
- "99.99" : 123.98385439192622,
- "99.999" : 123.98385439192622,
- "99.9999" : 123.98385439192622,
- "100.0" : 123.98385439192622
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 123.98385439192622,
- 123.87794615660056,
- 123.7043435263634,
- 123.51933731134018
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "1mb",
- "processorName" : "zio-json"
- },
- "primaryMetric" : {
- "score" : 237.49920878178992,
- "scoreError" : 0.8468180645129579,
- "scoreConfidence" : [
- 236.65239071727697,
- 238.34602684630286
- ],
- "scorePercentiles" : {
- "0.0" : 237.38379714534622,
- "50.0" : 237.47990141743617,
- "90.0" : 237.65323514694114,
- "95.0" : 237.65323514694114,
- "99.0" : 237.65323514694114,
- "99.9" : 237.65323514694114,
- "99.99" : 237.65323514694114,
- "99.999" : 237.65323514694114,
- "99.9999" : 237.65323514694114,
- "100.0" : 237.65323514694114
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 237.38379714534622,
- 237.65323514694114,
- 237.39704710989793,
- 237.56275572497444
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "32mb",
- "processorName" : "tethys-jackson"
- },
- "primaryMetric" : {
- "score" : 3.0137784534990244,
- "scoreError" : 0.40756789287665157,
- "scoreConfidence" : [
- 2.606210560622373,
- 3.421346346375676
- ],
- "scorePercentiles" : {
- "0.0" : 2.929741526264833,
- "50.0" : 3.0227237465019052,
- "90.0" : 3.079924794727454,
- "95.0" : 3.079924794727454,
- "99.0" : 3.079924794727454,
- "99.9" : 3.079924794727454,
- "99.99" : 3.079924794727454,
- "99.999" : 3.079924794727454,
- "99.9999" : 3.079924794727454,
- "100.0" : 3.079924794727454
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 3.0097527508957675,
- 3.035694742108043,
- 2.929741526264833,
- 3.079924794727454
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "32mb",
- "processorName" : "pure-jackson"
- },
- "primaryMetric" : {
- "score" : 3.7330143364309074,
- "scoreError" : 1.0340570672985834,
- "scoreConfidence" : [
- 2.698957269132324,
- 4.76707140372949
- ],
- "scorePercentiles" : {
- "0.0" : 3.5812111456422144,
- "50.0" : 3.7373796051402604,
- "90.0" : 3.876086989800895,
- "95.0" : 3.876086989800895,
- "99.0" : 3.876086989800895,
- "99.9" : 3.876086989800895,
- "99.99" : 3.876086989800895,
- "99.999" : 3.876086989800895,
- "99.9999" : 3.876086989800895,
- "100.0" : 3.876086989800895
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 3.6084057431955556,
- 3.876086989800895,
- 3.866353467084965,
- 3.5812111456422144
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "32mb",
- "processorName" : "circe-jawn"
- },
- "primaryMetric" : {
- "score" : 1.5225411461155023,
- "scoreError" : 0.39755599886945797,
- "scoreConfidence" : [
- 1.1249851472460444,
- 1.9200971449849602
- ],
- "scorePercentiles" : {
- "0.0" : 1.4362130009126042,
- "50.0" : 1.5373792525243788,
- "90.0" : 1.5791930785006478,
- "95.0" : 1.5791930785006478,
- "99.0" : 1.5791930785006478,
- "99.9" : 1.5791930785006478,
- "99.99" : 1.5791930785006478,
- "99.999" : 1.5791930785006478,
- "99.9999" : 1.5791930785006478,
- "100.0" : 1.5791930785006478
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 1.5486181533002588,
- 1.5791930785006478,
- 1.4362130009126042,
- 1.5261403517484986
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "32mb",
- "processorName" : "circe-jackson"
- },
- "primaryMetric" : {
- "score" : 0.8719602908897323,
- "scoreError" : 0.13303923204223653,
- "scoreConfidence" : [
- 0.7389210588474957,
- 1.0049995229319688
- ],
- "scorePercentiles" : {
- "0.0" : 0.852918679952709,
- "50.0" : 0.870454311975011,
- "90.0" : 0.8940138596561977,
- "95.0" : 0.8940138596561977,
- "99.0" : 0.8940138596561977,
- "99.9" : 0.8940138596561977,
- "99.99" : 0.8940138596561977,
- "99.999" : 0.8940138596561977,
- "99.9999" : 0.8940138596561977,
- "100.0" : 0.8940138596561977
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 0.8559954990209021,
- 0.852918679952709,
- 0.8940138596561977,
- 0.8849131249291199
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "32mb",
- "processorName" : "play-json"
- },
- "primaryMetric" : {
- "score" : 0.9268646470493817,
- "scoreError" : 0.07142129572211389,
- "scoreConfidence" : [
- 0.8554433513272678,
- 0.9982859427714955
- ],
- "scorePercentiles" : {
- "0.0" : 0.9158881941393551,
- "50.0" : 0.9248947313372281,
- "90.0" : 0.9417809313837151,
- "95.0" : 0.9417809313837151,
- "99.0" : 0.9417809313837151,
- "99.9" : 0.9417809313837151,
- "99.99" : 0.9417809313837151,
- "99.999" : 0.9417809313837151,
- "99.9999" : 0.9417809313837151,
- "100.0" : 0.9417809313837151
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 0.9220897620361919,
- 0.9158881941393551,
- 0.9276997006382643,
- 0.9417809313837151
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "32mb",
- "processorName" : "spray-json"
- },
- "primaryMetric" : {
- "score" : 2.188200726362677,
- "scoreError" : 0.3354607091980212,
- "scoreConfidence" : [
- 1.8527400171646558,
- 2.523661435560698
- ],
- "scorePercentiles" : {
- "0.0" : 2.1401609225442733,
- "50.0" : 2.1869952732658535,
- "90.0" : 2.238651436374728,
- "95.0" : 2.238651436374728,
- "99.0" : 2.238651436374728,
- "99.9" : 2.238651436374728,
- "99.99" : 2.238651436374728,
- "99.999" : 2.238651436374728,
- "99.9999" : 2.238651436374728,
- "100.0" : 2.238651436374728
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 2.2271751777085904,
- 2.1401609225442733,
- 2.1468153688231166,
- 2.238651436374728
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- },
- {
- "jmhVersion" : "1.36",
- "benchmark" : "json.bench.JmhReaderBench.bench",
- "mode" : "thrpt",
- "threads" : 1,
- "forks" : 1,
- "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
- "jvmArgs" : [
- "-Xms1G",
- "-Xmx1G"
- ],
- "jdkVersion" : "17.0.7",
- "vmName" : "OpenJDK 64-Bit Server VM",
- "vmVersion" : "17.0.7+7-LTS",
- "warmupIterations" : 4,
- "warmupTime" : "5 s",
- "warmupBatchSize" : 1,
- "measurementIterations" : 4,
- "measurementTime" : "5 s",
- "measurementBatchSize" : 1,
- "params" : {
- "jsonSize" : "32mb",
- "processorName" : "zio-json"
- },
- "primaryMetric" : {
- "score" : 3.438909701909425,
- "scoreError" : 0.28976027522897935,
- "scoreConfidence" : [
- 3.1491494266804456,
- 3.7286699771384044
- ],
- "scorePercentiles" : {
- "0.0" : 3.398288711481961,
- "50.0" : 3.437554759758812,
- "90.0" : 3.482240576638115,
- "95.0" : 3.482240576638115,
- "99.0" : 3.482240576638115,
- "99.9" : 3.482240576638115,
- "99.99" : 3.482240576638115,
- "99.999" : 3.482240576638115,
- "99.9999" : 3.482240576638115,
- "100.0" : 3.482240576638115
- },
- "scoreUnit" : "ops/s",
- "rawData" : [
- [
- 3.398288711481961,
- 3.482240576638115,
- 3.402193975664248,
- 3.472915543853375
- ]
- ]
- },
- "secondaryMetrics" : {
- }
- }
-]
-
-
+"Benchmark","Mode","Threads","Samples","Score","Score Error (99.9%)","Unit","Param: jsonSize","Param: processorName"
+"json.bench.JmhReaderBench.bench","thrpt",1,4,1845394.186985,74170.513403,"ops/s",128b,tethys
+"json.bench.JmhReaderBench.bench","thrpt",1,4,1498716.500223,20783.273473,"ops/s",128b,tethys-jackson
+"json.bench.JmhWriterBench.bench","thrpt",1,4,5343206.315903,132400.559328,"ops/s",128b,tethys
diff --git a/modules/benchmarks/jmh-reader.log b/modules/benchmarks/jmh-reader.log
new file mode 100644
index 00000000..acd1abe2
--- /dev/null
+++ b/modules/benchmarks/jmh-reader.log
@@ -0,0 +1,113 @@
+# JMH version: 1.37
+# VM version: JDK 17.0.7, OpenJDK 64-Bit Server VM, 17.0.7+7-LTS
+# VM invoker: /Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java
+# VM options: -Xms1G -Xmx1G
+# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
+# Warmup: 4 iterations, 5 s each
+# Measurement: 4 iterations, 5 s each
+# Timeout: 10 min per iteration
+# Threads: 1 thread, will synchronize iterations
+# Benchmark mode: Throughput, ops/time
+# Benchmark: json.bench.JmhReaderBench.bench
+# Parameters: (jsonSize = 128b, processorName = tethys)
+
+# Run progress: 0.00% complete, ETA 00:01:28
+# Fork: 1 of 1
+# Warmup Iteration 1: 1784680.808 ops/s
+# Warmup Iteration 2: 1846010.197 ops/s
+# Warmup Iteration 3: 1841240.555 ops/s
+# Warmup Iteration 4: 1835400.577 ops/s
+Iteration 1: 1850061.894 ops/s
+Iteration 2: 1835378.960 ops/s
+Iteration 3: 1859444.262 ops/s
+Iteration 4: 1836691.632 ops/s
+
+
+Result "json.bench.JmhReaderBench.bench":
+ 1845394.187 ±(99.9%) 74170.513 ops/s [Average]
+ (min, avg, max) = (1835378.960, 1845394.187, 1859444.262), stdev = 11477.969
+ CI (99.9%): [1771223.674, 1919564.700] (assumes normal distribution)
+
+
+# JMH version: 1.37
+# VM version: JDK 17.0.7, OpenJDK 64-Bit Server VM, 17.0.7+7-LTS
+# VM invoker: /Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java
+# VM options: -Xms1G -Xmx1G
+# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
+# Warmup: 4 iterations, 5 s each
+# Measurement: 4 iterations, 5 s each
+# Timeout: 10 min per iteration
+# Threads: 1 thread, will synchronize iterations
+# Benchmark mode: Throughput, ops/time
+# Benchmark: json.bench.JmhReaderBench.bench
+# Parameters: (jsonSize = 128b, processorName = tethys-jackson)
+
+# Run progress: 45.45% complete, ETA 00:00:48
+# Fork: 1 of 1
+# Warmup Iteration 1: 1436200.404 ops/s
+# Warmup Iteration 2: 1497128.016 ops/s
+# Warmup Iteration 3: 1488807.518 ops/s
+# Warmup Iteration 4: 1495728.184 ops/s
+Iteration 1: 1498466.737 ops/s
+Iteration 2: 1498386.191 ops/s
+Iteration 3: 1495089.091 ops/s
+Iteration 4: 1502923.983 ops/s
+
+
+Result "json.bench.JmhReaderBench.bench":
+ 1498716.500 ±(99.9%) 20783.273 ops/s [Average]
+ (min, avg, max) = (1495089.091, 1498716.500, 1502923.983), stdev = 3216.235
+ CI (99.9%): [1477933.227, 1519499.774] (assumes normal distribution)
+
+
+# JMH version: 1.37
+# VM version: JDK 17.0.7, OpenJDK 64-Bit Server VM, 17.0.7+7-LTS
+# VM invoker: /Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java
+# VM options: -Xms1G -Xmx1G
+# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
+# Warmup: 4 iterations, 1 s each
+# Measurement: 4 iterations, 1 s each
+# Timeout: 10 min per iteration
+# Threads: 1 thread, will synchronize iterations
+# Benchmark mode: Throughput, ops/time
+# Benchmark: json.bench.JmhWriterBench.bench
+# Parameters: (jsonSize = 128b, processorName = tethys)
+
+# Run progress: 90.91% complete, ETA 00:00:08
+# Fork: 1 of 1
+# Warmup Iteration 1: 4413936.316 ops/s
+# Warmup Iteration 2: 5379633.919 ops/s
+# Warmup Iteration 3: 5391641.469 ops/s
+# Warmup Iteration 4: 5370543.532 ops/s
+Iteration 1: 5356394.259 ops/s
+Iteration 2: 5314757.444 ops/s
+Iteration 3: 5359772.820 ops/s
+Iteration 4: 5341900.740 ops/s
+
+
+Result "json.bench.JmhWriterBench.bench":
+ 5343206.316 ±(99.9%) 132400.559 ops/s [Average]
+ (min, avg, max) = (5314757.444, 5343206.316, 5359772.820), stdev = 20489.133
+ CI (99.9%): [5210805.757, 5475606.875] (assumes normal distribution)
+
+
+# Run complete. Total time: 00:01:28
+
+REMEMBER: The numbers below are just data. To gain reusable insights, you need to follow up on
+why the numbers are the way they are. Use profilers (see -prof, -lprof), design factorial
+experiments, perform baseline and negative tests that provide experimental control, make sure
+the benchmarking environment is safe on JVM/OS/HW level, ask for reviews from the domain experts.
+Do not assume the numbers tell you what you want them to tell.
+
+NOTE: Current JVM experimentally supports Compiler Blackholes, and they are in use. Please exercise
+extra caution when trusting the results, look into the generated code to check the benchmark still
+works, and factor in a small probability of new VM bugs. Additionally, while comparisons between
+different JVMs are already problematic, the performance difference caused by different Blackhole
+modes can be very significant. Please make sure you use the consistent Blackhole mode for comparisons.
+
+Benchmark (jsonSize) (processorName) Mode Cnt Score Error Units
+JmhReaderBench.bench 128b tethys thrpt 4 1845394.187 ± 74170.513 ops/s
+JmhReaderBench.bench 128b tethys-jackson thrpt 4 1498716.500 ± 20783.273 ops/s
+JmhWriterBench.bench 128b tethys thrpt 4 5343206.316 ± 132400.559 ops/s
+
+Benchmark result is saved to jmh-reader.json
diff --git a/modules/benchmarks/jmh-writer.json b/modules/benchmarks/jmh-writer.json
index 5227eab3..3905b378 100644
--- a/modules/benchmarks/jmh-writer.json
+++ b/modules/benchmarks/jmh-writer.json
@@ -1,6 +1,6 @@
[
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -14,41 +14,97 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "128b",
+ "processorName" : "tethys"
+ },
+ "primaryMetric" : {
+ "score" : 5623657.356338973,
+ "scoreError" : 621518.5137114808,
+ "scoreConfidence" : [
+ 5002138.842627493,
+ 6245175.870050454
+ ],
+ "scorePercentiles" : {
+ "0.0" : 5542828.219103702,
+ "50.0" : 5594806.800902309,
+ "90.0" : 5762187.604447574,
+ "95.0" : 5762187.604447574,
+ "99.0" : 5762187.604447574,
+ "99.9" : 5762187.604447574,
+ "99.99" : 5762187.604447574,
+ "99.999" : 5762187.604447574,
+ "99.9999" : 5762187.604447574,
+ "100.0" : 5762187.604447574
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 5762187.604447574,
+ 5608287.904018748,
+ 5542828.219103702,
+ 5581325.69778587
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128b",
"processorName" : "tethys-jackson"
},
"primaryMetric" : {
- "score" : 1819887.866280839,
- "scoreError" : 204446.28818611056,
+ "score" : 1778419.8031546003,
+ "scoreError" : 88448.6938156392,
"scoreConfidence" : [
- 1615441.5780947285,
- 2024334.1544669496
+ 1689971.109338961,
+ 1866868.4969702396
],
"scorePercentiles" : {
- "0.0" : 1787500.8132402124,
- "50.0" : 1822320.2268073645,
- "90.0" : 1847410.1982684152,
- "95.0" : 1847410.1982684152,
- "99.0" : 1847410.1982684152,
- "99.9" : 1847410.1982684152,
- "99.99" : 1847410.1982684152,
- "99.999" : 1847410.1982684152,
- "99.9999" : 1847410.1982684152,
- "100.0" : 1847410.1982684152
+ "0.0" : 1758832.1560085032,
+ "50.0" : 1782241.358763401,
+ "90.0" : 1790364.3390830962,
+ "95.0" : 1790364.3390830962,
+ "99.0" : 1790364.3390830962,
+ "99.9" : 1790364.3390830962,
+ "99.99" : 1790364.3390830962,
+ "99.999" : 1790364.3390830962,
+ "99.9999" : 1790364.3390830962,
+ "100.0" : 1790364.3390830962
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 1847410.1982684152,
- 1846658.5849204224,
- 1797981.8686943066,
- 1787500.8132402124
+ 1790364.3390830962,
+ 1780440.0720526075,
+ 1784042.645474194,
+ 1758832.1560085032
]
]
},
@@ -56,7 +112,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -70,41 +126,97 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "128b",
+ "processorName" : "jsoniter"
+ },
+ "primaryMetric" : {
+ "score" : 6036735.562977094,
+ "scoreError" : 100765.38118882457,
+ "scoreConfidence" : [
+ 5935970.181788269,
+ 6137500.944165919
+ ],
+ "scorePercentiles" : {
+ "0.0" : 6016191.768396554,
+ "50.0" : 6038824.112508209,
+ "90.0" : 6053102.258495404,
+ "95.0" : 6053102.258495404,
+ "99.0" : 6053102.258495404,
+ "99.9" : 6053102.258495404,
+ "99.99" : 6053102.258495404,
+ "99.999" : 6053102.258495404,
+ "99.9999" : 6053102.258495404,
+ "100.0" : 6053102.258495404
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 6053102.258495404,
+ 6042750.721029169,
+ 6016191.768396554,
+ 6034897.50398725
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128b",
"processorName" : "pure-jackson"
},
"primaryMetric" : {
- "score" : 1109155.6927294005,
- "scoreError" : 11722.369381590592,
+ "score" : 1153651.396793026,
+ "scoreError" : 43091.44637822818,
"scoreConfidence" : [
- 1097433.32334781,
- 1120878.062110991
+ 1110559.9504147978,
+ 1196742.8431712543
],
"scorePercentiles" : {
- "0.0" : 1107159.6211813919,
- "50.0" : 1109266.9122699166,
- "90.0" : 1110929.3251963765,
- "95.0" : 1110929.3251963765,
- "99.0" : 1110929.3251963765,
- "99.9" : 1110929.3251963765,
- "99.99" : 1110929.3251963765,
- "99.999" : 1110929.3251963765,
- "99.9999" : 1110929.3251963765,
- "100.0" : 1110929.3251963765
+ "0.0" : 1148131.5116133774,
+ "50.0" : 1151584.3156358043,
+ "90.0" : 1163305.4442871178,
+ "95.0" : 1163305.4442871178,
+ "99.0" : 1163305.4442871178,
+ "99.9" : 1163305.4442871178,
+ "99.99" : 1163305.4442871178,
+ "99.999" : 1163305.4442871178,
+ "99.9999" : 1163305.4442871178,
+ "100.0" : 1163305.4442871178
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 1107159.6211813919,
- 1110432.569796542,
- 1110929.3251963765,
- 1108101.2547432913
+ 1150813.2051328751,
+ 1163305.4442871178,
+ 1152355.4261387335,
+ 1148131.5116133774
]
]
},
@@ -112,7 +224,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -126,41 +238,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128b",
"processorName" : "circe"
},
"primaryMetric" : {
- "score" : 1348593.2665467546,
- "scoreError" : 16725.42285120894,
+ "score" : 1393264.287496805,
+ "scoreError" : 75110.6708440298,
"scoreConfidence" : [
- 1331867.8436955458,
- 1365318.6893979635
+ 1318153.616652775,
+ 1468374.9583408348
],
"scorePercentiles" : {
- "0.0" : 1344915.614381369,
- "50.0" : 1349258.9734497662,
- "90.0" : 1350939.5049061175,
- "95.0" : 1350939.5049061175,
- "99.0" : 1350939.5049061175,
- "99.9" : 1350939.5049061175,
- "99.99" : 1350939.5049061175,
- "99.999" : 1350939.5049061175,
- "99.9999" : 1350939.5049061175,
- "100.0" : 1350939.5049061175
+ "0.0" : 1379468.2130725912,
+ "50.0" : 1395013.830156555,
+ "90.0" : 1403561.2766015183,
+ "95.0" : 1403561.2766015183,
+ "99.0" : 1403561.2766015183,
+ "99.9" : 1403561.2766015183,
+ "99.99" : 1403561.2766015183,
+ "99.999" : 1403561.2766015183,
+ "99.9999" : 1403561.2766015183,
+ "100.0" : 1403561.2766015183
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 1348957.93100329,
- 1349560.0158962426,
- 1350939.5049061175,
- 1344915.614381369
+ 1402184.3592851234,
+ 1387843.3010279862,
+ 1403561.2766015183,
+ 1379468.2130725912
]
]
},
@@ -168,7 +280,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -182,41 +294,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128b",
"processorName" : "play-json"
},
"primaryMetric" : {
- "score" : 327191.60334054136,
- "scoreError" : 16833.393674908828,
+ "score" : 329380.55274132255,
+ "scoreError" : 7549.991291541363,
"scoreConfidence" : [
- 310358.2096656325,
- 344024.9970154502
+ 321830.5614497812,
+ 336930.5440328639
],
"scorePercentiles" : {
- "0.0" : 323284.24612123315,
- "50.0" : 328479.9201514422,
- "90.0" : 328522.32693804795,
- "95.0" : 328522.32693804795,
- "99.0" : 328522.32693804795,
- "99.9" : 328522.32693804795,
- "99.99" : 328522.32693804795,
- "99.999" : 328522.32693804795,
- "99.9999" : 328522.32693804795,
- "100.0" : 328522.32693804795
+ "0.0" : 328093.33984041726,
+ "50.0" : 329517.90137734724,
+ "90.0" : 330393.0683701786,
+ "95.0" : 330393.0683701786,
+ "99.0" : 330393.0683701786,
+ "99.9" : 330393.0683701786,
+ "99.99" : 330393.0683701786,
+ "99.999" : 330393.0683701786,
+ "99.9999" : 330393.0683701786,
+ "100.0" : 330393.0683701786
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 323284.24612123315,
- 328473.80463527696,
- 328486.03566760744,
- 328522.32693804795
+ 328688.61623730283,
+ 330393.0683701786,
+ 330347.18651739164,
+ 328093.33984041726
]
]
},
@@ -224,7 +336,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -238,41 +350,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128b",
"processorName" : "spray-json"
},
"primaryMetric" : {
- "score" : 1172370.1949547157,
- "scoreError" : 44623.57635400855,
+ "score" : 1167053.2311822318,
+ "scoreError" : 5257.9839040498,
"scoreConfidence" : [
- 1127746.618600707,
- 1216993.7713087243
+ 1161795.247278182,
+ 1172311.2150862815
],
"scorePercentiles" : {
- "0.0" : 1164002.9183628866,
- "50.0" : 1172433.802146774,
- "90.0" : 1180610.257162428,
- "95.0" : 1180610.257162428,
- "99.0" : 1180610.257162428,
- "99.9" : 1180610.257162428,
- "99.99" : 1180610.257162428,
- "99.999" : 1180610.257162428,
- "99.9999" : 1180610.257162428,
- "100.0" : 1180610.257162428
+ "0.0" : 1165914.62029355,
+ "50.0" : 1167305.0975543554,
+ "90.0" : 1167688.1093266667,
+ "95.0" : 1167688.1093266667,
+ "99.0" : 1167688.1093266667,
+ "99.9" : 1167688.1093266667,
+ "99.99" : 1167688.1093266667,
+ "99.999" : 1167688.1093266667,
+ "99.9999" : 1167688.1093266667,
+ "100.0" : 1167688.1093266667
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 1180610.257162428,
- 1164002.9183628866,
- 1174037.1951503523,
- 1170830.4091431957
+ 1165914.62029355,
+ 1167688.1093266667,
+ 1167587.8000121168,
+ 1167022.395096594
]
]
},
@@ -280,7 +392,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -294,41 +406,97 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128b",
"processorName" : "zio-json"
},
"primaryMetric" : {
- "score" : 2191564.9811769784,
- "scoreError" : 20901.204115111937,
+ "score" : 2034331.7853433017,
+ "scoreError" : 45080.59303419907,
+ "scoreConfidence" : [
+ 1989251.1923091027,
+ 2079412.3783775007
+ ],
+ "scorePercentiles" : {
+ "0.0" : 2024697.6544727832,
+ "50.0" : 2035622.184185423,
+ "90.0" : 2041385.1185295777,
+ "95.0" : 2041385.1185295777,
+ "99.0" : 2041385.1185295777,
+ "99.9" : 2041385.1185295777,
+ "99.99" : 2041385.1185295777,
+ "99.999" : 2041385.1185295777,
+ "99.9999" : 2041385.1185295777,
+ "100.0" : 2041385.1185295777
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 2035855.4805261057,
+ 2041385.1185295777,
+ 2035388.8878447404,
+ 2024697.6544727832
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "1kb",
+ "processorName" : "tethys"
+ },
+ "primaryMetric" : {
+ "score" : 549741.974487169,
+ "scoreError" : 18510.860445075177,
"scoreConfidence" : [
- 2170663.7770618666,
- 2212466.1852920903
+ 531231.1140420937,
+ 568252.8349322441
],
"scorePercentiles" : {
- "0.0" : 2187130.045963812,
- "50.0" : 2192139.318299738,
- "90.0" : 2194851.2421446256,
- "95.0" : 2194851.2421446256,
- "99.0" : 2194851.2421446256,
- "99.9" : 2194851.2421446256,
- "99.99" : 2194851.2421446256,
- "99.999" : 2194851.2421446256,
- "99.9999" : 2194851.2421446256,
- "100.0" : 2194851.2421446256
+ "0.0" : 547187.0258438425,
+ "50.0" : 549027.1151022253,
+ "90.0" : 553726.6419003825,
+ "95.0" : 553726.6419003825,
+ "99.0" : 553726.6419003825,
+ "99.9" : 553726.6419003825,
+ "99.99" : 553726.6419003825,
+ "99.999" : 553726.6419003825,
+ "99.9999" : 553726.6419003825,
+ "100.0" : 553726.6419003825
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 2187130.045963812,
- 2192498.331993979,
- 2194851.2421446256,
- 2191780.3046054975
+ 549798.4790281476,
+ 553726.6419003825,
+ 547187.0258438425,
+ 548255.751176303
]
]
},
@@ -336,7 +504,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -350,41 +518,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1kb",
"processorName" : "tethys-jackson"
},
"primaryMetric" : {
- "score" : 403267.4027522539,
- "scoreError" : 43515.31402068551,
+ "score" : 434317.73653196916,
+ "scoreError" : 8887.004496772832,
"scoreConfidence" : [
- 359752.0887315684,
- 446782.7167729394
+ 425430.7320351963,
+ 443204.741028742
],
"scorePercentiles" : {
- "0.0" : 397328.9014945461,
- "50.0" : 402949.1987281769,
- "90.0" : 409842.3120581157,
- "95.0" : 409842.3120581157,
- "99.0" : 409842.3120581157,
- "99.9" : 409842.3120581157,
- "99.99" : 409842.3120581157,
- "99.999" : 409842.3120581157,
- "99.9999" : 409842.3120581157,
- "100.0" : 409842.3120581157
+ "0.0" : 432850.8425543397,
+ "50.0" : 434339.8306456285,
+ "90.0" : 435740.44228227995,
+ "95.0" : 435740.44228227995,
+ "99.0" : 435740.44228227995,
+ "99.9" : 435740.44228227995,
+ "99.99" : 435740.44228227995,
+ "99.999" : 435740.44228227995,
+ "99.9999" : 435740.44228227995,
+ "100.0" : 435740.44228227995
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 397594.5620112011,
- 397328.9014945461,
- 408303.8354451526,
- 409842.3120581157
+ 433474.58847059973,
+ 435740.44228227995,
+ 435205.07282065734,
+ 432850.8425543397
]
]
},
@@ -392,7 +560,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -406,41 +574,97 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "1kb",
+ "processorName" : "jsoniter"
+ },
+ "primaryMetric" : {
+ "score" : 892874.5689568161,
+ "scoreError" : 29854.73803129264,
+ "scoreConfidence" : [
+ 863019.8309255234,
+ 922729.3069881088
+ ],
+ "scorePercentiles" : {
+ "0.0" : 887656.8064194502,
+ "50.0" : 892500.8721891862,
+ "90.0" : 898839.7250294421,
+ "95.0" : 898839.7250294421,
+ "99.0" : 898839.7250294421,
+ "99.9" : 898839.7250294421,
+ "99.99" : 898839.7250294421,
+ "99.999" : 898839.7250294421,
+ "99.9999" : 898839.7250294421,
+ "100.0" : 898839.7250294421
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 898839.7250294421,
+ 887656.8064194502,
+ 893189.0795532095,
+ 891812.6648251629
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1kb",
"processorName" : "pure-jackson"
},
"primaryMetric" : {
- "score" : 427421.9247585598,
- "scoreError" : 24518.728600886658,
+ "score" : 420722.9597510017,
+ "scoreError" : 6008.59118247858,
"scoreConfidence" : [
- 402903.19615767314,
- 451940.65335944644
+ 414714.3685685231,
+ 426731.55093348026
],
"scorePercentiles" : {
- "0.0" : 421863.8499176919,
- "50.0" : 428821.3012850069,
- "90.0" : 430181.24654653337,
- "95.0" : 430181.24654653337,
- "99.0" : 430181.24654653337,
- "99.9" : 430181.24654653337,
- "99.99" : 430181.24654653337,
- "99.999" : 430181.24654653337,
- "99.9999" : 430181.24654653337,
- "100.0" : 430181.24654653337
+ "0.0" : 419381.737651805,
+ "50.0" : 420989.7569802229,
+ "90.0" : 421530.58739175607,
+ "95.0" : 421530.58739175607,
+ "99.0" : 421530.58739175607,
+ "99.9" : 421530.58739175607,
+ "99.99" : 421530.58739175607,
+ "99.999" : 421530.58739175607,
+ "99.9999" : 421530.58739175607,
+ "100.0" : 421530.58739175607
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 428201.79164324555,
- 430181.24654653337,
- 429440.8109267683,
- 421863.8499176919
+ 421530.58739175607,
+ 419381.737651805,
+ 420977.8180895547,
+ 421001.695870891
]
]
},
@@ -448,7 +672,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -462,41 +686,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1kb",
"processorName" : "circe"
},
"primaryMetric" : {
- "score" : 177497.0107319799,
- "scoreError" : 14364.31626843296,
+ "score" : 176604.56851430555,
+ "scoreError" : 5367.441061591579,
"scoreConfidence" : [
- 163132.69446354694,
- 191861.3270004129
+ 171237.12745271396,
+ 181972.00957589713
],
"scorePercentiles" : {
- "0.0" : 174194.40738251232,
- "50.0" : 178388.27788455077,
- "90.0" : 179017.07977630582,
- "95.0" : 179017.07977630582,
- "99.0" : 179017.07977630582,
- "99.9" : 179017.07977630582,
- "99.99" : 179017.07977630582,
- "99.999" : 179017.07977630582,
- "99.9999" : 179017.07977630582,
- "100.0" : 179017.07977630582
+ "0.0" : 175891.7547813067,
+ "50.0" : 176498.65950984042,
+ "90.0" : 177529.2002562346,
+ "95.0" : 177529.2002562346,
+ "99.0" : 177529.2002562346,
+ "99.9" : 177529.2002562346,
+ "99.99" : 177529.2002562346,
+ "99.999" : 177529.2002562346,
+ "99.9999" : 177529.2002562346,
+ "100.0" : 177529.2002562346
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 179017.07977630582,
- 178481.170435525,
- 174194.40738251232,
- 178295.38533357653
+ 175913.72561930353,
+ 175891.7547813067,
+ 177529.2002562346,
+ 177083.59340037729
]
]
},
@@ -504,7 +728,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -518,41 +742,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1kb",
"processorName" : "play-json"
},
"primaryMetric" : {
- "score" : 47736.65476696285,
- "scoreError" : 1345.094348191517,
+ "score" : 48283.89499745317,
+ "scoreError" : 1656.4635042534533,
"scoreConfidence" : [
- 46391.56041877133,
- 49081.749115154365
+ 46627.43149319972,
+ 49940.35850170662
],
"scorePercentiles" : {
- "0.0" : 47555.57243850504,
- "50.0" : 47686.14025700826,
- "90.0" : 48018.76611532986,
- "95.0" : 48018.76611532986,
- "99.0" : 48018.76611532986,
- "99.9" : 48018.76611532986,
- "99.99" : 48018.76611532986,
- "99.999" : 48018.76611532986,
- "99.9999" : 48018.76611532986,
- "100.0" : 48018.76611532986
+ "0.0" : 48041.62315484572,
+ "50.0" : 48229.64055126053,
+ "90.0" : 48634.67573244592,
+ "95.0" : 48634.67573244592,
+ "99.0" : 48634.67573244592,
+ "99.9" : 48634.67573244592,
+ "99.99" : 48634.67573244592,
+ "99.999" : 48634.67573244592,
+ "99.9999" : 48634.67573244592,
+ "100.0" : 48634.67573244592
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 48018.76611532986,
- 47607.06985357856,
- 47765.210660437944,
- 47555.57243850504
+ 48160.72011965533,
+ 48298.56098286572,
+ 48041.62315484572,
+ 48634.67573244592
]
]
},
@@ -560,7 +784,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -574,41 +798,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1kb",
"processorName" : "spray-json"
},
"primaryMetric" : {
- "score" : 148580.55389673484,
- "scoreError" : 7903.956542352092,
+ "score" : 149003.2664377889,
+ "scoreError" : 4034.6402888002817,
"scoreConfidence" : [
- 140676.59735438274,
- 156484.51043908694
+ 144968.6261489886,
+ 153037.9067265892
],
"scorePercentiles" : {
- "0.0" : 147110.3341399147,
- "50.0" : 148799.92883293395,
- "90.0" : 149612.0237811568,
- "95.0" : 149612.0237811568,
- "99.0" : 149612.0237811568,
- "99.9" : 149612.0237811568,
- "99.99" : 149612.0237811568,
- "99.999" : 149612.0237811568,
- "99.9999" : 149612.0237811568,
- "100.0" : 149612.0237811568
+ "0.0" : 148126.13096242023,
+ "50.0" : 149196.69225041344,
+ "90.0" : 149493.5502879085,
+ "95.0" : 149493.5502879085,
+ "99.0" : 149493.5502879085,
+ "99.9" : 149493.5502879085,
+ "99.99" : 149493.5502879085,
+ "99.999" : 149493.5502879085,
+ "99.9999" : 149493.5502879085,
+ "100.0" : 149493.5502879085
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 148036.21042685886,
- 147110.3341399147,
- 149563.64723900906,
- 149612.0237811568
+ 148126.13096242023,
+ 149493.5502879085,
+ 148990.63016748935,
+ 149402.75433333754
]
]
},
@@ -616,7 +840,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -630,41 +854,97 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1kb",
"processorName" : "zio-json"
},
"primaryMetric" : {
- "score" : 266636.44019146083,
- "scoreError" : 3831.5862190856205,
+ "score" : 277147.0123110621,
+ "scoreError" : 10341.200741446326,
+ "scoreConfidence" : [
+ 266805.81156961573,
+ 287488.2130525084
+ ],
+ "scorePercentiles" : {
+ "0.0" : 275522.27501270274,
+ "50.0" : 276875.17110009305,
+ "90.0" : 279315.4320313594,
+ "95.0" : 279315.4320313594,
+ "99.0" : 279315.4320313594,
+ "99.9" : 279315.4320313594,
+ "99.99" : 279315.4320313594,
+ "99.999" : 279315.4320313594,
+ "99.9999" : 279315.4320313594,
+ "100.0" : 279315.4320313594
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 276564.2150378382,
+ 277186.12716234784,
+ 279315.4320313594,
+ 275522.27501270274
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "128kb",
+ "processorName" : "tethys"
+ },
+ "primaryMetric" : {
+ "score" : 4728.468432101119,
+ "scoreError" : 169.23274141134854,
"scoreConfidence" : [
- 262804.8539723752,
- 270468.02641054644
+ 4559.23569068977,
+ 4897.701173512468
],
"scorePercentiles" : {
- "0.0" : 266038.3930441509,
- "50.0" : 266528.46815931366,
- "90.0" : 267450.43140306504,
- "95.0" : 267450.43140306504,
- "99.0" : 267450.43140306504,
- "99.9" : 267450.43140306504,
- "99.99" : 267450.43140306504,
- "99.999" : 267450.43140306504,
- "99.9999" : 267450.43140306504,
- "100.0" : 267450.43140306504
+ "0.0" : 4689.218535377044,
+ "50.0" : 4740.948460607137,
+ "90.0" : 4742.758271813157,
+ "95.0" : 4742.758271813157,
+ "99.0" : 4742.758271813157,
+ "99.9" : 4742.758271813157,
+ "99.99" : 4742.758271813157,
+ "99.999" : 4742.758271813157,
+ "99.9999" : 4742.758271813157,
+ "100.0" : 4742.758271813157
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 267450.43140306504,
- 266453.6905350327,
- 266603.24578359467,
- 266038.3930441509
+ 4689.218535377044,
+ 4742.758271813157,
+ 4741.763264207704,
+ 4740.1336570065705
]
]
},
@@ -672,7 +952,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -686,41 +966,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128kb",
"processorName" : "tethys-jackson"
},
"primaryMetric" : {
- "score" : 3265.018485513807,
- "scoreError" : 211.52248659255656,
+ "score" : 3577.79887805201,
+ "scoreError" : 26.18467106574972,
"scoreConfidence" : [
- 3053.4959989212502,
- 3476.5409721063634
+ 3551.6142069862603,
+ 3603.9835491177596
],
"scorePercentiles" : {
- "0.0" : 3220.6314731712628,
- "50.0" : 3274.0272158278585,
- "90.0" : 3291.388037228249,
- "95.0" : 3291.388037228249,
- "99.0" : 3291.388037228249,
- "99.9" : 3291.388037228249,
- "99.99" : 3291.388037228249,
- "99.999" : 3291.388037228249,
- "99.9999" : 3291.388037228249,
- "100.0" : 3291.388037228249
+ "0.0" : 3572.0979436748175,
+ "50.0" : 3579.15086827506,
+ "90.0" : 3580.795831983102,
+ "95.0" : 3580.795831983102,
+ "99.0" : 3580.795831983102,
+ "99.9" : 3580.795831983102,
+ "99.99" : 3580.795831983102,
+ "99.999" : 3580.795831983102,
+ "99.9999" : 3580.795831983102,
+ "100.0" : 3580.795831983102
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 3260.1250600835074,
- 3220.6314731712628,
- 3291.388037228249,
- 3287.929371572209
+ 3577.715499233423,
+ 3580.795831983102,
+ 3580.5862373166974,
+ 3572.0979436748175
]
]
},
@@ -728,7 +1008,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -742,41 +1022,97 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "128kb",
+ "processorName" : "jsoniter"
+ },
+ "primaryMetric" : {
+ "score" : 5576.712066017241,
+ "scoreError" : 56.87910417352511,
+ "scoreConfidence" : [
+ 5519.8329618437165,
+ 5633.591170190766
+ ],
+ "scorePercentiles" : {
+ "0.0" : 5569.143715740925,
+ "50.0" : 5574.144810654081,
+ "90.0" : 5589.414927019879,
+ "95.0" : 5589.414927019879,
+ "99.0" : 5589.414927019879,
+ "99.9" : 5589.414927019879,
+ "99.99" : 5589.414927019879,
+ "99.999" : 5589.414927019879,
+ "99.9999" : 5589.414927019879,
+ "100.0" : 5589.414927019879
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 5574.6956819036295,
+ 5573.593939404532,
+ 5589.414927019879,
+ 5569.143715740925
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128kb",
"processorName" : "pure-jackson"
},
"primaryMetric" : {
- "score" : 3096.090303694719,
- "scoreError" : 114.1578488303212,
+ "score" : 3177.920949665991,
+ "scoreError" : 58.74788909478107,
"scoreConfidence" : [
- 2981.9324548643976,
- 3210.24815252504
+ 3119.17306057121,
+ 3236.668838760772
],
"scorePercentiles" : {
- "0.0" : 3075.4612594160444,
- "50.0" : 3098.1337498176445,
- "90.0" : 3112.6324557275416,
- "95.0" : 3112.6324557275416,
- "99.0" : 3112.6324557275416,
- "99.9" : 3112.6324557275416,
- "99.99" : 3112.6324557275416,
- "99.999" : 3112.6324557275416,
- "99.9999" : 3112.6324557275416,
- "100.0" : 3112.6324557275416
+ "0.0" : 3169.9666905993176,
+ "50.0" : 3176.9266137796594,
+ "90.0" : 3187.8638805053274,
+ "95.0" : 3187.8638805053274,
+ "99.0" : 3187.8638805053274,
+ "99.9" : 3187.8638805053274,
+ "99.99" : 3187.8638805053274,
+ "99.999" : 3187.8638805053274,
+ "99.9999" : 3187.8638805053274,
+ "100.0" : 3187.8638805053274
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 3108.8275968787652,
- 3075.4612594160444,
- 3087.439902756524,
- 3112.6324557275416
+ 3170.4518266905884,
+ 3187.8638805053274,
+ 3183.40140086873,
+ 3169.9666905993176
]
]
},
@@ -784,7 +1120,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -798,41 +1134,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128kb",
"processorName" : "circe"
},
"primaryMetric" : {
- "score" : 1255.0932751513285,
- "scoreError" : 46.22591384242437,
+ "score" : 1279.9729785390234,
+ "scoreError" : 20.191937356816076,
"scoreConfidence" : [
- 1208.867361308904,
- 1301.319188993753
+ 1259.7810411822074,
+ 1300.1649158958394
],
"scorePercentiles" : {
- "0.0" : 1244.6630457421552,
- "50.0" : 1257.3824405600344,
- "90.0" : 1260.9451737430902,
- "95.0" : 1260.9451737430902,
- "99.0" : 1260.9451737430902,
- "99.9" : 1260.9451737430902,
- "99.99" : 1260.9451737430902,
- "99.999" : 1260.9451737430902,
- "99.9999" : 1260.9451737430902,
- "100.0" : 1260.9451737430902
+ "0.0" : 1275.6939320168879,
+ "50.0" : 1280.6173242904092,
+ "90.0" : 1282.9633335583874,
+ "95.0" : 1282.9633335583874,
+ "99.0" : 1282.9633335583874,
+ "99.9" : 1282.9633335583874,
+ "99.99" : 1282.9633335583874,
+ "99.999" : 1282.9633335583874,
+ "99.9999" : 1282.9633335583874,
+ "100.0" : 1282.9633335583874
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 1257.4225286709386,
- 1257.34235244913,
- 1260.9451737430902,
- 1244.6630457421552
+ 1275.6939320168879,
+ 1281.394784055471,
+ 1282.9633335583874,
+ 1279.8398645253476
]
]
},
@@ -840,7 +1176,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -854,41 +1190,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128kb",
"processorName" : "play-json"
},
"primaryMetric" : {
- "score" : 360.73397668574137,
- "scoreError" : 2.9849183809715094,
+ "score" : 375.69147856459927,
+ "scoreError" : 26.87803980719791,
"scoreConfidence" : [
- 357.74905830476985,
- 363.7188950667129
+ 348.81343875740134,
+ 402.5695183717972
],
"scorePercentiles" : {
- "0.0" : 360.1643871524679,
- "50.0" : 360.74936066459566,
- "90.0" : 361.27279826130626,
- "95.0" : 361.27279826130626,
- "99.0" : 361.27279826130626,
- "99.9" : 361.27279826130626,
- "99.99" : 361.27279826130626,
- "99.999" : 361.27279826130626,
- "99.9999" : 361.27279826130626,
- "100.0" : 361.27279826130626
+ "0.0" : 369.66421529142144,
+ "50.0" : 377.01959904759076,
+ "90.0" : 379.062500871794,
+ "95.0" : 379.062500871794,
+ "99.0" : 379.062500871794,
+ "99.9" : 379.062500871794,
+ "99.99" : 379.062500871794,
+ "99.999" : 379.062500871794,
+ "99.9999" : 379.062500871794,
+ "100.0" : 379.062500871794
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 360.86088286252544,
- 361.27279826130626,
- 360.6378384666658,
- 360.1643871524679
+ 377.6037578935196,
+ 376.43544020166183,
+ 379.062500871794,
+ 369.66421529142144
]
]
},
@@ -896,7 +1232,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -910,41 +1246,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128kb",
"processorName" : "spray-json"
},
"primaryMetric" : {
- "score" : 1053.7816934132636,
- "scoreError" : 17.98846836879526,
+ "score" : 1109.5044338432704,
+ "scoreError" : 14.324446676367586,
"scoreConfidence" : [
- 1035.7932250444683,
- 1071.770161782059
+ 1095.1799871669027,
+ 1123.828880519638
],
"scorePercentiles" : {
- "0.0" : 1051.7004653852352,
- "50.0" : 1052.8498041712742,
- "90.0" : 1057.726699925271,
- "95.0" : 1057.726699925271,
- "99.0" : 1057.726699925271,
- "99.9" : 1057.726699925271,
- "99.99" : 1057.726699925271,
- "99.999" : 1057.726699925271,
- "99.9999" : 1057.726699925271,
- "100.0" : 1057.726699925271
+ "0.0" : 1108.0686828247212,
+ "50.0" : 1108.6025725651468,
+ "90.0" : 1112.7439074180666,
+ "95.0" : 1112.7439074180666,
+ "99.0" : 1112.7439074180666,
+ "99.9" : 1112.7439074180666,
+ "99.99" : 1112.7439074180666,
+ "99.999" : 1112.7439074180666,
+ "99.9999" : 1112.7439074180666,
+ "100.0" : 1112.7439074180666
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 1057.726699925271,
- 1053.7487275819278,
- 1051.9508807606203,
- 1051.7004653852352
+ 1108.0737445954674,
+ 1108.0686828247212,
+ 1109.1314005348263,
+ 1112.7439074180666
]
]
},
@@ -952,7 +1288,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -966,41 +1302,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "128kb",
"processorName" : "zio-json"
},
"primaryMetric" : {
- "score" : 2092.4795061467858,
- "scoreError" : 3.4526544775530565,
+ "score" : 1934.2735060465438,
+ "scoreError" : 145.3142718068349,
"scoreConfidence" : [
- 2089.0268516692327,
- 2095.932160624339
+ 1788.9592342397088,
+ 2079.5877778533786
],
"scorePercentiles" : {
- "0.0" : 2092.002062716033,
- "50.0" : 2092.3633355152065,
- "90.0" : 2093.189290840698,
- "95.0" : 2093.189290840698,
- "99.0" : 2093.189290840698,
- "99.9" : 2093.189290840698,
- "99.99" : 2093.189290840698,
- "99.999" : 2093.189290840698,
- "99.9999" : 2093.189290840698,
- "100.0" : 2093.189290840698
+ "0.0" : 1900.5575849264953,
+ "50.0" : 1945.0995448027188,
+ "90.0" : 1946.3373496542426,
+ "95.0" : 1946.3373496542426,
+ "99.0" : 1946.3373496542426,
+ "99.9" : 1946.3373496542426,
+ "99.99" : 1946.3373496542426,
+ "99.999" : 1946.3373496542426,
+ "99.9999" : 1946.3373496542426,
+ "100.0" : 1946.3373496542426
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 2092.142316922251,
- 2093.189290840698,
- 2092.5843541081617,
- 2092.002062716033
+ 1945.5256149916627,
+ 1946.3373496542426,
+ 1944.6734746137747,
+ 1900.5575849264953
]
]
},
@@ -1008,7 +1344,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1022,41 +1358,153 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "1mb",
+ "processorName" : "tethys"
+ },
+ "primaryMetric" : {
+ "score" : 582.9115264911206,
+ "scoreError" : 3.036982655418544,
+ "scoreConfidence" : [
+ 579.8745438357021,
+ 585.9485091465392
+ ],
+ "scorePercentiles" : {
+ "0.0" : 582.5104232647693,
+ "50.0" : 582.7768370434262,
+ "90.0" : 583.5820086128612,
+ "95.0" : 583.5820086128612,
+ "99.0" : 583.5820086128612,
+ "99.9" : 583.5820086128612,
+ "99.99" : 583.5820086128612,
+ "99.999" : 583.5820086128612,
+ "99.9999" : 583.5820086128612,
+ "100.0" : 583.5820086128612
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 582.687615523413,
+ 582.8660585634393,
+ 583.5820086128612,
+ 582.5104232647693
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1mb",
"processorName" : "tethys-jackson"
},
"primaryMetric" : {
- "score" : 300.50175605365195,
- "scoreError" : 16.628260772796597,
+ "score" : 499.2126793098415,
+ "scoreError" : 16.65777230462521,
+ "scoreConfidence" : [
+ 482.5549070052163,
+ 515.8704516144667
+ ],
+ "scorePercentiles" : {
+ "0.0" : 495.5221775965387,
+ "50.0" : 500.12397993581743,
+ "90.0" : 501.08057977119233,
+ "95.0" : 501.08057977119233,
+ "99.0" : 501.08057977119233,
+ "99.9" : 501.08057977119233,
+ "99.99" : 501.08057977119233,
+ "99.999" : 501.08057977119233,
+ "99.9999" : 501.08057977119233,
+ "100.0" : 501.08057977119233
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 495.5221775965387,
+ 499.36059915298074,
+ 501.08057977119233,
+ 500.8873607186541
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "1mb",
+ "processorName" : "jsoniter"
+ },
+ "primaryMetric" : {
+ "score" : 727.8444320522649,
+ "scoreError" : 5.4201652188762965,
"scoreConfidence" : [
- 283.87349528085537,
- 317.1300168264485
+ 722.4242668333886,
+ 733.2645972711412
],
"scorePercentiles" : {
- "0.0" : 296.7270976551244,
- "50.0" : 301.46267892494234,
- "90.0" : 302.35456870959865,
- "95.0" : 302.35456870959865,
- "99.0" : 302.35456870959865,
- "99.9" : 302.35456870959865,
- "99.99" : 302.35456870959865,
- "99.999" : 302.35456870959865,
- "99.9999" : 302.35456870959865,
- "100.0" : 302.35456870959865
+ "0.0" : 726.9083542675232,
+ "50.0" : 727.7621565394252,
+ "90.0" : 728.9450608626861,
+ "95.0" : 728.9450608626861,
+ "99.0" : 728.9450608626861,
+ "99.9" : 728.9450608626861,
+ "99.99" : 728.9450608626861,
+ "99.999" : 728.9450608626861,
+ "99.9999" : 728.9450608626861,
+ "100.0" : 728.9450608626861
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 302.35456870959865,
- 301.0521833779614,
- 301.87317447192333,
- 296.7270976551244
+ 727.8309841229437,
+ 727.6933289559067,
+ 728.9450608626861,
+ 726.9083542675232
]
]
},
@@ -1064,7 +1512,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1078,41 +1526,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1mb",
"processorName" : "pure-jackson"
},
"primaryMetric" : {
- "score" : 392.0619134376386,
- "scoreError" : 1.6351377127176903,
+ "score" : 394.10021533585757,
+ "scoreError" : 4.34839951102803,
"scoreConfidence" : [
- 390.42677572492096,
- 393.6970511503563
+ 389.7518158248295,
+ 398.4486148468856
],
"scorePercentiles" : {
- "0.0" : 391.8480127263682,
- "50.0" : 391.98575996613056,
- "90.0" : 392.42812109192516,
- "95.0" : 392.42812109192516,
- "99.0" : 392.42812109192516,
- "99.9" : 392.42812109192516,
- "99.99" : 392.42812109192516,
- "99.999" : 392.42812109192516,
- "99.9999" : 392.42812109192516,
- "100.0" : 392.42812109192516
+ "0.0" : 393.5074744682836,
+ "50.0" : 393.93577760972846,
+ "90.0" : 395.0218316556898,
+ "95.0" : 395.0218316556898,
+ "99.0" : 395.0218316556898,
+ "99.9" : 395.0218316556898,
+ "99.99" : 395.0218316556898,
+ "99.999" : 395.0218316556898,
+ "99.9999" : 395.0218316556898,
+ "100.0" : 395.0218316556898
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 392.00345893627724,
- 391.96806099598393,
- 392.42812109192516,
- 391.8480127263682
+ 393.5074744682836,
+ 394.16344469985165,
+ 395.0218316556898,
+ 393.7081105196052
]
]
},
@@ -1120,7 +1568,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1134,41 +1582,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1mb",
"processorName" : "circe"
},
"primaryMetric" : {
- "score" : 157.51845044919145,
- "scoreError" : 0.680659832435363,
+ "score" : 156.18364150517357,
+ "scoreError" : 3.967924463335569,
"scoreConfidence" : [
- 156.83779061675608,
- 158.19911028162682
+ 152.215717041838,
+ 160.15156596850915
],
"scorePercentiles" : {
- "0.0" : 157.41259901009516,
- "50.0" : 157.51990966919712,
- "90.0" : 157.62138344827642,
- "95.0" : 157.62138344827642,
- "99.0" : 157.62138344827642,
- "99.9" : 157.62138344827642,
- "99.99" : 157.62138344827642,
- "99.999" : 157.62138344827642,
- "99.9999" : 157.62138344827642,
- "100.0" : 157.62138344827642
+ "0.0" : 155.33978014796764,
+ "50.0" : 156.33363491224395,
+ "90.0" : 156.72751604823878,
+ "95.0" : 156.72751604823878,
+ "99.0" : 156.72751604823878,
+ "99.9" : 156.72751604823878,
+ "99.99" : 156.72751604823878,
+ "99.999" : 156.72751604823878,
+ "99.9999" : 156.72751604823878,
+ "100.0" : 156.72751604823878
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 157.44414342450114,
- 157.62138344827642,
- 157.5956759138931,
- 157.41259901009516
+ 155.33978014796764,
+ 156.13584663979125,
+ 156.72751604823878,
+ 156.53142318469662
]
]
},
@@ -1176,7 +1624,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1190,41 +1638,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1mb",
"processorName" : "play-json"
},
"primaryMetric" : {
- "score" : 49.41659582197183,
- "scoreError" : 4.128650672278151,
+ "score" : 49.42348078315202,
+ "scoreError" : 4.458763358666092,
"scoreConfidence" : [
- 45.287945149693684,
- 53.54524649424998
+ 44.96471742448593,
+ 53.88224414181811
],
"scorePercentiles" : {
- "0.0" : 48.76101535916496,
- "50.0" : 49.306450172914005,
- "90.0" : 50.29246758289436,
- "95.0" : 50.29246758289436,
- "99.0" : 50.29246758289436,
- "99.9" : 50.29246758289436,
- "99.99" : 50.29246758289436,
- "99.999" : 50.29246758289436,
- "99.9999" : 50.29246758289436,
- "100.0" : 50.29246758289436
+ "0.0" : 48.56102653054879,
+ "50.0" : 49.49864645891251,
+ "90.0" : 50.135603684234304,
+ "95.0" : 50.135603684234304,
+ "99.0" : 50.135603684234304,
+ "99.9" : 50.135603684234304,
+ "99.99" : 50.135603684234304,
+ "99.999" : 50.135603684234304,
+ "99.9999" : 50.135603684234304,
+ "100.0" : 50.135603684234304
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 49.34785823381571,
- 50.29246758289436,
- 48.76101535916496,
- 49.2650421120123
+ 50.135603684234304,
+ 48.56102653054879,
+ 49.78678526110603,
+ 49.21050765671899
]
]
},
@@ -1232,7 +1680,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1246,41 +1694,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1mb",
"processorName" : "spray-json"
},
"primaryMetric" : {
- "score" : 142.1640514746978,
- "scoreError" : 1.4225526580210257,
+ "score" : 138.30900400088186,
+ "scoreError" : 2.3224502928310424,
"scoreConfidence" : [
- 140.74149881667677,
- 143.58660413271883
+ 135.98655370805082,
+ 140.6314542937129
],
"scorePercentiles" : {
- "0.0" : 141.90393517724456,
- "50.0" : 142.1560858061505,
- "90.0" : 142.4400991092456,
- "95.0" : 142.4400991092456,
- "99.0" : 142.4400991092456,
- "99.9" : 142.4400991092456,
- "99.99" : 142.4400991092456,
- "99.999" : 142.4400991092456,
- "99.9999" : 142.4400991092456,
- "100.0" : 142.4400991092456
+ "0.0" : 137.8065551308592,
+ "50.0" : 138.3862375523468,
+ "90.0" : 138.65698576797462,
+ "95.0" : 138.65698576797462,
+ "99.0" : 138.65698576797462,
+ "99.9" : 138.65698576797462,
+ "99.99" : 138.65698576797462,
+ "99.999" : 138.65698576797462,
+ "99.9999" : 138.65698576797462,
+ "100.0" : 138.65698576797462
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 142.18251762104686,
- 142.4400991092456,
- 141.90393517724456,
- 142.12965399125414
+ 138.65698576797462,
+ 138.4181173369827,
+ 137.8065551308592,
+ 138.35435776771092
]
]
},
@@ -1288,7 +1736,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1302,41 +1750,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "1mb",
"processorName" : "zio-json"
},
"primaryMetric" : {
- "score" : 239.59825470502108,
- "scoreError" : 12.090993885755951,
+ "score" : 238.86175823829703,
+ "scoreError" : 0.6522379606193902,
"scoreConfidence" : [
- 227.50726081926513,
- 251.68924859077703
+ 238.20952027767763,
+ 239.51399619891643
],
"scorePercentiles" : {
- "0.0" : 237.33856097367186,
- "50.0" : 239.95747440052935,
- "90.0" : 241.13950904535375,
- "95.0" : 241.13950904535375,
- "99.0" : 241.13950904535375,
- "99.9" : 241.13950904535375,
- "99.99" : 241.13950904535375,
- "99.999" : 241.13950904535375,
- "99.9999" : 241.13950904535375,
- "100.0" : 241.13950904535375
+ "0.0" : 238.71235571423296,
+ "50.0" : 238.9030442351787,
+ "90.0" : 238.92858876859776,
+ "95.0" : 238.92858876859776,
+ "99.0" : 238.92858876859776,
+ "99.9" : 238.92858876859776,
+ "99.99" : 238.92858876859776,
+ "99.999" : 238.92858876859776,
+ "99.9999" : 238.92858876859776,
+ "100.0" : 238.92858876859776
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 241.1328951147916,
- 241.13950904535375,
- 238.78205368626712,
- 237.33856097367186
+ 238.71235571423296,
+ 238.88950084360954,
+ 238.92858876859776,
+ 238.91658762674786
]
]
},
@@ -1344,7 +1792,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1358,41 +1806,153 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "32mb",
+ "processorName" : "tethys"
+ },
+ "primaryMetric" : {
+ "score" : 16.222819445110638,
+ "scoreError" : 0.12297511014609462,
+ "scoreConfidence" : [
+ 16.099844334964544,
+ 16.34579455525673
+ ],
+ "scorePercentiles" : {
+ "0.0" : 16.197468097538295,
+ "50.0" : 16.226136279571865,
+ "90.0" : 16.24153712376053,
+ "95.0" : 16.24153712376053,
+ "99.0" : 16.24153712376053,
+ "99.9" : 16.24153712376053,
+ "99.99" : 16.24153712376053,
+ "99.999" : 16.24153712376053,
+ "99.9999" : 16.24153712376053,
+ "100.0" : 16.24153712376053
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 16.197468097538295,
+ 16.24153712376053,
+ 16.220159716115297,
+ 16.232112843028432
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "32mb",
"processorName" : "tethys-jackson"
},
"primaryMetric" : {
- "score" : 11.786317004742862,
- "scoreError" : 0.14575827384743734,
+ "score" : 11.343010380444237,
+ "scoreError" : 0.15542330497021425,
+ "scoreConfidence" : [
+ 11.187587075474024,
+ 11.49843368541445
+ ],
+ "scorePercentiles" : {
+ "0.0" : 11.322620898539569,
+ "50.0" : 11.337904510310192,
+ "90.0" : 11.373611602616997,
+ "95.0" : 11.373611602616997,
+ "99.0" : 11.373611602616997,
+ "99.9" : 11.373611602616997,
+ "99.99" : 11.373611602616997,
+ "99.999" : 11.373611602616997,
+ "99.9999" : 11.373611602616997,
+ "100.0" : 11.373611602616997
+ },
+ "scoreUnit" : "ops/s",
+ "rawData" : [
+ [
+ 11.350772692027288,
+ 11.325036328593098,
+ 11.373611602616997,
+ 11.322620898539569
+ ]
+ ]
+ },
+ "secondaryMetrics" : {
+ }
+ },
+ {
+ "jmhVersion" : "1.37",
+ "benchmark" : "json.bench.JmhWriterBench.bench",
+ "mode" : "thrpt",
+ "threads" : 1,
+ "forks" : 1,
+ "jvm" : "/Users/gosha/Library/Java/JavaVirtualMachines/corretto-17.0.7/Contents/Home/bin/java",
+ "jvmArgs" : [
+ "-Xms1G",
+ "-Xmx1G"
+ ],
+ "jdkVersion" : "17.0.7",
+ "vmName" : "OpenJDK 64-Bit Server VM",
+ "vmVersion" : "17.0.7+7-LTS",
+ "warmupIterations" : 4,
+ "warmupTime" : "1 s",
+ "warmupBatchSize" : 1,
+ "measurementIterations" : 4,
+ "measurementTime" : "1 s",
+ "measurementBatchSize" : 1,
+ "params" : {
+ "jsonSize" : "32mb",
+ "processorName" : "jsoniter"
+ },
+ "primaryMetric" : {
+ "score" : 19.49130674162078,
+ "scoreError" : 0.06554510920029963,
"scoreConfidence" : [
- 11.640558730895425,
- 11.9320752785903
+ 19.42576163242048,
+ 19.55685185082108
],
"scorePercentiles" : {
- "0.0" : 11.758114656066427,
- "50.0" : 11.78703829894285,
- "90.0" : 11.81307676501932,
- "95.0" : 11.81307676501932,
- "99.0" : 11.81307676501932,
- "99.9" : 11.81307676501932,
- "99.99" : 11.81307676501932,
- "99.999" : 11.81307676501932,
- "99.9999" : 11.81307676501932,
- "100.0" : 11.81307676501932
+ "0.0" : 19.48264722596688,
+ "50.0" : 19.488354876861465,
+ "90.0" : 19.505869986793307,
+ "95.0" : 19.505869986793307,
+ "99.0" : 19.505869986793307,
+ "99.9" : 19.505869986793307,
+ "99.99" : 19.505869986793307,
+ "99.999" : 19.505869986793307,
+ "99.9999" : 19.505869986793307,
+ "100.0" : 19.505869986793307
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 11.758114656066427,
- 11.78967041235971,
- 11.784406185525992,
- 11.81307676501932
+ 19.486914415176994,
+ 19.505869986793307,
+ 19.48264722596688,
+ 19.489795338545935
]
]
},
@@ -1400,7 +1960,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1414,41 +1974,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "32mb",
"processorName" : "pure-jackson"
},
"primaryMetric" : {
- "score" : 10.441401348799962,
- "scoreError" : 0.4257042267370256,
+ "score" : 9.97226934026093,
+ "scoreError" : 0.14099093351999584,
"scoreConfidence" : [
- 10.015697122062937,
- 10.867105575536987
+ 9.831278406740934,
+ 10.113260273780925
],
"scorePercentiles" : {
- "0.0" : 10.349816758614011,
- "50.0" : 10.460552010194798,
- "90.0" : 10.494684616196244,
- "95.0" : 10.494684616196244,
- "99.0" : 10.494684616196244,
- "99.9" : 10.494684616196244,
- "99.99" : 10.494684616196244,
- "99.999" : 10.494684616196244,
- "99.9999" : 10.494684616196244,
- "100.0" : 10.494684616196244
+ "0.0" : 9.943063947678294,
+ "50.0" : 9.978283708721236,
+ "90.0" : 9.98944599592296,
+ "95.0" : 9.98944599592296,
+ "99.0" : 9.98944599592296,
+ "99.9" : 9.98944599592296,
+ "99.99" : 9.98944599592296,
+ "99.999" : 9.98944599592296,
+ "99.9999" : 9.98944599592296,
+ "100.0" : 9.98944599592296
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 10.494684616196244,
- 10.437536011173183,
- 10.483568009216413,
- 10.349816758614011
+ 9.988476929796445,
+ 9.943063947678294,
+ 9.968090487646025,
+ 9.98944599592296
]
]
},
@@ -1456,7 +2016,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1470,41 +2030,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "32mb",
"processorName" : "circe"
},
"primaryMetric" : {
- "score" : 2.7059660558815626,
- "scoreError" : 0.5529906205263425,
+ "score" : 2.1917481848188074,
+ "scoreError" : 0.19753919868090142,
"scoreConfidence" : [
- 2.15297543535522,
- 3.258956676407905
+ 1.994208986137906,
+ 2.389287383499709
],
"scorePercentiles" : {
- "0.0" : 2.5821394278212177,
- "50.0" : 2.7386367255520128,
- "90.0" : 2.7644513446010057,
- "95.0" : 2.7644513446010057,
- "99.0" : 2.7644513446010057,
- "99.9" : 2.7644513446010057,
- "99.99" : 2.7644513446010057,
- "99.999" : 2.7644513446010057,
- "99.9999" : 2.7644513446010057,
- "100.0" : 2.7644513446010057
+ "0.0" : 2.161174770863264,
+ "50.0" : 2.1931165391649596,
+ "90.0" : 2.2195848900820465,
+ "95.0" : 2.2195848900820465,
+ "99.0" : 2.2195848900820465,
+ "99.9" : 2.2195848900820465,
+ "99.99" : 2.2195848900820465,
+ "99.999" : 2.2195848900820465,
+ "99.9999" : 2.2195848900820465,
+ "100.0" : 2.2195848900820465
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 2.715383754518447,
- 2.5821394278212177,
- 2.7618896965855786,
- 2.7644513446010057
+ 2.2164629075394187,
+ 2.161174770863264,
+ 2.169770170790501,
+ 2.2195848900820465
]
]
},
@@ -1512,7 +2072,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1526,41 +2086,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "32mb",
"processorName" : "play-json"
},
"primaryMetric" : {
- "score" : 1.0644466719884713,
- "scoreError" : 0.10828100329407624,
+ "score" : 1.0653568016661057,
+ "scoreError" : 0.10740651196183097,
"scoreConfidence" : [
- 0.9561656686943951,
- 1.1727276752825475
+ 0.9579502897042748,
+ 1.1727633136279367
],
"scorePercentiles" : {
- "0.0" : 1.0488479596654356,
- "50.0" : 1.0605586721042153,
- "90.0" : 1.0878213840800195,
- "95.0" : 1.0878213840800195,
- "99.0" : 1.0878213840800195,
- "99.9" : 1.0878213840800195,
- "99.99" : 1.0878213840800195,
- "99.999" : 1.0878213840800195,
- "99.9999" : 1.0878213840800195,
- "100.0" : 1.0878213840800195
+ "0.0" : 1.0433927257114266,
+ "50.0" : 1.0689022836621562,
+ "90.0" : 1.0802299136286833,
+ "95.0" : 1.0802299136286833,
+ "99.0" : 1.0802299136286833,
+ "99.9" : 1.0802299136286833,
+ "99.99" : 1.0802299136286833,
+ "99.999" : 1.0802299136286833,
+ "99.9999" : 1.0802299136286833,
+ "100.0" : 1.0802299136286833
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 1.0639069080349006,
- 1.0488479596654356,
- 1.05721043617353,
- 1.0878213840800195
+ 1.0802299136286833,
+ 1.0759743955128906,
+ 1.0618301718114218,
+ 1.0433927257114266
]
]
},
@@ -1568,7 +2128,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1582,41 +2142,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "32mb",
"processorName" : "spray-json"
},
"primaryMetric" : {
- "score" : 3.4253825528465027,
- "scoreError" : 0.39600842164497063,
+ "score" : 2.949846107789613,
+ "scoreError" : 1.2234104146252418,
"scoreConfidence" : [
- 3.029374131201532,
- 3.8213909744914734
+ 1.7264356931643714,
+ 4.173256522414855
],
"scorePercentiles" : {
- "0.0" : 3.385092868844048,
- "50.0" : 3.400474031886585,
- "90.0" : 3.5154892787687935,
- "95.0" : 3.5154892787687935,
- "99.0" : 3.5154892787687935,
- "99.9" : 3.5154892787687935,
- "99.99" : 3.5154892787687935,
- "99.999" : 3.5154892787687935,
- "99.9999" : 3.5154892787687935,
- "100.0" : 3.5154892787687935
+ "0.0" : 2.668916069460295,
+ "50.0" : 3.0243580598737383,
+ "90.0" : 3.0817522419506798,
+ "95.0" : 3.0817522419506798,
+ "99.0" : 3.0817522419506798,
+ "99.9" : 3.0817522419506798,
+ "99.99" : 3.0817522419506798,
+ "99.999" : 3.0817522419506798,
+ "99.9999" : 3.0817522419506798,
+ "100.0" : 3.0817522419506798
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 3.5154892787687935,
- 3.385092868844048,
- 3.4123772610553913,
- 3.388570802717778
+ 3.0817522419506798,
+ 3.017081868987934,
+ 2.668916069460295,
+ 3.031634250759543
]
]
},
@@ -1624,7 +2184,7 @@
}
},
{
- "jmhVersion" : "1.36",
+ "jmhVersion" : "1.37",
"benchmark" : "json.bench.JmhWriterBench.bench",
"mode" : "thrpt",
"threads" : 1,
@@ -1638,41 +2198,41 @@
"vmName" : "OpenJDK 64-Bit Server VM",
"vmVersion" : "17.0.7+7-LTS",
"warmupIterations" : 4,
- "warmupTime" : "5 s",
+ "warmupTime" : "1 s",
"warmupBatchSize" : 1,
"measurementIterations" : 4,
- "measurementTime" : "5 s",
+ "measurementTime" : "1 s",
"measurementBatchSize" : 1,
"params" : {
"jsonSize" : "32mb",
"processorName" : "zio-json"
},
"primaryMetric" : {
- "score" : 7.347089488260391,
- "scoreError" : 0.025160682828075858,
+ "score" : 7.332264236852336,
+ "scoreError" : 0.0252345687851438,
"scoreConfidence" : [
- 7.321928805432315,
- 7.372250171088466
+ 7.307029668067193,
+ 7.35749880563748
],
"scorePercentiles" : {
- "0.0" : 7.344136033159427,
- "50.0" : 7.345870502020608,
- "90.0" : 7.35248091584092,
- "95.0" : 7.35248091584092,
- "99.0" : 7.35248091584092,
- "99.9" : 7.35248091584092,
- "99.99" : 7.35248091584092,
- "99.999" : 7.35248091584092,
- "99.9999" : 7.35248091584092,
- "100.0" : 7.35248091584092
+ "0.0" : 7.32926086007502,
+ "50.0" : 7.330910436590052,
+ "90.0" : 7.337975214154221,
+ "95.0" : 7.337975214154221,
+ "99.0" : 7.337975214154221,
+ "99.9" : 7.337975214154221,
+ "99.99" : 7.337975214154221,
+ "99.999" : 7.337975214154221,
+ "99.9999" : 7.337975214154221,
+ "100.0" : 7.337975214154221
},
"scoreUnit" : "ops/s",
"rawData" : [
[
- 7.344334413439188,
- 7.347406590602027,
- 7.35248091584092,
- 7.344136033159427
+ 7.330437252894339,
+ 7.337975214154221,
+ 7.32926086007502,
+ 7.331383620285766
]
]
},
diff --git a/modules/benchmarks/src/main/scala-2/json/bench/tethysjson/TethysBench.scala b/modules/benchmarks/src/main/scala-2/json/bench/tethysjson/TethysBench.scala
index ecb5e3b6..8cc4c84f 100644
--- a/modules/benchmarks/src/main/scala-2/json/bench/tethysjson/TethysBench.scala
+++ b/modules/benchmarks/src/main/scala-2/json/bench/tethysjson/TethysBench.scala
@@ -7,13 +7,21 @@ import tethys.jackson._
object TethysBench {
- implicit val dataWriter: JsonWriter[Data] = tethys.derivation.semiauto.jsonWriter[Data]
- implicit val dataReader: JsonReader[Data] = tethys.derivation.semiauto.jsonReader[Data]
+ implicit val dataWriter: JsonWriter[Data] =
+ tethys.derivation.semiauto.jsonWriter[Data]
+ implicit val dataReader: JsonReader[Data] =
+ tethys.derivation.semiauto.jsonReader[Data]
+
+ object TethysDataProcessor extends DataWriter with DataReader {
+ override def write(seq: Seq[Data]): String = seq.asJson
+ override def read(json: String): Seq[Data] = ???
+ }
object TethysJacksonDataProcessor extends DataWriter with DataReader {
override def write(seq: Seq[Data]): String = seq.asJson
- override def read(json: String): Seq[Data] = json.jsonAs[Seq[Data]].toOption.get
+ override def read(json: String): Seq[Data] =
+ json.jsonAs[Seq[Data]].toOption.get
}
}
diff --git a/modules/benchmarks/src/main/scala-3/json/bench/tethysjson/TethysBench.scala b/modules/benchmarks/src/main/scala-3/json/bench/tethysjson/TethysBench.scala
index 18a22c21..bee6d861 100644
--- a/modules/benchmarks/src/main/scala-3/json/bench/tethysjson/TethysBench.scala
+++ b/modules/benchmarks/src/main/scala-3/json/bench/tethysjson/TethysBench.scala
@@ -2,21 +2,32 @@ package json.bench.tethysjson
import json.bench.model.Data
import json.bench.{DataReader, DataWriter}
-import tethys.*
-import tethys.jackson.*
+import tethys._
-/**
- * Created by eld0727 on 21.04.17.
+/** Created by eld0727 on 21.04.17.
*/
object TethysBench {
- implicit val dataWriter: JsonWriter[Data] = JsonWriter.derived[Data]
- implicit val dataReader: JsonReader[Data] = JsonReader.derived[Data]
+ implicit val dataWriter: JsonWriter[Seq[Data]] =
+ JsonWriter.iterableWriter(tethys.derivation.semiauto.jsonWriter[Data])
+ implicit val dataReader: JsonReader[Seq[Data]] =
+ JsonReader.iterableReader(tethys.derivation.semiauto.jsonReader[Data])
+
+ object TethysDataProcessor extends DataWriter with DataReader {
+ override def write(seq: Seq[Data]): String = seq.asJson
+ override def read(json: String): Seq[Data] =
+ json.jsonAs[Seq[Data]] match {
+ case Right(data) => data
+ case Left(ex) => throw ex
+ }
+ }
object TethysJacksonDataProcessor extends DataWriter with DataReader {
+ import tethys.jackson._
override def write(seq: Seq[Data]): String = seq.asJson
- override def read(json: String): Seq[Data] = json.jsonAs[Seq[Data]].toOption.get
+ override def read(json: String): Seq[Data] =
+ json.jsonAs[Seq[Data]].toOption.get
}
}
diff --git a/modules/benchmarks/src/main/scala/json/bench/BenchMarkdown.scala b/modules/benchmarks/src/main/scala/json/bench/BenchMarkdown.scala
index a3ec4d3d..74d8484b 100644
--- a/modules/benchmarks/src/main/scala/json/bench/BenchMarkdown.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/BenchMarkdown.scala
@@ -19,22 +19,24 @@ object BenchMarkdown {
val benchmarksOrdering: Ordering[String] = Ordering.by[String, Int] {
case "Parsing" | "json.bench.JmhReaderBench.bench" => 1
case "Writing" | "json.bench.JmhWriterBench.bench" => 2
- case _ => 3
+ case _ => 3
}
val processorsOrdering: Ordering[String] = Ordering.by[String, Int] {
+ case "tethys" => 0
case "tethys-jackson" => 1
- case "pure-jackson" => 2
- case "circe" => 3
- case "circe-jawn" => 4
- case "circe-jackson" => 5
+ case "pure-jackson" => 2
+ case "circe" => 3
+ case "circe-jawn" => 4
+ case "circe-jackson" => 5
+ case "jsoniter" => 7
case "json4s-jackson" => 8
- case "json4s-native" => 9
- case "play-json" => 10
- case "spray-json" => 11
- case "pushka" => 12
- case "zio-json" => 13
- case _ => 13
+ case "json4s-native" => 9
+ case "play-json" => 10
+ case "spray-json" => 11
+ case "pushka" => 12
+ case "zio-json" => 13
+ case _ => 13
}
val namesMapping = Map(
@@ -42,17 +44,30 @@ object BenchMarkdown {
"json.bench.JmhWriterBench.bench" -> "Writing"
)
- implicit val mbScore: JsonReader[Either[String, Double]] = new JsonReader[Either[String, Double]] {
- override def read(it: TokenIterator)(implicit fieldName: FieldName): Either[String, Double] = {
- if(it.currentToken().isNumberValue) Right(Math.round(JsonReader.doubleReader.read(it) * 1000) / 1000.0)
- else Left(JsonReader.stringReader.read(it))
+ implicit val mbScore: JsonReader[Either[String, Double]] =
+ new JsonReader[Either[String, Double]] {
+ override def read(
+ it: TokenIterator
+ )(implicit fieldName: FieldName): Either[String, Double] = {
+ if (it.currentToken().isNumberValue)
+ Right(Math.round(JsonReader.doubleReader.read(it) * 1000) / 1000.0)
+ else Left(JsonReader.stringReader.read(it))
+ }
}
- }
- implicit val primaryMetricsReader: JsonReader[PrimaryMetrics] = jsonReader[PrimaryMetrics]
+ implicit val primaryMetricsReader: JsonReader[PrimaryMetrics] =
+ jsonReader[PrimaryMetrics]
implicit val benchmarkReader: JsonReader[Benchmark] = jsonReader[Benchmark]
- case class Benchmark(benchmark: String, mode: String, params: mutable.LinkedHashMap[String, String], primaryMetric: PrimaryMetrics)
- case class PrimaryMetrics(score: Either[String, Double], scoreError: Either[String, Double])
+ case class Benchmark(
+ benchmark: String,
+ mode: String,
+ params: mutable.LinkedHashMap[String, String],
+ primaryMetric: PrimaryMetrics
+ )
+ case class PrimaryMetrics(
+ score: Either[String, Double],
+ scoreError: Either[String, Double]
+ )
def readBenchmarks(dir: String, file: String): Seq[Benchmark] = {
val jhmResultsPath = Paths.get(dir, file)
@@ -61,15 +76,22 @@ object BenchMarkdown {
}
def main(args: Array[String]): Unit = {
- val List(dir) = args.toList
- val benchs = readBenchmarks(dir, "jmh-reader.json") ++ readBenchmarks(dir, "jmh-writer.json")
+ val dir = util.Try(args(0)).getOrElse(".")
+ val benchs = readBenchmarks(dir, "jmh-reader.json") ++ readBenchmarks(
+ dir,
+ "jmh-writer.json"
+ )
val grouped = benchs.groupBy(_.benchmark)
val mainTables = grouped.toList.sortBy(_._1)(benchmarksOrdering).map {
case (name, benchmarks) =>
- val rows = benchmarks.map(_.params(nameColumn)).distinct.sorted(processorsOrdering)
+ val rows = benchmarks
+ .map(_.params(nameColumn))
+ .distinct
+ .sorted(processorsOrdering)
val colls = benchmarks.map(_.params(sizeColumn)).distinct
val data = benchmarks.map { b =>
- (b.params(nameColumn), b.params(sizeColumn)) -> b.primaryMetric.score.fold(identity, _.toString)
+ (b.params(nameColumn), b.params(sizeColumn)) -> b.primaryMetric.score
+ .fold(identity, _.toString)
}.toMap
s"""
@@ -87,7 +109,7 @@ object BenchMarkdown {
private def chart(name: String, bs: Seq[Benchmark], dir: String): String = {
val title = namesMapping.getOrElse(name, name)
val images = Paths.get(dir, "images")
- if(Files.notExists(images)) {
+ if (Files.notExists(images)) {
Files.createDirectory(images)
}
val imgPath = dir + "/images/" + title + "Performance"
@@ -98,16 +120,25 @@ object BenchMarkdown {
""".stripMargin
}
- private def table(rows: Seq[String], columns: Seq[String], data: Map[(String, String), String]): String = {
+ private def table(
+ rows: Seq[String],
+ columns: Seq[String],
+ data: Map[(String, String), String]
+ ): String = {
val header = columns.foldLeft("name \\ size")(_ + "|" + _)
val line = columns.map(_ => "---").foldLeft("---")(_ + "|" + _)
val dataLines = rows.map { row =>
- columns.map(col => data.getOrElse((row, col), " ")).foldLeft(row)(_ + "|" + _)
+ columns
+ .map(col => data.getOrElse((row, col), " "))
+ .foldLeft(row)(_ + "|" + _)
}
dataLines.foldLeft(header + "\n" + line)(_ + "\n" + _)
}
- private def buildPerformanceChart(title: String, benchmarks: Seq[Benchmark]): CategoryChart = {
+ private def buildPerformanceChart(
+ title: String,
+ benchmarks: Seq[Benchmark]
+ ): CategoryChart = {
val chart: CategoryChart = new CategoryChartBuilder()
.width(940)
.height(400)
@@ -116,12 +147,15 @@ object BenchMarkdown {
.xAxisTitle("size")
.build()
- val maxs = benchmarks.groupBy(_.params(sizeColumn)).map {
- case (size, bs) => size -> bs.map(_.primaryMetric.score.fold(_ => 0.0, identity)).max
+ val maxs = benchmarks.groupBy(_.params(sizeColumn)).map { case (size, bs) =>
+ size -> bs.map(_.primaryMetric.score.fold(_ => 0.0, identity)).max
}
- benchmarks.groupBy(_.params(nameColumn)).toList.sortBy(_._1)(processorsOrdering).foreach {
- case (name, bs) =>
+ benchmarks
+ .groupBy(_.params(nameColumn))
+ .toList
+ .sortBy(_._1)(processorsOrdering)
+ .foreach { case (name, bs) =>
import scala.collection.JavaConverters._
val data = bs.map { b =>
@@ -131,7 +165,7 @@ object BenchMarkdown {
val xData = data.map(_._1).asJava
val yData = data.map(t => Double.box(t._2)).asJava
chart.addSeries(name, xData, yData)
- }
+ }
chart
}
diff --git a/modules/benchmarks/src/main/scala/json/bench/DataReader.scala b/modules/benchmarks/src/main/scala/json/bench/DataReader.scala
index 151926a2..57ecabfc 100644
--- a/modules/benchmarks/src/main/scala/json/bench/DataReader.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/DataReader.scala
@@ -3,6 +3,7 @@ package json.bench
import json.bench.circe.CirceBench
import json.bench.handwritten.HandwrittenBench
import json.bench.json4s.Json4sBench
+import json.bench.jsoniter.JsoniterBench
import json.bench.model.Data
import json.bench.play.PlayBench
import json.bench.spray.SprayBench
@@ -16,9 +17,11 @@ trait DataReader {
object DataReader {
val instances: Map[String, DataReader] = Map[String, DataReader](
"tethys-jackson" -> TethysBench.TethysJacksonDataProcessor,
+ "tethys" -> TethysBench.TethysDataProcessor,
"pure-jackson" -> HandwrittenBench.HandwrittenJacksonDataProcessor,
"circe-jawn" -> CirceBench.CirceJawnDataReader,
"circe-jackson" -> CirceBench.CirceJacksonDataReader,
+ "jsoniter" -> JsoniterBench.JsoniterProcessor,
"json4s-jackson" -> Json4sBench.Json4sJacksonDataProcessor,
"json4s-native" -> Json4sBench.Json4sNativeDataProcessor,
"play-json" -> PlayBench.PlayDataProcessor,
diff --git a/modules/benchmarks/src/main/scala/json/bench/DataWriter.scala b/modules/benchmarks/src/main/scala/json/bench/DataWriter.scala
index a54654bc..6f3a5e24 100644
--- a/modules/benchmarks/src/main/scala/json/bench/DataWriter.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/DataWriter.scala
@@ -3,6 +3,7 @@ package json.bench
import json.bench.circe.CirceBench
import json.bench.handwritten.HandwrittenBench
import json.bench.json4s.Json4sBench
+import json.bench.jsoniter.JsoniterBench
import json.bench.model.Data
import json.bench.play.PlayBench
import json.bench.spray.SprayBench
@@ -16,12 +17,14 @@ trait DataWriter {
object DataWriter {
val instances: Map[String, DataWriter] = Map(
"tethys-jackson" -> TethysBench.TethysJacksonDataProcessor,
+ "tethys" -> TethysBench.TethysDataProcessor,
"pure-jackson" -> HandwrittenBench.HandwrittenJacksonDataProcessor,
"circe" -> CirceBench.CirceDataWriter,
"java.lang.StringBuilder" -> HandwrittenBench.HandwrittenJavaDataWriter,
"scala.StringBuilder" -> HandwrittenBench.HandwrittenScalaDataWriter,
"json4s-jackson" -> Json4sBench.Json4sJacksonDataProcessor,
"json4s-native" -> Json4sBench.Json4sNativeDataProcessor,
+ "jsoniter" -> JsoniterBench.JsoniterProcessor,
"play-json" -> PlayBench.PlayDataProcessor,
"spray-json" -> SprayBench.SprayDataProcessor,
"zio-json" -> ZIOJsonBench.ZIOJsonDataProcesser
diff --git a/modules/benchmarks/src/main/scala/json/bench/JmhReaderBench.scala b/modules/benchmarks/src/main/scala/json/bench/JmhReaderBench.scala
index e32e0131..9a94ce86 100644
--- a/modules/benchmarks/src/main/scala/json/bench/JmhReaderBench.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/JmhReaderBench.scala
@@ -13,40 +13,46 @@ import org.openjdk.jmh.annotations.{State, _}
@Fork(value = 1, jvmArgsAppend = Array("-Xms1G", "-Xmx1G"))
@State(Scope.Benchmark)
class JmhReaderBench {
- @Param(Array(
- "128b",
- "1kb",
- "128kb",
- "1mb",
- "32mb"
- ))
+ @Param(
+ Array(
+ "128b"
+ // "1kb",
+ // "128kb",
+ // "1mb",
+ // "32mb"
+ )
+ )
var jsonSize: String = _
val seed = 10000
- var data: String =_
+ var data: String = _
@Setup(Level.Trial)
def setup(): Unit = {
val entities = jsonSize match {
- case "128b" => Data.dataSamples(1, seed)
- case "1kb" => Data.dataSamples(8, seed)
+ case "128b" => Data.dataSamples(1, seed)
+ case "1kb" => Data.dataSamples(8, seed)
case "128kb" => Data.dataSamples(128 * 8, seed)
- case "1mb" => Data.dataSamples(8 * 128 * 8, seed)
- case "32mb" => Data.dataSamples(32 * 8 * 128 * 8, seed)
+ case "1mb" => Data.dataSamples(8 * 128 * 8, seed)
+ case "32mb" => Data.dataSamples(32 * 8 * 128 * 8, seed)
}
data = TethysJacksonDataProcessor.write(entities)
}
- @Param(Array(
- "tethys-jackson",
- "pure-jackson",
- "circe-jawn",
- "circe-jackson",
- "play-json",
- "spray-json",
- "zio-json"
- ))
+ @Param(
+ Array(
+ "tethys",
+ "tethys-jackson"
+ // "jsoniter"
+ // "pure-jackson",
+ // "circe-jawn",
+ // "circe-jackson",
+ // "play-json",
+ // "spray-json",
+ // "zio-json"
+ )
+ )
var processorName: String = _
@Benchmark
diff --git a/modules/benchmarks/src/main/scala/json/bench/JmhWriterBench.scala b/modules/benchmarks/src/main/scala/json/bench/JmhWriterBench.scala
index 60bcae5c..5ab1ae8c 100644
--- a/modules/benchmarks/src/main/scala/json/bench/JmhWriterBench.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/JmhWriterBench.scala
@@ -7,18 +7,20 @@ import org.openjdk.jmh.annotations._
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
-@Warmup(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
-@Measurement(iterations = 4, time = 5, timeUnit = TimeUnit.SECONDS)
+@Warmup(iterations = 4, time = 1, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 4, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(value = 1, jvmArgsAppend = Array("-Xms1G", "-Xmx1G"))
@State(Scope.Benchmark)
class JmhWriterBench {
- @Param(Array(
- "128b",
- "1kb",
- "128kb",
- "1mb",
- "32mb"
- ))
+ @Param(
+ Array(
+ "128b"
+ // "1kb",
+ // "128kb",
+ // "1mb",
+ // "32mb"
+ )
+ )
var jsonSize: String = _
val seed = 10000
@@ -27,22 +29,26 @@ class JmhWriterBench {
@Setup(Level.Trial)
def setup(): Unit = {
data = jsonSize match {
- case "128b" => Data.dataSamples(1, seed)
- case "1kb" => Data.dataSamples(8, seed)
+ case "128b" => Data.dataSamples(1, seed)
+ case "1kb" => Data.dataSamples(8, seed)
case "128kb" => Data.dataSamples(128 * 8, seed)
- case "1mb" => Data.dataSamples(8 * 128 * 8, seed)
- case "32mb" => Data.dataSamples(32 * 8 * 128 * 8, seed)
+ case "1mb" => Data.dataSamples(8 * 128 * 8, seed)
+ case "32mb" => Data.dataSamples(32 * 8 * 128 * 8, seed)
}
}
- @Param(Array(
- "tethys-jackson",
- "pure-jackson",
- "circe",
- "play-json",
- "spray-json",
- "zio-json"
- ))
+ @Param(
+ Array(
+ "tethys"
+ // "tethys-jackson",
+ // "jsoniter",
+ // "pure-jackson",
+ // "circe",
+ // "play-json",
+ // "spray-json",
+ // "zio-json"
+ )
+ )
var processorName: String = _
@Benchmark
diff --git a/modules/benchmarks/src/main/scala/json/bench/circe/CirceBench.scala b/modules/benchmarks/src/main/scala/json/bench/circe/CirceBench.scala
index 87f8a2e6..1527cc68 100644
--- a/modules/benchmarks/src/main/scala/json/bench/circe/CirceBench.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/circe/CirceBench.scala
@@ -15,10 +15,12 @@ object CirceBench {
}
object CirceJawnDataReader extends DataReader {
- override def read(json: String): Seq[Data] = jawn.decode[Seq[Data]](json).toOption.get
+ override def read(json: String): Seq[Data] =
+ jawn.decode[Seq[Data]](json).toOption.get
}
object CirceJacksonDataReader extends DataReader {
- override def read(json: String): Seq[Data] = jackson.decode[Seq[Data]](json).toOption.get
+ override def read(json: String): Seq[Data] =
+ jackson.decode[Seq[Data]](json).toOption.get
}
}
diff --git a/modules/benchmarks/src/main/scala/json/bench/handwritten/HandwrittenBench.scala b/modules/benchmarks/src/main/scala/json/bench/handwritten/HandwrittenBench.scala
index 42d27f32..5b822272 100644
--- a/modules/benchmarks/src/main/scala/json/bench/handwritten/HandwrittenBench.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/handwritten/HandwrittenBench.scala
@@ -14,10 +14,10 @@ object HandwrittenBench {
val builder = new StringBuilder("[")
val dataIterator = seq.iterator
- if(dataIterator.hasNext) {
+ if (dataIterator.hasNext) {
writeData(dataIterator.next(), builder)
}
- while(dataIterator.hasNext) {
+ while (dataIterator.hasNext) {
writeData(dataIterator.next(), builder.append(","))
}
@@ -45,10 +45,10 @@ object HandwrittenBench {
.append('[')
val intIter = data.seqInt.iterator
- if(intIter.hasNext) {
+ if (intIter.hasNext) {
builder.append(intIter.next())
}
- while(intIter.hasNext) {
+ while (intIter.hasNext) {
builder.append(",").append(intIter.next())
}
@@ -58,11 +58,11 @@ object HandwrittenBench {
.append('{')
val mapStringIntIter = data.mapStringInt.iterator
- if(mapStringIntIter.hasNext) {
+ if (mapStringIntIter.hasNext) {
val (key, value) = mapStringIntIter.next()
builder.appendName(key).append(value)
}
- while(mapStringIntIter.hasNext) {
+ while (mapStringIntIter.hasNext) {
val (key, value) = mapStringIntIter.next()
builder.appendName(key).append(value)
}
@@ -73,7 +73,8 @@ object HandwrittenBench {
}
- private implicit class ScalaBuilderOps(val builder: StringBuilder) extends AnyVal {
+ private implicit class ScalaBuilderOps(val builder: StringBuilder)
+ extends AnyVal {
def appendName(name: String): StringBuilder = {
builder.append('"')
appendString(builder, name)
@@ -89,32 +90,33 @@ object HandwrittenBench {
private def appendString(builder: StringBuilder, s: String): Unit = {
var i = 0
- while(i < s.length) {
+ while (i < s.length) {
appendChar(builder, s.charAt(i))
i = i + 1
}
}
- private def appendChar(builder: StringBuilder, char: Char): Unit = char match {
- case '\n' => builder.append("\\n")
- case '\r' => builder.append("\\r")
- case '\t' => builder.append("\\t")
- case '\b' => builder.append("\\b")
- case '\f' => builder.append("\\f")
- case '\\' => builder.append("\\\\")
- case '"' => builder.append("\\\"")
- case _ => builder.append(char)
- }
+ private def appendChar(builder: StringBuilder, char: Char): Unit =
+ char match {
+ case '\n' => builder.append("\\n")
+ case '\r' => builder.append("\\r")
+ case '\t' => builder.append("\\t")
+ case '\b' => builder.append("\\b")
+ case '\f' => builder.append("\\f")
+ case '\\' => builder.append("\\\\")
+ case '"' => builder.append("\\\"")
+ case _ => builder.append(char)
+ }
object HandwrittenJavaDataWriter extends DataWriter {
override def write(seq: Seq[Data]): String = {
val builder = new java.lang.StringBuilder("[")
val dataIterator = seq.iterator
- if(dataIterator.hasNext) {
+ if (dataIterator.hasNext) {
writeData(dataIterator.next(), builder)
}
- while(dataIterator.hasNext) {
+ while (dataIterator.hasNext) {
writeData(dataIterator.next(), builder.append(","))
}
@@ -123,7 +125,10 @@ object HandwrittenBench {
.toString()
}
- private def writeData(data: Data, builder: java.lang.StringBuilder): Unit = {
+ private def writeData(
+ data: Data,
+ builder: java.lang.StringBuilder
+ ): Unit = {
builder
.append("{")
.appendName("string")
@@ -142,10 +147,10 @@ object HandwrittenBench {
.append('[')
val intIter = data.seqInt.iterator
- if(intIter.hasNext) {
+ if (intIter.hasNext) {
builder.append(intIter.next())
}
- while(intIter.hasNext) {
+ while (intIter.hasNext) {
builder.append(",").append(intIter.next())
}
@@ -155,11 +160,11 @@ object HandwrittenBench {
.append('{')
val mapStringIntIter = data.mapStringInt.iterator
- if(mapStringIntIter.hasNext) {
+ if (mapStringIntIter.hasNext) {
val (key, value) = mapStringIntIter.next()
builder.appendName(key).append(value)
}
- while(mapStringIntIter.hasNext) {
+ while (mapStringIntIter.hasNext) {
val (key, value) = mapStringIntIter.next()
builder.appendName(key).append(value)
}
@@ -169,7 +174,8 @@ object HandwrittenBench {
}
}
- private implicit class JavaBuilderOps(val builder: java.lang.StringBuilder) extends AnyVal {
+ private implicit class JavaBuilderOps(val builder: java.lang.StringBuilder)
+ extends AnyVal {
def appendName(name: String): java.lang.StringBuilder = {
builder.append('"')
appendString(builder, name)
@@ -183,24 +189,28 @@ object HandwrittenBench {
}
}
- private def appendString(builder: java.lang.StringBuilder, s: String): Unit = {
+ private def appendString(
+ builder: java.lang.StringBuilder,
+ s: String
+ ): Unit = {
var i = 0
- while(i < s.length) {
+ while (i < s.length) {
appendChar(builder, s.charAt(i))
i = i + 1
}
}
- private def appendChar(builder: java.lang.StringBuilder, char: Char): Unit = char match {
- case '\n' => builder.append("\\n")
- case '\r' => builder.append("\\r")
- case '\t' => builder.append("\\t")
- case '\b' => builder.append("\\b")
- case '\f' => builder.append("\\f")
- case '\\' => builder.append("\\\\")
- case '"' => builder.append("\\\"")
- case _ => builder.append(char)
- }
+ private def appendChar(builder: java.lang.StringBuilder, char: Char): Unit =
+ char match {
+ case '\n' => builder.append("\\n")
+ case '\r' => builder.append("\\r")
+ case '\t' => builder.append("\\t")
+ case '\b' => builder.append("\\b")
+ case '\f' => builder.append("\\f")
+ case '\\' => builder.append("\\\\")
+ case '"' => builder.append("\\\"")
+ case _ => builder.append(char)
+ }
object HandwrittenJacksonDataProcessor extends DataWriter with DataReader {
private val jsonFactory = {
@@ -227,7 +237,7 @@ object HandwrittenBench {
generator.writeStartArray()
val dataIterator = seq.iterator
- while(dataIterator.hasNext) {
+ while (dataIterator.hasNext) {
writeData(dataIterator.next(), generator)
}
@@ -251,11 +261,10 @@ object HandwrittenBench {
generator.writeFieldName("bigDecimal")
generator.writeNumber(data.bigDecimal.bigDecimal)
-
generator.writeFieldName("seqInt")
generator.writeStartArray()
val intIter = data.seqInt.iterator
- while(intIter.hasNext) {
+ while (intIter.hasNext) {
generator.writeNumber(intIter.next)
}
generator.writeEndArray()
@@ -264,7 +273,7 @@ object HandwrittenBench {
generator.writeStartObject()
val mapStringIntIter = data.mapStringInt.iterator
- while(mapStringIntIter.hasNext) {
+ while (mapStringIntIter.hasNext) {
val (key, value) = mapStringIntIter.next()
generator.writeFieldName(key)
generator.writeNumber(value)
@@ -278,7 +287,7 @@ object HandwrittenBench {
val parser = jsonParser(json)
val builder = Seq.newBuilder[Data]
require(parser.nextToken() == JsonToken.START_ARRAY)
- while(parser.nextToken() != JsonToken.END_ARRAY) {
+ while (parser.nextToken() != JsonToken.END_ARRAY) {
builder += readDataObject(parser)
}
builder.result()
@@ -303,24 +312,24 @@ object HandwrittenBench {
var mapStringIntField: Map[String, Int] = null
var mapStringIntFieldInitialized: Boolean = false
- while(parser.nextToken() != JsonToken.END_OBJECT) {
+ while (parser.nextToken() != JsonToken.END_OBJECT) {
val field = parser.getCurrentName
- if(field == "string") {
+ if (field == "string") {
stringField = parser.nextTextValue()
stringFieldInitialized = true
- } else if(field == "int") {
+ } else if (field == "int") {
parser.nextToken()
intField = parser.getIntValue
intFieldInitialized = true
- } else if(field == "boolean") {
+ } else if (field == "boolean") {
booleanField = parser.nextBooleanValue()
booleanFieldInitialized = true
- } else if(field == "bigDecimal") {
+ } else if (field == "bigDecimal") {
parser.nextToken()
bigDecimalField = BigDecimal(parser.getNumberValue.doubleValue())
bigDecimalFieldInitialized = true
- } else if(field == "seqInt") {
+ } else if (field == "seqInt") {
seqIntField = readSeqOfInt(parser)
seqIntFieldInitialized = true
} else {
@@ -329,7 +338,9 @@ object HandwrittenBench {
}
}
- require(stringFieldInitialized && intFieldInitialized && booleanFieldInitialized && bigDecimalFieldInitialized && mapStringIntFieldInitialized)
+ require(
+ stringFieldInitialized && intFieldInitialized && booleanFieldInitialized && bigDecimalFieldInitialized && mapStringIntFieldInitialized
+ )
Data(
string = stringField,
diff --git a/modules/benchmarks/src/main/scala/json/bench/json4s/Json4sBench.scala b/modules/benchmarks/src/main/scala/json/bench/json4s/Json4sBench.scala
index ce5c3656..c1a17b71 100644
--- a/modules/benchmarks/src/main/scala/json/bench/json4s/Json4sBench.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/json4s/Json4sBench.scala
@@ -8,14 +8,18 @@ object Json4sBench {
implicit val format: DefaultFormats.type = DefaultFormats
object Json4sNativeDataProcessor extends DataWriter with DataReader {
- override def write(seq: Seq[Data]): String = org.json4s.native.Serialization.write(seq)
+ override def write(seq: Seq[Data]): String =
+ org.json4s.native.Serialization.write(seq)
- override def read(json: String): Seq[Data] = org.json4s.native.parseJson(json).extract[Seq[Data]]
+ override def read(json: String): Seq[Data] =
+ org.json4s.native.parseJson(json).extract[Seq[Data]]
}
object Json4sJacksonDataProcessor extends DataWriter with DataReader {
- override def write(seq: Seq[Data]): String = org.json4s.jackson.Serialization.write(seq)
+ override def write(seq: Seq[Data]): String =
+ org.json4s.jackson.Serialization.write(seq)
- override def read(json: String): Seq[Data] = org.json4s.jackson.parseJson(json).extract[Seq[Data]]
+ override def read(json: String): Seq[Data] =
+ org.json4s.jackson.parseJson(json).extract[Seq[Data]]
}
}
diff --git a/modules/benchmarks/src/main/scala/json/bench/jsoniter/JsoniterBench.scala b/modules/benchmarks/src/main/scala/json/bench/jsoniter/JsoniterBench.scala
new file mode 100644
index 00000000..fa6cbf19
--- /dev/null
+++ b/modules/benchmarks/src/main/scala/json/bench/jsoniter/JsoniterBench.scala
@@ -0,0 +1,17 @@
+package json.bench.jsoniter
+
+import json.bench.model.Data
+
+import com.github.plokhotnyuk.jsoniter_scala.macros._
+import com.github.plokhotnyuk.jsoniter_scala.core._
+import json.bench.{DataReader, DataWriter}
+
+object JsoniterBench {
+ implicit val dataWrites: JsonValueCodec[Seq[Data]] = JsonCodecMaker.make
+
+ object JsoniterProcessor extends DataWriter with DataReader {
+ override def write(seq: Seq[Data]): String = writeToString(seq)
+
+ override def read(json: String): Seq[Data] = readFromString[Seq[Data]](json)
+ }
+}
diff --git a/modules/benchmarks/src/main/scala/json/bench/model/Data.scala b/modules/benchmarks/src/main/scala/json/bench/model/Data.scala
index a171da38..b6674018 100644
--- a/modules/benchmarks/src/main/scala/json/bench/model/Data.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/model/Data.scala
@@ -2,16 +2,22 @@ package json.bench.model
import scala.util.Random
-case class Data(string: String,
- int: Int,
- boolean: Boolean,
- bigDecimal: BigDecimal,
- seqInt: Seq[Int],
- mapStringInt: Map[String, Int])
+case class Data(
+ string: String,
+ int: Int,
+ boolean: Boolean,
+ bigDecimal: BigDecimal,
+ seqInt: Seq[Int],
+ mapStringInt: Map[String, Int]
+)
object Data {
- def samples[JAst](dataBuilder: DataBuilder[JAst], count: Int, seed: Int): JAst = {
+ def samples[JAst](
+ dataBuilder: DataBuilder[JAst],
+ count: Int,
+ seed: Int
+ ): JAst = {
val asts = dataSamples(count, seed).map(dataBuilder.ast)
dataBuilder.array(asts)
}
@@ -27,7 +33,7 @@ object Data {
(1 to count).toList.map { i =>
val flag = (i % 2) == 0
- //128 bytes entity
+ // 128 bytes entity
Data( // 2 bytes
string = rndString(6 - (if (flag) 0 else 1)), // 9 + 8 (7) + 1 bytes
int = rndInt(3), // 6 + 3 + 1 bytes
diff --git a/modules/benchmarks/src/main/scala/json/bench/play/PlayBench.scala b/modules/benchmarks/src/main/scala/json/bench/play/PlayBench.scala
index 085c61f8..7316cf45 100644
--- a/modules/benchmarks/src/main/scala/json/bench/play/PlayBench.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/play/PlayBench.scala
@@ -10,7 +10,8 @@ object PlayBench {
implicit val dataReads: Reads[Data] = Json.reads[Data]
object PlayDataProcessor extends DataWriter with DataReader {
- override def write(seq: Seq[Data]): String = Json.stringify(Json.toJson(seq))
+ override def write(seq: Seq[Data]): String =
+ Json.stringify(Json.toJson(seq))
override def read(json: String): Seq[Data] = Json.parse(json).as[Seq[Data]]
}
diff --git a/modules/benchmarks/src/main/scala/json/bench/spray/SprayBench.scala b/modules/benchmarks/src/main/scala/json/bench/spray/SprayBench.scala
index a5f87a54..849fd6de 100644
--- a/modules/benchmarks/src/main/scala/json/bench/spray/SprayBench.scala
+++ b/modules/benchmarks/src/main/scala/json/bench/spray/SprayBench.scala
@@ -12,7 +12,8 @@ object SprayBench {
object SprayDataProcessor extends DataWriter with DataReader {
override def write(seq: Seq[Data]): String = seq.toJson.compactPrint
- override def read(json: String): Seq[Data] = json.parseJson.convertTo[Seq[Data]]
+ override def read(json: String): Seq[Data] =
+ json.parseJson.convertTo[Seq[Data]]
}
}
diff --git a/modules/core/src/main/scala-3/tethys/OrdinalEnumJsonReader.scala b/modules/core/src/main/scala-3/tethys/OrdinalEnumJsonReader.scala
index d59bdd4f..08985aaf 100644
--- a/modules/core/src/main/scala-3/tethys/OrdinalEnumJsonReader.scala
+++ b/modules/core/src/main/scala-3/tethys/OrdinalEnumJsonReader.scala
@@ -3,20 +3,21 @@ package tethys
import tethys.readers.{FieldName, ReaderError}
import tethys.readers.tokens.TokenIterator
-trait OrdinalEnumJsonReader[A] extends JsonReader[A]
+class OrdinalEnumJsonReader[A <: scala.reflect.Enum](getByOrdinal: Int => A)
+ extends JsonReader[A]:
+ override def read(it: TokenIterator)(using FieldName): A =
+ if it.currentToken().isNumberValue then
+ val res = it.int()
+ it.next()
+ try getByOrdinal(res)
+ catch
+ case ex: NoSuchElementException =>
+ ReaderError.wrongJson(s"Unknown enum ordinal: $res")
+ else
+ ReaderError.wrongJson(
+ s"Expected int value but found: ${it.currentToken()}"
+ )
object OrdinalEnumJsonReader:
inline def derived[A <: scala.reflect.Enum]: OrdinalEnumJsonReader[A] =
- new OrdinalEnumJsonReader[A]:
- def read(it: TokenIterator)(implicit fieldName: FieldName): A =
- if it.currentToken().isNumberValue then
- val res = it.int()
- it.next()
- try derivation.EnumCompanion.getByOrdinal[A](res)
- catch
- case ex: NoSuchElementException =>
- ReaderError.wrongJson(s"Unknown enum ordinal: $res")
- else
- ReaderError.wrongJson(
- s"Expected int value but found: ${it.currentToken()}"
- )
+ new OrdinalEnumJsonReader[A](derivation.EnumCompanion.getByOrdinal[A])
diff --git a/modules/core/src/main/scala-3/tethys/OrdinalEnumJsonWriter.scala b/modules/core/src/main/scala-3/tethys/OrdinalEnumJsonWriter.scala
index 457d1330..6bf7e7d6 100644
--- a/modules/core/src/main/scala-3/tethys/OrdinalEnumJsonWriter.scala
+++ b/modules/core/src/main/scala-3/tethys/OrdinalEnumJsonWriter.scala
@@ -2,16 +2,23 @@ package tethys
import tethys.writers.tokens.TokenWriter
-trait OrdinalEnumJsonWriter[A] extends JsonWriter[A]
+class OrdinalEnumJsonWriter[A <: scala.reflect.Enum] extends JsonWriter[A] {
+ override def write(value: A, tokenWriter: TokenWriter): Unit =
+ tokenWriter.writeNumber(value.ordinal)
+}
object OrdinalEnumJsonWriter:
inline def derived[A <: scala.reflect.Enum]: OrdinalEnumJsonWriter[A] =
- (value: A, tokenWriter: TokenWriter) =>
- tokenWriter.writeNumber(value.ordinal)
+ new OrdinalEnumJsonWriter[A]
- inline def withLabel[A <: scala.reflect.Enum](
- label: String
- ): JsonObjectWriter[A] =
- (value: A, tokenWriter: writers.tokens.TokenWriter) =>
+ private class WithLabel[A <: scala.reflect.Enum](label: String)
+ extends JsonObjectWriter[A] {
+ def writeValues(value: A, tokenWriter: TokenWriter): Unit = {
tokenWriter.writeFieldName(label)
tokenWriter.writeNumber(value.ordinal)
+ }
+ }
+ def withLabel[A <: scala.reflect.Enum](
+ label: String
+ ): JsonObjectWriter[A] =
+ new WithLabel[A](label)
diff --git a/modules/core/src/main/scala-3/tethys/StringEnumJsonReader.scala b/modules/core/src/main/scala-3/tethys/StringEnumJsonReader.scala
index e143d371..857ea7ab 100644
--- a/modules/core/src/main/scala-3/tethys/StringEnumJsonReader.scala
+++ b/modules/core/src/main/scala-3/tethys/StringEnumJsonReader.scala
@@ -3,20 +3,23 @@ package tethys
import tethys.readers.{FieldName, ReaderError}
import tethys.readers.tokens.TokenIterator
-trait StringEnumJsonReader[A] extends JsonReader[A]
+class StringEnumJsonReader[A <: scala.reflect.Enum](
+ getByName: String => A
+) extends JsonReader[A] {
+ override def read(it: TokenIterator)(using FieldName): A =
+ if it.currentToken().isStringValue then
+ val res = it.string()
+ it.next()
+ try getByName(res)
+ catch
+ case ex: NoSuchElementException =>
+ ReaderError.wrongJson(s"Unknown enum name: $res")
+ else
+ ReaderError.wrongJson(
+ s"Expected string value but found: ${it.currentToken()}"
+ )
+}
object StringEnumJsonReader:
inline def derived[A <: scala.reflect.Enum]: StringEnumJsonReader[A] =
- new StringEnumJsonReader[A]:
- def read(it: TokenIterator)(implicit fieldName: FieldName): A =
- if it.currentToken().isStringValue then
- val res = it.string()
- it.next()
- try derivation.EnumCompanion.getByName[A](res)
- catch
- case ex: NoSuchElementException =>
- ReaderError.wrongJson(s"Unknown enum name: $res")
- else
- ReaderError.wrongJson(
- s"Expected string value but found: ${it.currentToken()}"
- )
+ new StringEnumJsonReader[A](derivation.EnumCompanion.getByName[A])
diff --git a/modules/core/src/main/scala-3/tethys/StringEnumJsonWriter.scala b/modules/core/src/main/scala-3/tethys/StringEnumJsonWriter.scala
index c63a0b84..1a3da768 100644
--- a/modules/core/src/main/scala-3/tethys/StringEnumJsonWriter.scala
+++ b/modules/core/src/main/scala-3/tethys/StringEnumJsonWriter.scala
@@ -1,16 +1,23 @@
package tethys
import tethys.writers.tokens.TokenWriter
-trait StringEnumJsonWriter[A] extends JsonWriter[A]
+class StringEnumJsonWriter[A <: scala.reflect.Enum] extends JsonWriter[A] {
+ def write(value: A, tokenWriter: TokenWriter): Unit =
+ tokenWriter.writeString(value.toString)
+}
object StringEnumJsonWriter:
inline def derived[A <: scala.reflect.Enum]: StringEnumJsonWriter[A] =
- (value: A, tokenWriter: TokenWriter) =>
- tokenWriter.writeString(value.toString)
+ new StringEnumJsonWriter[A]
- inline def withLabel[A <: scala.reflect.Enum](
- label: String
- ): JsonObjectWriter[A] =
- (value: A, tokenWriter: writers.tokens.TokenWriter) =>
+ private class WithLabel[A <: scala.reflect.Enum](label: String)
+ extends JsonObjectWriter[A] {
+ def writeValues(value: A, tokenWriter: TokenWriter): Unit = {
tokenWriter.writeFieldName(label)
tokenWriter.writeString(value.toString)
+ }
+ }
+
+ def withLabel[A <: scala.reflect.Enum](
+ label: String
+ ): JsonObjectWriter[A] = new WithLabel[A](label)
diff --git a/modules/core/src/main/scala-3/tethys/derivation/Derivation.scala b/modules/core/src/main/scala-3/tethys/derivation/Derivation.scala
index 66661169..4eb51562 100644
--- a/modules/core/src/main/scala-3/tethys/derivation/Derivation.scala
+++ b/modules/core/src/main/scala-3/tethys/derivation/Derivation.scala
@@ -148,7 +148,7 @@ private[derivation] class DerivationMacro(val quotes: Quotes)
.get(field.tpe)
.fold(lookup[JsonWriter[f]])(_.asExprOf[JsonWriter[f]])
'{
- ${ writer }.write(
+ $writer.write(
${ field.label },
${ field.value('{ value }.asTerm).asExprOf[f] },
tokenWriter
diff --git a/modules/core/src/main/scala/tethys/commons/RawJson.scala b/modules/core/src/main/scala/tethys/commons/RawJson.scala
index 26b40093..61b45cf2 100644
--- a/modules/core/src/main/scala/tethys/commons/RawJson.scala
+++ b/modules/core/src/main/scala/tethys/commons/RawJson.scala
@@ -1,10 +1,12 @@
package tethys.commons
-import java.io.StringWriter
-
import tethys.readers.FieldName
import tethys.readers.tokens.TokenIterator
-import tethys.writers.tokens.{TokenWriter, TokenWriterProducer}
+import tethys.writers.tokens.{
+ TokenWriter,
+ TokenWriterConfig,
+ TokenWriterProducer
+}
import tethys.{JsonReader, JsonStreaming, JsonWriter}
final case class RawJson(json: String)
@@ -16,16 +18,16 @@ object RawJson {
}
implicit def rawJsonReader(implicit
- tokenWriterProducer: TokenWriterProducer
+ tokenWriterProducer: TokenWriterProducer,
+ tokenWriterConfig: TokenWriterConfig
): JsonReader[RawJson] = new JsonReader[RawJson] {
override def read(
it: TokenIterator
)(implicit fieldName: FieldName): RawJson = {
- val stringWriter = new StringWriter()
- val tokenWriter: TokenWriter = tokenWriterProducer.forWriter(stringWriter)
- JsonStreaming.streamValue(it, tokenWriter)
- tokenWriter.flush()
- RawJson(stringWriter.toString)
+ val tokenWriter = tokenWriterProducer.produce(tokenWriterConfig)
+ try JsonStreaming.streamValue(it, tokenWriter)
+ finally tokenWriter.flush()
+ RawJson(tokenWriter.result())
}
}
}
diff --git a/modules/core/src/main/scala/tethys/commons/TokenNode.scala b/modules/core/src/main/scala/tethys/commons/TokenNode.scala
index 306978d4..73d5bca7 100644
--- a/modules/core/src/main/scala/tethys/commons/TokenNode.scala
+++ b/modules/core/src/main/scala/tethys/commons/TokenNode.scala
@@ -2,9 +2,7 @@ package tethys.commons
import tethys.JsonReader
import tethys.commons.Token._
-import tethys.readers.ReaderError
import tethys.readers.tokens.{QueueIterator, TokenIteratorProducer}
-
sealed trait TokenNode {
def token: Token
}
@@ -144,7 +142,8 @@ object TokenNode {
implicit class TokenListOps(private val tokens: Seq[TokenNode])
extends AnyVal {
- import tethys.TokenIteratorOps
+ import tethys._
+
def tokensAs[A: JsonReader]: A =
QueueIterator(tokens).readJson[A].fold(throw _, identity)
}
diff --git a/modules/core/src/main/scala/tethys/package.scala b/modules/core/src/main/scala/tethys/package.scala
index ca0f45e1..e793322d 100644
--- a/modules/core/src/main/scala/tethys/package.scala
+++ b/modules/core/src/main/scala/tethys/package.scala
@@ -1,9 +1,8 @@
-import java.io.{Reader, StringReader, StringWriter, Writer}
-
-import tethys.readers.{FieldName, ReaderError}
import tethys.readers.tokens.{TokenIterator, TokenIteratorProducer}
-import tethys.writers.tokens.{TokenWriter, TokenWriterProducer}
+import tethys.readers.{FieldName, ReaderError}
+import tethys.writers.tokens.{TokenWriter, TokenWriterConfig, TokenWriterProducer}
+import java.io.{Reader, StringReader, StringWriter}
import scala.Specializable.Group
package object tethys {
@@ -12,53 +11,54 @@ package object tethys {
(Byte, Short, Int, Long, Float, Double, Boolean)
)
- // given
-
implicit class JsonWriterOps[A](val a: A) extends AnyVal {
def asJson(implicit
jsonWriter: JsonWriter[A],
- tokenWriterProducer: TokenWriterProducer
+ tokenWriterProducer: TokenWriterProducer,
+ tokenWriterConfig: TokenWriterConfig
): String = {
- val stringWriter = new StringWriter()
- writeJson(tokenWriterProducer.forWriter(stringWriter))
- stringWriter.toString
+ val tokenWriter = tokenWriterProducer.produce(tokenWriterConfig)
+ try jsonWriter.write(a, tokenWriter)
+ finally tokenWriter.flush()
+ tokenWriter.result()
}
def asJsonWith(
jsonWriter: JsonWriter[A]
- )(implicit tokenWriterProducer: TokenWriterProducer): String = {
- asJson(jsonWriter, tokenWriterProducer)
+ )(implicit
+ tokenWriterProducer: TokenWriterProducer,
+ tokenWriterConfig: TokenWriterConfig
+ ): String = {
+ val tokenWriter = tokenWriterProducer.produce(tokenWriterConfig)
+ try
+ jsonWriter.write(a, tokenWriter)
+ finally
+ tokenWriter.flush()
+ tokenWriter.result()
}
def writeJson(
tokenWriter: TokenWriter
)(implicit jsonWriter: JsonWriter[A]): Unit = {
- try jsonWriter.write(a, tokenWriter)
- finally {
+ try
+ jsonWriter.write(a, tokenWriter)
+ finally
tokenWriter.flush()
- }
}
}
- implicit class WriterOps(val w: Writer) extends AnyVal {
- def toTokenWriter(implicit
- tokenWriterProducer: TokenWriterProducer
- ): TokenWriter = tokenWriterProducer.forWriter(w)
- }
-
implicit class StringReaderOps(val json: String) extends AnyVal {
def jsonAs[A](implicit
jsonReader: JsonReader[A],
producer: TokenIteratorProducer
): Either[ReaderError, A] = {
- new StringReader(json).readJson[A]
+ implicit val fieldName: FieldName = FieldName.Root
+ producer.produce(json).flatMap(it => ReaderError.catchNonFatal(jsonReader.read(it)))
}
def toTokenIterator(implicit
producer: TokenIteratorProducer
- ): Either[ReaderError, TokenIterator] = {
- new StringReader(json).toTokenIterator
- }
+ ): Either[ReaderError, TokenIterator] = producer.produce(json)
}
implicit class ReaderReaderOps(val reader: Reader) extends AnyVal {
@@ -66,7 +66,7 @@ package object tethys {
jsonReader: JsonReader[A],
producer: TokenIteratorProducer
): Either[ReaderError, A] = {
- implicit val root: FieldName = FieldName()
+ implicit val root: FieldName = FieldName.Root
producer.fromReader(reader).right.flatMap(_.readJson[A])
}
@@ -88,7 +88,7 @@ package object tethys {
def readJson[A](implicit
jsonReader: JsonReader[A]
): Either[ReaderError, A] = {
- implicit val fieldName: FieldName = FieldName()
+ implicit val fieldName: FieldName = FieldName.Root
ReaderError.catchNonFatal(jsonReader.read(tokenIterator))
}
}
diff --git a/modules/core/src/main/scala/tethys/readers/FieldName.scala b/modules/core/src/main/scala/tethys/readers/FieldName.scala
index 84ba204e..bfb4d5e0 100644
--- a/modules/core/src/main/scala/tethys/readers/FieldName.scala
+++ b/modules/core/src/main/scala/tethys/readers/FieldName.scala
@@ -1,6 +1,6 @@
package tethys.readers
-final case class FieldName(value: () => String) {
+final case class FieldName(value: () => String) extends AnyVal {
self =>
def appendFieldName(s: String): FieldName = FieldName(() => s"${value()}.$s")
@@ -8,7 +8,9 @@ final case class FieldName(value: () => String) {
def appendArrayIndex(i: Int): FieldName = FieldName(() => s"${value()}[$i]")
}
object FieldName {
- def apply(): FieldName = new FieldName(() => "[ROOT]")
+ val Root: FieldName = new FieldName(() => "[ROOT]")
+
+ def apply(): FieldName = Root
def apply(value: String): FieldName = new FieldName(() => value)
}
diff --git a/modules/core/src/main/scala/tethys/readers/KeyReader.scala b/modules/core/src/main/scala/tethys/readers/KeyReader.scala
index bcd25dbd..c992632c 100644
--- a/modules/core/src/main/scala/tethys/readers/KeyReader.scala
+++ b/modules/core/src/main/scala/tethys/readers/KeyReader.scala
@@ -1,37 +1,38 @@
package tethys.readers
+@specialized(tethys.specializations)
trait KeyReader[A] {
def read(s: String)(implicit fieldName: FieldName): A
}
object KeyReader {
- implicit lazy val stringKeyReader: KeyReader[String] = new KeyReader[String] {
+ implicit val stringKeyReader: KeyReader[String] = new KeyReader[String] {
override def read(s: String)(implicit fieldName: FieldName): String = s
}
- implicit lazy val uuidKeyReader: KeyReader[java.util.UUID] =
+ implicit val uuidKeyReader: KeyReader[java.util.UUID] =
new KeyReader[java.util.UUID] {
override def read(s: String)(implicit
fieldName: FieldName
): java.util.UUID = java.util.UUID.fromString(s)
}
- implicit lazy val intKeyReader: KeyReader[Int] = new KeyReader[Int] {
+ implicit val intKeyReader: KeyReader[Int] = new KeyReader[Int] {
override def read(s: String)(implicit fieldName: FieldName): Int = s.toInt
}
- implicit lazy val longKeyReader: KeyReader[Long] = new KeyReader[Long] {
+ implicit val longKeyReader: KeyReader[Long] = new KeyReader[Long] {
override def read(s: String)(implicit fieldName: FieldName): Long = s.toLong
}
- implicit lazy val instantKeyReader: KeyReader[java.time.Instant] =
+ implicit val instantKeyReader: KeyReader[java.time.Instant] =
new KeyReader[java.time.Instant] {
override def read(s: String)(implicit
fieldName: FieldName
): java.time.Instant = java.time.Instant.parse(s)
}
- implicit lazy val localDateKeyReader: KeyReader[java.time.LocalDate] =
+ implicit val localDateKeyReader: KeyReader[java.time.LocalDate] =
new KeyReader[java.time.LocalDate] {
override def read(
s: String
@@ -40,7 +41,7 @@ object KeyReader {
.parse(s, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)
}
- implicit lazy val localDateTimeKeyReader: KeyReader[java.time.LocalDateTime] =
+ implicit val localDateTimeKeyReader: KeyReader[java.time.LocalDateTime] =
new KeyReader[java.time.LocalDateTime] {
override def read(
s: String
@@ -49,8 +50,7 @@ object KeyReader {
.parse(s, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)
}
- implicit lazy val offsetDateTimeKeyReader
- : KeyReader[java.time.OffsetDateTime] =
+ implicit val offsetDateTimeKeyReader: KeyReader[java.time.OffsetDateTime] =
new KeyReader[java.time.OffsetDateTime] {
override def read(
s: String
@@ -59,7 +59,7 @@ object KeyReader {
.parse(s, java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME)
}
- implicit lazy val zonedDateTimeKeyReader: KeyReader[java.time.ZonedDateTime] =
+ implicit val zonedDateTimeKeyReader: KeyReader[java.time.ZonedDateTime] =
new KeyReader[java.time.ZonedDateTime] {
override def read(
s: String
diff --git a/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala
index 8aa4944a..f98ebee5 100644
--- a/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala
+++ b/modules/core/src/main/scala/tethys/readers/instances/AllJsonReaders.scala
@@ -5,7 +5,7 @@ import tethys.readers.tokens.TokenIterator
import tethys.readers.{FieldName, ReaderError}
trait AllJsonReaders extends OptionReaders {
- implicit lazy val booleanReader: JsonReader[Boolean] =
+ implicit val booleanReader: JsonReader[Boolean] =
new JsonReader[Boolean] {
override def read(
it: TokenIterator
@@ -22,7 +22,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val stringReader: JsonReader[String] = new JsonReader[String] {
+ implicit val stringReader: JsonReader[String] = new JsonReader[String] {
override def read(
it: TokenIterator
)(implicit fieldName: FieldName): String = {
@@ -38,7 +38,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val charReader: JsonReader[Char] = stringReader.mapWithField {
+ implicit val charReader: JsonReader[Char] = stringReader.mapWithField {
implicit fieldName =>
{
case s if s.length == 1 => s.head
@@ -46,7 +46,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val numberReader: JsonReader[Number] = new JsonReader[Number] {
+ implicit val numberReader: JsonReader[Number] = new JsonReader[Number] {
override def read(
it: TokenIterator
)(implicit fieldName: FieldName): Number = {
@@ -62,7 +62,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val byteReader: JsonReader[Byte] = new JsonReader[Byte] {
+ implicit val byteReader: JsonReader[Byte] = new JsonReader[Byte] {
override def read(
it: TokenIterator
)(implicit fieldName: FieldName): Byte = {
@@ -78,7 +78,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val shortReader: JsonReader[Short] = new JsonReader[Short] {
+ implicit val shortReader: JsonReader[Short] = new JsonReader[Short] {
override def read(
it: TokenIterator
)(implicit fieldName: FieldName): Short = {
@@ -94,7 +94,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val intReader: JsonReader[Int] = new JsonReader[Int] {
+ implicit val intReader: JsonReader[Int] = new JsonReader[Int] {
override def read(it: TokenIterator)(implicit fieldName: FieldName): Int = {
if (it.currentToken().isNumberValue) {
val res = it.int()
@@ -108,7 +108,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val longReader: JsonReader[Long] = new JsonReader[Long] {
+ implicit val longReader: JsonReader[Long] = new JsonReader[Long] {
override def read(
it: TokenIterator
)(implicit fieldName: FieldName): Long = {
@@ -124,7 +124,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val floatReader: JsonReader[Float] = new JsonReader[Float] {
+ implicit val floatReader: JsonReader[Float] = new JsonReader[Float] {
override def read(
it: TokenIterator
)(implicit fieldName: FieldName): Float = {
@@ -140,7 +140,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val doubleReader: JsonReader[Double] = new JsonReader[Double] {
+ implicit val doubleReader: JsonReader[Double] = new JsonReader[Double] {
override def read(
it: TokenIterator
)(implicit fieldName: FieldName): Double = {
@@ -156,7 +156,7 @@ trait AllJsonReaders extends OptionReaders {
}
}
- implicit lazy val bigDecimalReader: JsonReader[BigDecimal] =
+ implicit val bigDecimalReader: JsonReader[BigDecimal] =
numberReader.map {
case bd: BigDecimal => bd
case bi: BigInt => BigDecimal(bi)
@@ -171,7 +171,7 @@ trait AllJsonReaders extends OptionReaders {
case num => BigDecimal(num.doubleValue())
}
- implicit lazy val bigIntReader: JsonReader[BigInt] = numberReader.map {
+ implicit val bigIntReader: JsonReader[BigInt] = numberReader.map {
case bi: BigInt => bi
case jbi: java.math.BigInteger => BigInt(jbi)
case bd: BigDecimal => bd.toBigInt
@@ -183,47 +183,47 @@ trait AllJsonReaders extends OptionReaders {
case num => BigInt(num.longValue())
}
- implicit lazy val javaBooleanReader: JsonReader[java.lang.Boolean] =
+ implicit val javaBooleanReader: JsonReader[java.lang.Boolean] =
booleanReader.map(a => a)
- implicit lazy val javaByteReader: JsonReader[java.lang.Byte] =
+ implicit val javaByteReader: JsonReader[java.lang.Byte] =
byteReader.map(a => a)
- implicit lazy val javaShortReader: JsonReader[java.lang.Short] =
+ implicit val javaShortReader: JsonReader[java.lang.Short] =
shortReader.map(a => a)
- implicit lazy val javaIntReader: JsonReader[java.lang.Integer] =
+ implicit val javaIntReader: JsonReader[java.lang.Integer] =
intReader.map(a => a)
- implicit lazy val javaLongReader: JsonReader[java.lang.Long] =
+ implicit val javaLongReader: JsonReader[java.lang.Long] =
longReader.map(a => a)
- implicit lazy val javaFloatReader: JsonReader[java.lang.Float] =
+ implicit val javaFloatReader: JsonReader[java.lang.Float] =
floatReader.map(a => a)
- implicit lazy val javaDoubleReader: JsonReader[java.lang.Double] =
+ implicit val javaDoubleReader: JsonReader[java.lang.Double] =
doubleReader.map(a => a)
- implicit lazy val javaBigDecimalReader: JsonReader[java.math.BigDecimal] =
+ implicit val javaBigDecimalReader: JsonReader[java.math.BigDecimal] =
bigDecimalReader.map(_.bigDecimal)
- implicit lazy val javaBigIntegerReader: JsonReader[java.math.BigInteger] =
+ implicit val javaBigIntegerReader: JsonReader[java.math.BigInteger] =
bigIntReader.map(_.bigInteger)
- implicit lazy val javaUUIDReader: JsonReader[java.util.UUID] =
+ implicit val javaUUIDReader: JsonReader[java.util.UUID] =
stringReader.map(java.util.UUID.fromString(_))
- implicit lazy val javaInstantReader: JsonReader[java.time.Instant] =
+ implicit val javaInstantReader: JsonReader[java.time.Instant] =
stringReader.map(java.time.Instant.parse)
- implicit lazy val javaLocalDateReader: JsonReader[java.time.LocalDate] =
+ implicit val javaLocalDateReader: JsonReader[java.time.LocalDate] =
stringReader.map(
java.time.LocalDate
.parse(_, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE)
)
- implicit lazy val javaLocalDateTimeReader
+ implicit val javaLocalDateTimeReader
: JsonReader[java.time.LocalDateTime] =
stringReader.map(
java.time.LocalDateTime
.parse(_, java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME)
)
- implicit lazy val javaOffsetDateTimeReader
+ implicit val javaOffsetDateTimeReader
: JsonReader[java.time.OffsetDateTime] =
stringReader.map(
java.time.OffsetDateTime
.parse(_, java.time.format.DateTimeFormatter.ISO_OFFSET_DATE_TIME)
)
- implicit lazy val javaZonedDateTimeReader
+ implicit val javaZonedDateTimeReader
: JsonReader[java.time.ZonedDateTime] =
stringReader.map(
java.time.ZonedDateTime
diff --git a/modules/core/src/main/scala/tethys/readers/instances/IterableReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/IterableReaders.scala
index 555a7b9e..9304da22 100644
--- a/modules/core/src/main/scala/tethys/readers/instances/IterableReaders.scala
+++ b/modules/core/src/main/scala/tethys/readers/instances/IterableReaders.scala
@@ -1,143 +1,28 @@
package tethys.readers.instances
import tethys.JsonReader
-import tethys.compat.CollectionBuilder
+import tethys.commons.Token
import tethys.readers.tokens.TokenIterator
-import tethys.readers.{FieldName, ReaderError}
+import tethys.readers.FieldName
-import scala.annotation.tailrec
-import scala.collection.mutable
+import scala.collection.Factory
import scala.language.higherKinds
-private[tethys] trait IterableReaders extends LowPriorityIterableReaders {
-
- implicit def byteIterableReader[C[X] <: Iterable[X]](implicit
- cb: CollectionBuilder[Byte, C[Byte]]
- ): JsonReader[C[Byte]] = new TraversableReader[Byte, C] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[Byte, C[Byte]]
- )(implicit fieldName: FieldName): Unit = {
- builder += PrimitiveReaders.ByteJsonReader.read(it)
- }
- }
-
- implicit def shortIterableReader[C[X] <: Iterable[X]](implicit
- cb: CollectionBuilder[Short, C[Short]]
- ): JsonReader[C[Short]] = new TraversableReader[Short, C] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[Short, C[Short]]
- )(implicit fieldName: FieldName): Unit = {
- builder += PrimitiveReaders.ShortJsonReader.read(it)
- }
- }
-
- implicit def intIterableReader[C[X] <: Iterable[X]](implicit
- cb: CollectionBuilder[Int, C[Int]]
- ): JsonReader[C[Int]] = new TraversableReader[Int, C] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[Int, C[Int]]
- )(implicit fieldName: FieldName): Unit = {
- builder += PrimitiveReaders.IntJsonReader.read(it)
- }
- }
-
- implicit def longIterableReader[C[X] <: Iterable[X]](implicit
- cb: CollectionBuilder[Long, C[Long]]
- ): JsonReader[C[Long]] = new TraversableReader[Long, C] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[Long, C[Long]]
- )(implicit fieldName: FieldName): Unit = {
- builder += PrimitiveReaders.LongJsonReader.read(it)
- }
- }
-
- implicit def floatIterableReader[C[X] <: Iterable[X]](implicit
- cb: CollectionBuilder[Float, C[Float]]
- ): JsonReader[C[Float]] = new TraversableReader[Float, C] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[Float, C[Float]]
- )(implicit fieldName: FieldName): Unit = {
- builder += PrimitiveReaders.FloatJsonReader.read(it)
- }
- }
-
- implicit def doubleIterableReader[C[X] <: Iterable[X]](implicit
- cb: CollectionBuilder[Double, C[Double]]
- ): JsonReader[C[Double]] = new TraversableReader[Double, C] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[Double, C[Double]]
- )(implicit fieldName: FieldName): Unit = {
- builder += PrimitiveReaders.DoubleJsonReader.read(it)
- }
- }
-
- implicit def booleanIterableReader[C[X] <: Iterable[X]](implicit
- cb: CollectionBuilder[Boolean, C[Boolean]]
- ): JsonReader[C[Boolean]] = new TraversableReader[Boolean, C] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[Boolean, C[Boolean]]
- )(implicit fieldName: FieldName): Unit = {
- builder += PrimitiveReaders.BooleanJsonReader.read(it)
- }
- }
-}
-
-private[tethys] trait LowPriorityIterableReaders
- extends LowPriorityJsonReaders {
-
+private[tethys] trait IterableReaders extends LowPriorityJsonReaders {
implicit def iterableReader[A, C[X] <: Iterable[X]](implicit
jsonReader: JsonReader[A],
- collectionBuilder: CollectionBuilder[A, C[A]]
- ): JsonReader[C[A]] = new TraversableReader[A, C] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[A, C[A]]
- )(implicit fieldName: FieldName): Unit = {
- builder += jsonReader.read(it)
- }
- }
-
- protected abstract class TraversableReader[A, C[X] <: Iterable[X]](implicit
- cb: CollectionBuilder[A, C[A]]
- ) extends JsonReader[C[A]] {
- protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[A, C[A]]
- )(implicit fieldName: FieldName): Unit
-
- override def read(
- it: TokenIterator
- )(implicit fieldName: FieldName): C[A] = {
- if (it.currentToken().isArrayStart) recRead(0, it.next(), cb.newBuilder)
- else
- ReaderError.wrongJson(
- s"Expected array start but found: ${it.currentToken()}"
- )
- }
-
- @tailrec
- private def recRead(
- i: Int,
- it: TokenIterator,
- builder: mutable.Builder[A, C[A]]
- )(implicit fieldName: FieldName): C[A] = {
- it.currentToken() match {
- case token if token.isEmpty =>
- ReaderError.wrongJson("Unexpected end of input")
- case token if token.isArrayEnd =>
- it.nextToken()
- builder.result()
- case _ =>
- appendBuilder(it, builder)(fieldName.appendArrayIndex(i))
- recRead(i + 1, it, builder)
+ from: Factory[A, C[A]]
+ ): JsonReader[C[A]] = new JsonReader[C[A]] {
+ override def read(it: TokenIterator)(implicit fieldName: FieldName) = {
+ val builder = from.newBuilder
+ it.nextToken()
+ var idx = 0
+ while (it.currentToken() != Token.ArrayEndToken) {
+ builder.addOne(jsonReader.read(it)(fieldName.appendArrayIndex(idx)))
+ idx += 1
}
+ it.nextToken()
+ builder.result()
}
}
}
diff --git a/modules/core/src/main/scala/tethys/readers/instances/MapReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/MapReaders.scala
index ce2b0de3..ab7027e2 100644
--- a/modules/core/src/main/scala/tethys/readers/instances/MapReaders.scala
+++ b/modules/core/src/main/scala/tethys/readers/instances/MapReaders.scala
@@ -1,188 +1,34 @@
package tethys.readers.instances
import tethys.JsonReader
-import tethys.compat.CollectionBuilder
+import tethys.commons.Token
import tethys.readers.tokens.TokenIterator
import tethys.readers.{FieldName, KeyReader, ReaderError}
-import scala.annotation.tailrec
-import scala.collection.mutable
+import scala.collection.Factory
import scala.language.higherKinds
-private[tethys] trait MapReaders extends LowPriorityMapReaders {
- implicit def byteMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](implicit
- keyReader: KeyReader[K],
- cb: CollectionBuilder[(K, Byte), M[K, Byte]]
- ): JsonReader[M[K, Byte]] = {
- new MapReader[K, Byte, M] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, Byte), M[K, Byte]],
- key: K
- )(implicit fieldName: FieldName): Unit = {
- builder += key -> PrimitiveReaders.ByteJsonReader.read(it)
- }
- }
- }
-
- implicit def shortMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](implicit
- keyReader: KeyReader[K],
- cb: CollectionBuilder[(K, Short), M[K, Short]]
- ): JsonReader[M[K, Short]] = {
- new MapReader[K, Short, M] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, Short), M[K, Short]],
- key: K
- )(implicit fieldName: FieldName): Unit = {
- builder += key -> PrimitiveReaders.ShortJsonReader.read(it)
- }
- }
- }
-
- implicit def intMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](implicit
- keyReader: KeyReader[K],
- cb: CollectionBuilder[(K, Int), M[K, Int]]
- ): JsonReader[M[K, Int]] = {
- new MapReader[K, Int, M] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, Int), M[K, Int]],
- key: K
- )(implicit fieldName: FieldName): Unit = {
- builder += key -> PrimitiveReaders.IntJsonReader.read(it)
- }
- }
- }
-
- implicit def longMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](implicit
- keyReader: KeyReader[K],
- cb: CollectionBuilder[(K, Long), M[K, Long]]
- ): JsonReader[M[K, Long]] = {
- new MapReader[K, Long, M] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, Long), M[K, Long]],
- key: K
- )(implicit fieldName: FieldName): Unit = {
- builder += key -> PrimitiveReaders.LongJsonReader.read(it)
- }
- }
- }
-
- implicit def floatMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](implicit
- keyReader: KeyReader[K],
- cb: CollectionBuilder[(K, Float), M[K, Float]]
- ): JsonReader[M[K, Float]] = {
- new MapReader[K, Float, M] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, Float), M[K, Float]],
- key: K
- )(implicit fieldName: FieldName): Unit = {
- builder += key -> PrimitiveReaders.FloatJsonReader.read(it)
- }
- }
- }
-
- implicit def doubleMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](
- implicit
- keyReader: KeyReader[K],
- cb: CollectionBuilder[(K, Double), M[K, Double]]
- ): JsonReader[M[K, Double]] = {
- new MapReader[K, Double, M] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, Double), M[K, Double]],
- key: K
- )(implicit fieldName: FieldName): Unit = {
- builder += key -> PrimitiveReaders.DoubleJsonReader.read(it)
- }
- }
- }
-
- implicit def booleanMapReader[K, M[X, Y] <: scala.collection.Map[X, Y]](
- implicit
- keyReader: KeyReader[K],
- cb: CollectionBuilder[(K, Boolean), M[K, Boolean]]
- ): JsonReader[M[K, Boolean]] = {
- new MapReader[K, Boolean, M] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, Boolean), M[K, Boolean]],
- key: K
- )(implicit fieldName: FieldName): Unit = {
- builder += key -> PrimitiveReaders.BooleanJsonReader.read(it)
- }
- }
- }
-}
-
-private[tethys] trait LowPriorityMapReaders extends IterableReaders {
-
- implicit def mapReader[K, A, M[X, Y] <: scala.collection.Map[X, Y]](implicit
- keyReader: KeyReader[K],
- jsonReader: JsonReader[A],
- cb: CollectionBuilder[(K, A), M[K, A]]
- ): JsonReader[M[K, A]] = {
- new MapReader[K, A, M] {
- override protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, A), M[K, A]],
- key: K
- )(implicit fieldName: FieldName): Unit = {
- builder += key -> jsonReader.read(it)
- }
- }
- }
-
- protected abstract class MapReader[K, A, M[_, _]](implicit
- keyReader: KeyReader[K],
- cb: CollectionBuilder[(K, A), M[K, A]]
- ) extends JsonReader[M[K, A]] {
-
- protected def appendBuilder(
- it: TokenIterator,
- builder: mutable.Builder[(K, A), M[K, A]],
- key: K
- )(implicit fieldName: FieldName): Unit
-
- override def read(
- it: TokenIterator
- )(implicit fieldName: FieldName): M[K, A] = {
- if (it.currentToken().isObjectStart)
- recRead(it.next(), cb.newBuilder)(fieldName)
- else
- ReaderError.wrongJson(
- s"Expected object start but found: ${it.currentToken()}"
+private[tethys] trait MapReaders extends IterableReaders {
+ implicit def mapReader[K, V, M[X, Y] <: scala.collection.Map[X, Y]](implicit
+ keyReader: KeyReader[K],
+ valueReader: JsonReader[V],
+ factory: Factory[(K, V), M[K, V]]
+ ): JsonReader[M[K, V]] = new JsonReader[M[K, V]] {
+ override def read(it: TokenIterator)(implicit fieldName: FieldName) = {
+ val builder = factory.newBuilder
+ it.nextToken()
+ while (it.currentToken() != Token.ObjectEndToken) {
+ val name = it.fieldName()
+ it.nextToken()
+ builder.addOne(
+ (
+ keyReader.read(name),
+ valueReader.read(it)(fieldName.appendFieldName(name))
+ )
)
- }
-
- @tailrec
- private def recRead(
- it: TokenIterator,
- builder: mutable.Builder[(K, A), M[K, A]]
- )(fieldName: FieldName): M[K, A] = {
- it.currentToken() match {
- case token if token.isObjectEnd =>
- it.nextToken()
- builder.result()
- case token if token.isFieldName =>
- val name = it.fieldName()
- val nextFieldName = fieldName.appendFieldName(name)
- appendBuilder(
- it.next(),
- builder,
- keyReader.read(name)(nextFieldName)
- )(nextFieldName)
- recRead(it, builder)(fieldName)
-
- case token =>
- ReaderError.wrongJson(
- s"Expect end of object or field name but '$token' found"
- )(fieldName)
}
+ it.nextToken()
+ builder.result()
}
}
-
}
diff --git a/modules/core/src/main/scala/tethys/readers/instances/OptionReaders.scala b/modules/core/src/main/scala/tethys/readers/instances/OptionReaders.scala
index 7c47825f..60f4a209 100644
--- a/modules/core/src/main/scala/tethys/readers/instances/OptionReaders.scala
+++ b/modules/core/src/main/scala/tethys/readers/instances/OptionReaders.scala
@@ -5,7 +5,7 @@ import tethys.readers.FieldName
import tethys.readers.tokens.TokenIterator
private[tethys] trait OptionReaders extends LowPriorityOptionReaders {
- implicit lazy val byteOptionReader: JsonReader[Option[Byte]] =
+ implicit val byteOptionReader: JsonReader[Option[Byte]] =
new OptionJsonReader[Byte] {
override protected def readSomeValue(
it: TokenIterator
@@ -14,7 +14,7 @@ private[tethys] trait OptionReaders extends LowPriorityOptionReaders {
}
}
- implicit lazy val shortOptionReader: JsonReader[Option[Short]] =
+ implicit val shortOptionReader: JsonReader[Option[Short]] =
new OptionJsonReader[Short] {
override protected def readSomeValue(
it: TokenIterator
@@ -23,7 +23,7 @@ private[tethys] trait OptionReaders extends LowPriorityOptionReaders {
}
}
- implicit lazy val intOptionReader: JsonReader[Option[Int]] =
+ implicit val intOptionReader: JsonReader[Option[Int]] =
new OptionJsonReader[Int] {
override protected def readSomeValue(
it: TokenIterator
@@ -32,7 +32,7 @@ private[tethys] trait OptionReaders extends LowPriorityOptionReaders {
}
}
- implicit lazy val longOptionReader: JsonReader[Option[Long]] =
+ implicit val longOptionReader: JsonReader[Option[Long]] =
new OptionJsonReader[Long] {
override protected def readSomeValue(
it: TokenIterator
@@ -41,7 +41,7 @@ private[tethys] trait OptionReaders extends LowPriorityOptionReaders {
}
}
- implicit lazy val floatOptionReader: JsonReader[Option[Float]] =
+ implicit val floatOptionReader: JsonReader[Option[Float]] =
new OptionJsonReader[Float] {
override protected def readSomeValue(
it: TokenIterator
@@ -50,7 +50,7 @@ private[tethys] trait OptionReaders extends LowPriorityOptionReaders {
}
}
- implicit lazy val doubleOptionReader: JsonReader[Option[Double]] =
+ implicit val doubleOptionReader: JsonReader[Option[Double]] =
new OptionJsonReader[Double] {
override protected def readSomeValue(
it: TokenIterator
@@ -59,7 +59,7 @@ private[tethys] trait OptionReaders extends LowPriorityOptionReaders {
}
}
- implicit lazy val booleanOptionReader: JsonReader[Option[Boolean]] =
+ implicit val booleanOptionReader: JsonReader[Option[Boolean]] =
new OptionJsonReader[Boolean] {
override protected def readSomeValue(
it: TokenIterator
diff --git a/modules/core/src/main/scala/tethys/readers/tokens/ByteArrayAccess.java b/modules/core/src/main/scala/tethys/readers/tokens/ByteArrayAccess.java
new file mode 100644
index 00000000..c3309a1f
--- /dev/null
+++ b/modules/core/src/main/scala/tethys/readers/tokens/ByteArrayAccess.java
@@ -0,0 +1,46 @@
+package tethys.readers.tokens;
+
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.VarHandle;
+import java.nio.ByteOrder;
+
+class ByteArrayAccess { // FIXME: Use Java wrapper as w/a for missing support of @PolymorphicSignature methods in Scala 3, see: https://github.com/lampepfl/dotty/issues/11332
+ private static final VarHandle VH_LONG =
+ MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.LITTLE_ENDIAN);
+ private static final VarHandle VH_INT =
+ MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.LITTLE_ENDIAN);
+ private static final VarHandle VH_SHORT =
+ MethodHandles.byteArrayViewVarHandle(short[].class, ByteOrder.LITTLE_ENDIAN);
+ private static final VarHandle VH_LONG_REVERSED =
+ MethodHandles.byteArrayViewVarHandle(long[].class, ByteOrder.BIG_ENDIAN);
+ private static final VarHandle VH_INT_REVERSED =
+ MethodHandles.byteArrayViewVarHandle(int[].class, ByteOrder.BIG_ENDIAN);
+
+ static void setLong(byte[] buf, int pos, long value) {
+ VH_LONG.set(buf, pos, value);
+ }
+
+ static long getLong(byte[] buf, int pos) {
+ return (long) VH_LONG.get(buf, pos);
+ }
+
+ static void setInt(byte[] buf, int pos, int value) {
+ VH_INT.set(buf, pos, value);
+ }
+
+ static int getInt(byte[] buf, int pos) {
+ return (int) VH_INT.get(buf, pos);
+ }
+
+ static void setShort(byte[] buf, int pos, short value) {
+ VH_SHORT.set(buf, pos, value);
+ }
+
+ static void setLongReversed(byte[] buf, int pos, long value) {
+ VH_LONG_REVERSED.set(buf, pos, value);
+ }
+
+ static int getIntReversed(byte[] buf, int pos) {
+ return (int) VH_INT_REVERSED.get(buf, pos);
+ }
+}
diff --git a/modules/core/src/main/scala/tethys/readers/tokens/DefaultTokenIterator.scala b/modules/core/src/main/scala/tethys/readers/tokens/DefaultTokenIterator.scala
new file mode 100644
index 00000000..7095b63e
--- /dev/null
+++ b/modules/core/src/main/scala/tethys/readers/tokens/DefaultTokenIterator.scala
@@ -0,0 +1,2139 @@
+package tethys.readers.tokens
+
+import tethys.commons.Token
+import java.math.MathContext
+
+import java.io.InputStream
+import java.nio.ByteBuffer
+import scala.annotation.tailrec
+import scala.util.control.NoStackTrace
+
+final class DefaultTokenIterator(
+ private var buf: Array[Byte] = new Array[Byte](32768),
+ private var head: Int = 0,
+ private var tail: Int = 0,
+ private var mark: Int = -1,
+ private var charBuf: Array[Char] = new Array[Char](4096),
+ private var bbuf: ByteBuffer = null,
+ private var in: InputStream = null,
+ private var totalRead: Long = 0,
+ private var config: ReaderConfig = null
+) extends TokenIterator
+ with BaseTokenIterator {
+ import DefaultTokenIterator._
+
+ private var magnitude: Array[Byte] = _
+ private val objectTrace = new scala.collection.mutable.Stack[Boolean](6)
+ private var token: Token = _
+
+ def init(s: String): this.type = {
+ objectTrace.clear()
+ this.buf = s.getBytes()
+ this.config = config
+ head = 0
+ val to = buf.length
+ tail = to
+ totalRead = 0
+ mark = -1
+ token = nextTethysToken(null)
+ this
+ }
+
+ override def next(): this.type = {
+ token = nextTethysToken(token)
+ this
+ }
+
+ override def currentToken(): Token = token
+
+ override def nextToken(): Token = try {
+ val b = searchNextToken(head)
+ val next =
+ if (
+ b == '"' &&
+ objectTrace.nonEmpty
+ && objectTrace.head &&
+ token != null &&
+ !token.isFieldName
+ )
+ head -= 1
+ Token.FieldNameToken
+ else if (b == '"')
+ head -= 1
+ Token.StringValueToken
+ else if ((b >= '0' && b <= '9') || b == '-')
+ head -= 1
+ Token.NumberValueToken
+ else if (b == 'n') readNullOrError(Token.NullValueToken, "expected null")
+ else if (b == 'f' || b == 't')
+ head -= 1
+ Token.BooleanValueToken
+ else if (b == '[')
+ objectTrace.push(false)
+ Token.ArrayStartToken
+ else if (b == ']')
+ objectTrace.pop()
+ Token.ArrayEndToken
+ else if (b == '{')
+ objectTrace.push(true)
+ Token.ObjectStartToken
+ else if (b == '}')
+ objectTrace.pop()
+ Token.ObjectEndToken
+ else decodeError("expected value")
+ token = next
+ next
+ } catch case _: EndOfInputError => Token.Empty
+
+ @tailrec
+ private def searchNextToken(pos: Int): Byte =
+ if (pos < tail) {
+ val b = buf(pos)
+ if (b == ' ' || b == ',' || b == '\n' || (b | 0x4) == '\r')
+ searchNextToken(pos + 1)
+ else {
+ head = pos + 1
+ b
+ }
+ } else searchNextToken(loadMoreOrError(pos))
+
+ private def decodeError(msg: String, bs: Int, pos: Int): Nothing =
+ decodeError(
+ msg,
+ (java.lang.Integer.numberOfTrailingZeros(bs ^ 0x6c6c756e) >> 3) + pos - 1
+ )
+
+ private def decodeError(
+ from: Int,
+ pos: Int,
+ cause: Throwable
+ ): Nothing = {
+ var i = appendString(", offset: 0x", from)
+ val offset =
+ if ((bbuf eq null) && (in eq null)) 0
+ else totalRead - tail
+ i = appendHexOffset(offset + pos, i)
+ if (config.appendHexDumpToParseException) {
+ i = appendString(", buf:", i)
+ i = appendHexDump(pos, offset.toInt, i)
+ }
+ throw new JsonReaderException(
+ new String(charBuf, 0, i),
+ cause,
+ config.throwReaderExceptionWithStackTrace
+ )
+ }
+
+ private def appendHexDump(pos: Int, offset: Int, from: Int): Int = {
+ val hexDumpSizeInBytes = config.hexDumpSize << 4
+ val start = Math.max(pos - hexDumpSizeInBytes & 0xfffffff0, 0)
+ val end = Math.min(pos + hexDumpSizeInBytes + 16 & 0xfffffff0, tail)
+ val alignedAbsFrom = start + offset & 0xfffffff0
+ val alignedAbsTo = end + offset + 15 & 0xfffffff0
+ val len = alignedAbsTo - alignedAbsFrom
+ val bufOffset = alignedAbsFrom - offset
+ var i = appendChars(dumpBorder, from)
+ i = appendChars(dumpHeader, i)
+ i = appendChars(dumpBorder, i)
+ val buf = this.buf
+ val ds = hexDigits
+ var charBuf = this.charBuf
+ var lim = charBuf.length
+ var j = 0
+ while (j < len) {
+ val linePos = j & 0xf
+ if (linePos == 0) {
+ if (i + 81 >= lim) { // 81 == dumpBorder.length
+ lim = growCharBuf(i + 81)
+ charBuf = this.charBuf
+ }
+ charBuf(i) = '\n'
+ charBuf(i + 1) = '|'
+ charBuf(i + 2) = ' '
+ putHexInt(alignedAbsFrom + j, i + 3, charBuf, ds)
+ charBuf(i + 11) = ' '
+ charBuf(i + 12) = '|'
+ charBuf(i + 13) = ' '
+ i += 14
+ }
+ val pos = bufOffset + j
+ charBuf(i + 50 - (linePos << 1)) = if (pos >= start && pos < end) {
+ val b = buf(pos)
+ charBuf(i) = ds(b >> 4 & 0xf)
+ charBuf(i + 1) = ds(b & 0xf)
+ charBuf(i + 2) = ' '
+ if (b <= 31 || b >= 127) '.'
+ else b.toChar
+ } else {
+ charBuf(i) = ' '
+ charBuf(i + 1) = ' '
+ charBuf(i + 2) = ' '
+ ' '
+ }
+ i += 3
+ if (linePos == 15) {
+ charBuf(i) = '|'
+ charBuf(i + 1) = ' '
+ charBuf(i + 18) = ' '
+ charBuf(i + 19) = '|'
+ i += 20
+ }
+ j += 1
+ }
+ appendChars(dumpBorder, i)
+ }
+
+ private def appendChar(ch: Char, i: Int): Int = {
+ ensureCharBufCapacity(i + 1)
+ charBuf(i) = ch
+ i + 1
+ }
+
+ private def appendChars(cs: Array[Char], i: Int): Int = {
+ val len = cs.length
+ val required = i + len
+ ensureCharBufCapacity(required)
+ System.arraycopy(cs, 0, charBuf, i, len)
+ required
+ }
+
+ private def appendHexOffset(d: Long, i: Int): Int = {
+ ensureCharBufCapacity(i + 16)
+ val ds = hexDigits
+ var j = i
+ val dl = d.toInt
+ if (dl != d) {
+ val dh = (d >> 32).toInt
+ var shift = 32 - java.lang.Integer.numberOfLeadingZeros(dh) & 0x1c
+ while (shift >= 0) {
+ charBuf(j) = ds(dh >> shift & 0xf)
+ shift -= 4
+ j += 1
+ }
+ }
+ putHexInt(dl, j, charBuf, ds)
+ j + 8
+ }
+
+ private def appendHexByte(b: Byte, i: Int, ds: Array[Char]): Int = {
+ ensureCharBufCapacity(i + 2)
+ charBuf(i) = ds(b >> 4 & 0xf)
+ charBuf(i + 1) = ds(b & 0xf)
+ i + 2
+ }
+
+ private def putHexInt(
+ d: Int,
+ i: Int,
+ charBuf: Array[Char],
+ ds: Array[Char]
+ ): Unit = {
+ charBuf(i) = ds(d >>> 28)
+ charBuf(i + 1) = ds(d >> 24 & 0xf)
+ charBuf(i + 2) = ds(d >> 20 & 0xf)
+ charBuf(i + 3) = ds(d >> 16 & 0xf)
+ charBuf(i + 4) = ds(d >> 12 & 0xf)
+ charBuf(i + 5) = ds(d >> 8 & 0xf)
+ charBuf(i + 6) = ds(d >> 4 & 0xf)
+ charBuf(i + 7) = ds(d & 0xf)
+ }
+
+ private def appendString(s: String, i: Int): Int = {
+ val len = s.length
+ val required = i + len
+ ensureCharBufCapacity(required)
+ s.getChars(0, len, charBuf, i)
+ required
+ }
+
+ private def ensureCharBufCapacity(required: Int): Unit =
+ if (charBuf.length < required) growCharBuf(required): Unit
+
+ private def growCharBuf(required: Int): Int = {
+ var charBufLen = charBuf.length
+ val maxCharBufSize = config.maxCharBufSize
+ if (charBufLen == maxCharBufSize) tooLongStringError()
+ charBufLen =
+ (-1 >>> Integer.numberOfLeadingZeros(charBufLen | required)) + 1
+ if (Integer.compareUnsigned(charBufLen, maxCharBufSize) > 0)
+ charBufLen = maxCharBufSize
+ charBuf = java.util.Arrays.copyOf(charBuf, charBufLen)
+ charBufLen
+ }
+
+ private def tooLongInputError(): Nothing =
+ decodeError("too long part of input exceeded 'maxBufSize'", tail)
+
+ private def tooLongStringError(): Nothing =
+ decodeError("too long string exceeded 'maxCharBufSize'", tail)
+
+ def decodeError(msg: String): Nothing = decodeError(msg, head - 1)
+
+ private def decodeError(
+ msg: String,
+ pos: Int,
+ cause: Throwable = null
+ ): Nothing =
+ decodeError(appendString(msg, 0), pos, cause)
+
+ private def loadMoreOrError(pos: Int): Int = {
+ if ((bbuf eq null) && (in eq null)) throw EndOfInputError()
+ loadMore(pos, throwOnEndOfInput = true)
+ }
+
+ private def loadMore(pos: Int): Int =
+ if ((bbuf eq null) && (in eq null)) pos
+ else loadMore(pos, throwOnEndOfInput = false)
+
+ private def loadMore(pos: Int, throwOnEndOfInput: Boolean): Int = {
+ var newPos = pos
+ val offset =
+ if (mark < 0) pos
+ else mark
+ if (offset > 0) {
+ newPos -= offset
+ val buf = this.buf
+ val remaining = tail - offset
+ var i = 0
+ while (i < remaining) {
+ buf(i) = buf(i + offset)
+ i += 1
+ }
+ if (mark > 0) mark = 0
+ tail = remaining
+ head = newPos
+ } else growBuf()
+ var len = buf.length - tail
+ if (bbuf ne null) {
+ len = Math.min(bbuf.remaining, len)
+ bbuf.get(buf, tail, len)
+ } else len = Math.max(in.read(buf, tail, len), 0)
+ if (throwOnEndOfInput && len == 0) throw EndOfInputError()
+ tail += len
+ totalRead += len
+ newPos
+ }
+
+ private class EndOfInputError extends RuntimeException with NoStackTrace
+
+ private def growBuf(): Unit = {
+ var bufLen = buf.length
+ val maxBufSize = config.maxBufSize
+ if (bufLen == maxBufSize) tooLongInputError()
+ bufLen <<= 1
+ if (Integer.compareUnsigned(bufLen, maxBufSize) > 0) bufLen = maxBufSize
+ buf = java.util.Arrays.copyOf(buf, bufLen)
+ }
+
+ override def fieldName(): String = readKeyAsString()
+
+ /** Reads a JSON key into the internal char buffer and returns a `String`
+ * instance.
+ *
+ * @return
+ * a `String` instance of the parsed JSON key
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or invalid encoding of JSON key
+ */
+ private def readKeyAsString(): String = {
+ nextTokenOrError('"', head)
+ val pos = head
+ val len = parseString(0, Math.min(tail - pos, charBuf.length), charBuf, pos)
+ nextTokenOrError(':', head)
+ new String(charBuf, 0, len)
+ }
+
+ @tailrec
+ private def nextTokenOrError(t: Byte, pos: Int): Unit =
+ if (pos < tail) {
+ val b = buf(pos)
+ head = pos + 1
+ if (
+ b != t && ((b != ' ' && b != '\n' && (b | 0x4) != '\r') || searchNextToken(
+ pos + 1
+ ) != t)
+ ) tokenError(t, head - 1)
+ } else nextTokenOrError(t, loadMoreOrError(pos))
+
+ override def string(): String = readString(null)
+
+ /** Reads a JSON string value into a `String` instance. In case of `null` JSON
+ * value returns the provided default value or throws a
+ * [[JsonReaderException]] if the provided default value is `null`.
+ *
+ * @param default
+ * the default `String` value to return if the JSON value is `null`.
+ * @return
+ * a `String` instance of the parsed JSON value or the default value if the
+ * JSON value is `null`.
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or invalid encoding of JSON value
+ * or when both the JSON value and the provided default value are `null`
+ */
+ private def readString(default: String): String = {
+ val pos = head + 1
+ val len =
+ parseString(0, Math.min(tail - pos, charBuf.length), charBuf, pos)
+ new String(charBuf, 0, len)
+ }
+
+ @tailrec
+ private def parseString(
+ i: Int,
+ minLim: Int,
+ charBuf: Array[Char],
+ pos: Int
+ ): Int =
+ if (i + 3 < minLim) { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456
+ val bs = ByteArrayAccess.getInt(buf, pos)
+ val m =
+ ((bs - 0x20202020 ^ 0x3c3c3c3c) - 0x1010101 | (bs ^ 0x5d5d5d5d) + 0x1010101) & 0x80808080
+ charBuf(i) = (bs & 0xff).toChar
+ charBuf(i + 1) = (bs >> 8 & 0xff).toChar
+ charBuf(i + 2) = (bs >> 16 & 0xff).toChar
+ charBuf(i + 3) = (bs >> 24).toChar
+ if (m != 0) {
+ val offset = java.lang.Integer.numberOfTrailingZeros(m) >> 3
+ if ((bs >> (offset << 3)).toByte == '"') {
+ head = pos + offset + 1
+ i + offset
+ } else
+ parseEncodedString(
+ i + offset,
+ charBuf.length - 1,
+ charBuf,
+ pos + offset
+ )
+ } else parseString(i + 4, minLim, charBuf, pos + 4)
+ } else if (i < minLim) {
+ val b = buf(pos)
+ charBuf(i) = b.toChar
+ if (b == '"') {
+ head = pos + 1
+ i
+ } else if ((b - 0x20 ^ 0x3c) <= 0)
+ parseEncodedString(i, charBuf.length - 1, charBuf, pos)
+ else parseString(i + 1, minLim, charBuf, pos + 1)
+ } else if (pos >= tail) {
+ val newPos = loadMoreOrError(pos)
+ parseString(
+ i,
+ Math.min(charBuf.length, i + tail - newPos),
+ charBuf,
+ newPos
+ )
+ } else
+ parseString(
+ i,
+ Math.min(growCharBuf(i + 1), i + tail - pos),
+ this.charBuf,
+ pos
+ )
+
+ @tailrec
+ private def parseEncodedString(
+ i: Int,
+ lim: Int,
+ charBuf: Array[Char],
+ pos: Int
+ ): Int = {
+ val remaining = tail - pos
+ if (i < lim) {
+ if (remaining > 0) {
+ val b1 = buf(pos)
+ if (b1 >= 0) {
+ if (b1 == '"') {
+ head = pos + 1
+ i
+ } else if (b1 != '\\') { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char)
+ if (b1 < ' ') unescapedControlCharacterError(pos)
+ charBuf(i) = b1.toChar
+ parseEncodedString(i + 1, lim, charBuf, pos + 1)
+ } else if (remaining > 1) {
+ val b2 = buf(pos + 1)
+ if (b2 != 'u') {
+ charBuf(i) = (b2: @scala.annotation.switch) match {
+ case '"' => '"'
+ case 'n' => '\n'
+ case 'r' => '\r'
+ case 't' => '\t'
+ case 'b' => '\b'
+ case 'f' => '\f'
+ case '\\' => '\\'
+ case '/' => '/'
+ case _ => escapeSequenceError(pos + 1)
+ }
+ parseEncodedString(i + 1, lim, charBuf, pos + 2)
+ } else if (remaining > 5) {
+ val ch1 = readEscapedUnicode(pos + 2, buf)
+ charBuf(i) = ch1
+ if ((ch1 & 0xf800) != 0xd800)
+ parseEncodedString(i + 1, lim, charBuf, pos + 6)
+ else if (remaining > 11) {
+ if (buf(pos + 6) != '\\') escapeSequenceError(pos + 6)
+ if (buf(pos + 7) != 'u') escapeSequenceError(pos + 7)
+ val ch2 = readEscapedUnicode(pos + 8, buf)
+ charBuf(i + 1) = ch2
+ if (ch1 >= 0xdc00 || (ch2 & 0xfc00) != 0xdc00)
+ decodeError("illegal surrogate character pair", pos + 11)
+ parseEncodedString(i + 2, lim, charBuf, pos + 12)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else if ((b1 & 0xe0) == 0xc0) { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char)
+ if (remaining > 1) {
+ val b2 = buf(pos + 1)
+ val ch =
+ (b1 << 6 ^ b2 ^ 0xf80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte
+ charBuf(i) = ch
+ if ((b2 & 0xc0) != 0x80 || ch < 0x80)
+ malformedBytesError(b1, b2, pos)
+ parseEncodedString(i + 1, lim, charBuf, pos + 2)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else if ((b1 & 0xf0) == 0xe0) { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char)
+ if (remaining > 2) {
+ val b2 = buf(pos + 1)
+ val b3 = buf(pos + 2)
+ val ch =
+ (b1 << 12 ^ b2 << 6 ^ b3 ^ 0x1f80).toChar // 0x1F80 == (0x80.toByte << 6 ^ 0x80.toByte).toChar
+ charBuf(i) = ch
+ if (
+ (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || ch < 0x800 ||
+ (ch & 0xf800) == 0xd800
+ ) malformedBytesError(b1, b2, b3, pos)
+ parseEncodedString(i + 1, lim, charBuf, pos + 3)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else if ((b1 & 0xf8) == 0xf0) { // 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars), where uuuu = ddddd - 1
+ if (remaining > 3) {
+ val b2 = buf(pos + 1)
+ val b3 = buf(pos + 2)
+ val b4 = buf(pos + 3)
+ val cp =
+ b1 << 18 ^ b2 << 12 ^ b3 << 6 ^ b4 ^ 0x381f80 // 0x381F80 == 0xF0.toByte << 18 ^ 0x80.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte
+ val ch1 =
+ ((cp >>> 10) + 0xd7c0).toChar // 0xD7C0 == 0xD800 - (0x10000 >>> 10)
+ charBuf(i) = ch1
+ charBuf(i + 1) = ((cp & 0x3ff) | 0xdc00).toChar
+ if (
+ (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || (b4 & 0xc0) != 0x80 ||
+ (ch1 & 0xf800) != 0xd800
+ ) malformedBytesError(b1, b2, b3, b4, pos)
+ parseEncodedString(i + 2, lim, charBuf, pos + 4)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else malformedBytesError(b1, pos)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else
+ parseEncodedString(
+ i,
+ growCharBuf(i + 2) - 1,
+ this.charBuf,
+ pos
+ ) // 2 is length of surrogate pair
+ }
+
+ private def readEscapedUnicode(pos: Int, buf: Array[Byte]): Char = {
+ val ns = nibbles
+ val x =
+ ns(buf(pos) & 0xff) << 12 |
+ ns(buf(pos + 1) & 0xff) << 8 |
+ ns(buf(pos + 2) & 0xff) << 4 |
+ ns(buf(pos + 3) & 0xff)
+ if (x < 0) hexDigitError(pos)
+ x.toChar
+ }
+
+ @tailrec
+ private def hexDigitError(pos: Int): Nothing = {
+ if (nibbles(buf(pos) & 0xff) < 0) decodeError("expected hex digit", pos)
+ hexDigitError(pos + 1)
+ }
+
+ private def malformedBytesError(b1: Byte, pos: Int): Nothing = {
+ var i = appendString("malformed byte(s): 0x", 0)
+ i = appendHexByte(b1, i, hexDigits)
+ decodeError(i, pos, null)
+ }
+
+ private def malformedBytesError(
+ b1: Byte,
+ b2: Byte,
+ pos: Int
+ ): Nothing = {
+ val ds = hexDigits
+ var i = appendString("malformed byte(s): 0x", 0)
+ i = appendHexByte(b1, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b2, i, ds)
+ decodeError(i, pos + 1, null)
+ }
+
+ private def malformedBytesError(
+ b1: Byte,
+ b2: Byte,
+ b3: Byte,
+ pos: Int
+ ): Nothing = {
+ val ds = hexDigits
+ var i = appendString("malformed byte(s): 0x", 0)
+ i = appendHexByte(b1, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b2, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b3, i, ds)
+ decodeError(i, pos + 2, null)
+ }
+
+ private def malformedBytesError(
+ b1: Byte,
+ b2: Byte,
+ b3: Byte,
+ b4: Byte,
+ pos: Int
+ ): Nothing = {
+ val ds = hexDigits
+ var i = appendString("malformed byte(s): 0x", 0)
+ i = appendHexByte(b1, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b2, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b3, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b4, i, ds)
+ decodeError(i, pos + 3, null)
+ }
+
+ override def number(): Number = readBigDecimal(
+ isToken = true,
+ null,
+ bigDecimalMathContext,
+ bigDecimalScaleLimit,
+ bigDecimalDigitsLimit
+ )
+
+ private def readBigDecimal(
+ isToken: Boolean,
+ default: BigDecimal,
+ mc: MathContext,
+ scaleLimit: Int,
+ digitsLimit: Int
+ ): BigDecimal = {
+ var b =
+ if (isToken) searchNextToken(head)
+ else nextByte(head)
+ if (isToken && b == 'n') readNullOrNumberError(default, head)
+ else {
+ var s = 0
+ if (b == '-') {
+ b = nextByte(head)
+ s = -1
+ }
+ if (b < '0' || b > '9') numberError()
+ var pos = head
+ var buf = this.buf
+ var from = pos - 1
+ val oldMark = mark
+ val newMark =
+ if (oldMark < 0) from
+ else oldMark
+ mark = newMark
+ var digits = 1
+ if (isToken && b == '0') {
+ if (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) leadingZeroError(pos - 1)
+ } else {
+ digits -= pos
+ var m, bs = 0L
+ while (
+ (pos + 7 < tail || {
+ digits += pos
+ pos = loadMore(pos)
+ digits -= pos
+ buf = this.buf
+ pos + 7 < tail
+ }) && {
+ bs = ByteArrayAccess.getLong(
+ buf,
+ pos
+ ) // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ m = (bs + 0x4646464646464646L | bs - 0x3030303030303030L) & 0x8080808080808080L
+ m == 0
+ }
+ ) pos += 8
+ if (m == 0) {
+ while (
+ (pos < tail || {
+ digits += pos
+ pos = loadMore(pos)
+ digits -= pos
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) pos += 1
+ } else {
+ val offset = java.lang.Long.numberOfTrailingZeros(m) >> 3
+ pos += offset
+ b = (bs >> (offset << 3)).toByte
+ }
+ digits += pos
+ }
+ var fracLen, scale = 0
+ if (digits >= digitsLimit)
+ digitsLimitError(pos + digitsLimit - digits - 1)
+ if (b == '.') {
+ pos += 1
+ fracLen -= pos
+ var m, bs = 0L
+ while (
+ (pos + 7 < tail || {
+ fracLen += pos
+ pos = loadMore(pos)
+ fracLen -= pos
+ buf = this.buf
+ pos + 7 < tail
+ }) && {
+ bs = ByteArrayAccess.getLong(
+ buf,
+ pos
+ ) // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ m = (bs + 0x4646464646464646L | bs - 0x3030303030303030L) & 0x8080808080808080L
+ m == 0
+ }
+ ) pos += 8
+ if (m == 0) {
+ while (
+ (pos < tail || {
+ fracLen += pos
+ pos = loadMore(pos)
+ fracLen -= pos
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) pos += 1
+ } else {
+ val offset = java.lang.Long.numberOfTrailingZeros(m) >> 3
+ pos += offset
+ b = (bs >> (offset << 3)).toByte
+ }
+ fracLen += pos
+ digits += fracLen
+ if (fracLen == 0) numberError(pos)
+ if (digits >= digitsLimit)
+ digitsLimitError(pos + digitsLimit - digits - 1)
+ }
+ if ((b | 0x20) == 'e') {
+ b = nextByte(pos + 1)
+ var ss = 0
+ if (b == '-' || b == '+') {
+ ss = '+' - b >> 31
+ b = nextByte(head)
+ }
+ if (b < '0' || b > '9') numberError()
+ scale = '0' - b
+ pos = head
+ buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (
+ scale < -214748364 || {
+ scale = scale * 10 + ('0' - b)
+ scale > 0
+ }
+ ) numberError(pos)
+ pos += 1
+ }
+ scale ^= ss
+ scale -= ss
+ if (scale == -2147483648) numberError(pos - 1)
+ }
+ head = pos
+ if (mark == 0) from -= newMark
+ if (mark > oldMark) mark = oldMark
+ var d =
+ if (fracLen != 0) {
+ val limit = from + digits + 1
+ val fracPos = limit - fracLen
+ val fracLimit = fracPos - 1
+ if (digits < 19) {
+ var x = (buf(from) - '0').toLong
+ from += 1
+ while (from < fracLimit) {
+ x = x * 10 + (buf(from) - '0')
+ from += 1
+ }
+ from += 1
+ while (from < limit) {
+ x = x * 10 + (buf(from) - '0')
+ from += 1
+ }
+ java.math.BigDecimal.valueOf((x ^ s) - s, scale + fracLen)
+ } else
+ toBigDecimal(buf, from, fracLimit, s, scale).add(
+ toBigDecimal(buf, fracPos, limit, s, scale + fracLen)
+ )
+ } else toBigDecimal(buf, from, from + digits, s, scale)
+ if (mc.getPrecision < digits) d = d.plus(mc)
+ if (Math.abs(d.scale) >= scaleLimit) scaleLimitError()
+ new BigDecimal(d, mc)
+ }
+ }
+
+ @tailrec
+ private def readNullOrNumberError[@specialized A](
+ default: A,
+ pos: Int
+ ): A =
+ if (default != null) {
+ if (pos + 2 < tail) {
+ val bs = ByteArrayAccess.getInt(buf, pos - 1)
+ if (bs == 0x6c6c756e) {
+ head = pos + 3
+ default
+ } else decodeError("expected number or null", bs, pos)
+ } else readNullOrNumberError(default, loadMoreOrError(pos - 1) + 1)
+ } else numberError(pos - 1)
+
+ private def toBigDecimal(
+ buf: Array[Byte],
+ p: Int,
+ limit: Int,
+ s: Int,
+ scale: Int
+ ): java.math.BigDecimal = {
+ val len = limit - p
+ if (len < 19) {
+ var pos = p
+ var x = (buf(pos) - '0').toLong
+ pos += 1
+ while (pos < limit) {
+ x = x * 10 + (buf(pos) - '0')
+ pos += 1
+ }
+ java.math.BigDecimal.valueOf((x ^ s) - s, scale)
+ } else if (len <= 36) toBigDecimal36(buf, p, limit, s, scale)
+ else if (len <= 308)
+ new java.math.BigDecimal(toBigInteger308(buf, p, limit, s), scale)
+ else {
+ // Based on the great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing really big numbers
+ // with O(n^1.5) complexity instead of O(n^2) when using the constructor for the decimal representation from JDK:
+ // https://github.com/eobermuhlner/big-math/commit/7a5419aac8b2adba2aa700ccf00197f97b2ad89f
+ val mid = len >> 1
+ val midPos = limit - mid
+ toBigDecimal(buf, p, midPos, s, scale - mid).add(
+ toBigDecimal(buf, midPos, limit, s, scale)
+ )
+ }
+ }
+
+ private def toBigDecimal36(
+ buf: Array[Byte],
+ p: Int,
+ limit: Int,
+ s: Int,
+ scale: Int
+ ): java.math.BigDecimal = {
+ val firstBlockLimit = limit - 18
+ var pos = p
+ var x1 = (buf(pos) - '0').toLong
+ pos += 1
+ while (pos < firstBlockLimit) {
+ x1 = x1 * 10 + (buf(pos) - '0')
+ pos += 1
+ }
+ val x2 =
+ ({ // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 8)) * 1000000000 + {
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos + 9) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 17) - 48000000048L
+ val q = x1 * 1000000000000000000L
+ val l = q + x2
+ val h = Math.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
+ if (l >= 0 && h == 0) java.math.BigDecimal.valueOf((l ^ s) - s, scale)
+ else {
+ var magnitude = this.magnitude
+ if (magnitude eq null) {
+ magnitude = new Array[Byte](128)
+ this.magnitude = magnitude
+ }
+ ByteArrayAccess.setLongReversed(magnitude, 0, h)
+ ByteArrayAccess.setLongReversed(magnitude, 8, l)
+ new java.math.BigDecimal(
+ new java.math.BigInteger(s | 1, magnitude, 0, 16),
+ scale
+ )
+ }
+ }
+
+ private def toBigInteger308(
+ buf: Array[Byte],
+ p: Int,
+ limit: Int,
+ s: Int
+ ): java.math.BigInteger = {
+ val len = limit - p
+ val last =
+ (len * 222930821L >> 32).toInt << 3 // (len * Math.log(10) / Math.log(1L << 64)).toInt * 8
+ var magnitude = this.magnitude
+ if (magnitude eq null) {
+ magnitude = new Array[Byte](128)
+ this.magnitude = magnitude
+ } else {
+ var i = 0
+ while (i < last) {
+ ByteArrayAccess.setLong(magnitude, i, 0L)
+ i += 8
+ }
+ }
+ var x = 0L
+ val firstBlockLimit = len % 18 + p
+ var pos = p
+ while (pos < firstBlockLimit) {
+ x = x * 10 + (buf(pos) - '0')
+ pos += 1
+ }
+ ByteArrayAccess.setLong(magnitude, last, x)
+ var first = last
+ while (pos < limit) {
+ x =
+ ({ // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 8)) * 1000000000 + {
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos + 9) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 17) - 48000000048L
+ pos += 18
+ first = Math.max(first - 8, 0)
+ var i = last
+ val q = 1000000000000000000L
+ var m, mq = 0L
+ while ({
+ m = ByteArrayAccess.getLong(magnitude, i)
+ mq = m * q
+ x += mq
+ ByteArrayAccess.setLong(magnitude, i, x)
+ i -= 8
+ i >= first
+ }) {
+ x = Math.multiplyHigh(
+ m,
+ q
+ ) + (m >> 63 & q) + ((~x & mq) >>> 63) // TODO: when dropping JDK 17 support replace by Math.unsignedMultiplyHigh(m, q) + ((~x & mq) >>> 63)
+ }
+ }
+ var i = 0
+ while (i <= last) {
+ ByteArrayAccess.setLongReversed(
+ magnitude,
+ i,
+ ByteArrayAccess.getLong(magnitude, i)
+ )
+ i += 8
+ }
+ new java.math.BigInteger(s | 1, magnitude, 0, last + 8)
+ }
+
+ override def byte(): Byte = readByte(isToken = true)
+
+ private def readByte(isToken: Boolean): Byte = {
+ var b =
+ if (isToken) searchNextToken(head)
+ else nextByte(head)
+ var s = 0
+ if (b == '-') {
+ b = nextByte(head)
+ s = -1
+ }
+ if (b < '0' || b > '9') numberError()
+ var x = b - '0'
+ if (isToken && x == 0) ensureNotLeadingZero()
+ else {
+ var pos = head
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ x = x * 10 + (b - '0')
+ if (x > 128) byteOverflowError(pos)
+ pos += 1
+ }
+ head = pos
+ x ^= s
+ x -= s
+ if (x == 128) byteOverflowError(pos - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ }
+ x.toByte
+ }
+
+ override def short(): Short = readShort(isToken = true)
+
+ private def readShort(isToken: Boolean): Short = {
+ var b =
+ if (isToken) searchNextToken(head)
+ else nextByte(head)
+ var s = 0
+ if (b == '-') {
+ b = nextByte(head)
+ s = -1
+ }
+ if (b < '0' || b > '9') numberError()
+ var x = b - '0'
+ if (isToken && x == 0) ensureNotLeadingZero()
+ else {
+ var pos = head
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ x = x * 10 + (b - '0')
+ if (x > 32768) shortOverflowError(pos)
+ pos += 1
+ }
+ head = pos
+ x ^= s
+ x -= s
+ if (x == 32768) shortOverflowError(pos - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ }
+ x.toShort
+ }
+
+ override def int(): Int = readInt(isToken = true)
+
+ private def readInt(isToken: Boolean): Int = {
+ var b =
+ if (isToken) searchNextToken(head)
+ else nextByte(head)
+ var s = -1
+ if (b == '-') {
+ b = nextByte(head)
+ s = 0
+ }
+ if (b < '0' || b > '9') numberError()
+ var x = '0' - b
+ if (isToken && x == 0) ensureNotLeadingZero()
+ else {
+ var pos = head
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (
+ x < -214748364 || {
+ x = x * 10 + ('0' - b)
+ x > 0
+ }
+ ) intOverflowError(pos)
+ pos += 1
+ }
+ head = pos
+ x ^= s
+ x -= s
+ if ((s & x) == -2147483648) intOverflowError(pos - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ }
+ x
+ }
+
+ private def intOverflowError(pos: Int): Nothing =
+ decodeError("value is too large for int", pos)
+
+ private def shortOverflowError(pos: Int): Nothing =
+ decodeError("value is too large for short", pos)
+
+ private def byteOverflowError(pos: Int): Nothing =
+ decodeError("value is too large for byte", pos)
+
+ override def long(): Long = readLong(isToken = true)
+
+ private def readLong(isToken: Boolean): Long = {
+ var b =
+ if (isToken) searchNextToken(head)
+ else nextByte(head)
+ var s = -1L
+ if (b == '-') {
+ b = nextByte(head)
+ s = 0L
+ }
+ if (b < '0' || b > '9') numberError()
+ var x = ('0' - b).toLong
+ if (isToken && x == 0) ensureNotLeadingZero()
+ else {
+ var pos = head
+ var buf = this.buf
+ var dec = 0L
+ while (
+ (pos + 7 < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos + 7 < tail
+ }) && {
+ val bs = ByteArrayAccess.getLong(
+ buf,
+ pos
+ ) // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ dec = bs - 0x3030303030303030L
+ ((bs + 0x4646464646464646L | dec) & 0x8080808080808080L) == 0
+ }
+ ) {
+ if (
+ x < -92233720368L || {
+ dec *= 2561
+ x *= 100000000
+ x -= ((dec >> 8 & 0xff000000ffL) * 4294967296000100L + (dec >> 24 & 0xff000000ffL) * 42949672960001L >> 32)
+ x > 0
+ }
+ ) longOverflowError(pos + 2)
+ pos += 8
+ }
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (
+ x < -922337203685477580L || {
+ x = x * 10 + ('0' - b)
+ x > 0
+ }
+ ) longOverflowError(pos)
+ pos += 1
+ }
+ head = pos
+ x ^= s
+ x -= s
+ if ((s & x) == -9223372036854775808L) longOverflowError(pos - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ }
+ x
+ }
+
+ private def ensureNotLeadingZero(): Unit = {
+ var pos = head
+ if (
+ (pos < tail || {
+ pos = loadMore(pos)
+ pos < tail
+ }) && {
+ val b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) leadingZeroError(pos - 1)
+ }
+
+ override def float() = readFloat(isToken = true)
+
+ private def readFloat(isToken: Boolean): Float = {
+ var b =
+ if (isToken) searchNextToken(head)
+ else nextByte(head)
+ var isNeg = false
+ if (b == '-') {
+ b = nextByte(head)
+ isNeg = true
+ }
+ if (b < '0' || b > '9') numberError()
+ var pos = head
+ var buf = this.buf
+ val from = pos - 1
+ val oldMark = mark
+ val newMark =
+ if (oldMark < 0) from
+ else oldMark
+ mark = newMark
+ var m10 = (b - '0').toLong
+ var e10 = 0
+ var digits = 1
+ if (isToken && m10 == 0) {
+ if (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) leadingZeroError(pos - 1)
+ } else {
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (m10 < 922337203685477580L) {
+ m10 = m10 * 10 + (b - '0')
+ digits += 1
+ } else e10 += 1
+ pos += 1
+ }
+ }
+ if (b == '.') {
+ pos += 1
+ e10 += digits
+ var noFracDigits = true
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (m10 < 922337203685477580L) {
+ m10 = m10 * 10 + (b - '0')
+ digits += 1
+ }
+ noFracDigits = false
+ pos += 1
+ }
+ e10 -= digits
+ if (noFracDigits) numberError(pos)
+ }
+ if ((b | 0x20) == 'e') {
+ b = nextByte(pos + 1)
+ var s = 0
+ if (b == '-' || b == '+') {
+ s = '+' - b >> 31
+ b = nextByte(head)
+ }
+ if (b < '0' || b > '9') numberError()
+ var exp = b - '0'
+ pos = head
+ buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (exp < 214748364) exp = exp * 10 + (b - '0')
+ pos += 1
+ }
+ exp ^= s
+ exp -= s
+ e10 += exp
+ }
+ head = pos
+ var x: Float =
+ if (e10 == 0 && m10 < 922337203685477580L) m10.toFloat
+ else if (m10 < 4294967296L && e10 >= digits - 23 && e10 <= 19 - digits) {
+ val pow10 = pow10Doubles
+ (if (e10 < 0) m10 / pow10(-e10)
+ else m10 * pow10(e10)).toFloat
+ } else toFloat(m10, e10, from, newMark, pos)
+ if (isNeg) x = -x
+ if (mark > oldMark) mark = oldMark
+ x
+ }
+
+ // Based on the 'Moderate Path' algorithm from the awesome library of Alexander Huszagh: https://github.com/Alexhuszagh/rust-lexical
+ // Here is his inspiring post: https://www.reddit.com/r/rust/comments/a6j5j1/making_rust_float_parsing_fast_and_correct
+ private def toFloat(
+ m10: Long,
+ e10: Int,
+ from: Int,
+ newMark: Int,
+ pos: Int
+ ): Float =
+ if (m10 == 0 || e10 < -64) 0.0f
+ else if (e10 >= 39) Float.PositiveInfinity
+ else {
+ var shift = java.lang.Long.numberOfLeadingZeros(m10)
+ var m2 = unsignedMultiplyHigh(
+ pow10Mantissas(e10 + 343),
+ m10 << shift
+ ) // FIXME: Use Math.unsignedMultiplyHigh after dropping of JDK 17 support
+ var e2 =
+ (e10 * 108853 >> 15) - shift + 1 // (e10 * Math.log(10) / Math.log(2)).toInt - shift + 1
+ shift = java.lang.Long.numberOfLeadingZeros(m2)
+ m2 <<= shift
+ e2 -= shift
+ val roundingError =
+ (if (m10 < 922337203685477580L) 1
+ else 19) << shift
+ val truncatedBitNum = Math.max(-149 - e2, 40)
+ val savedBitNum = 64 - truncatedBitNum
+ val mask = -1L >>> Math.max(savedBitNum, 0)
+ val halfwayDiff = (m2 & mask) - (mask >>> 1)
+ if (Math.abs(halfwayDiff) > roundingError || savedBitNum <= 0)
+ java.lang.Float.intBitsToFloat {
+ var mf = 0
+ if (savedBitNum > 0) mf = (m2 >>> truncatedBitNum).toInt
+ e2 += truncatedBitNum
+ if (savedBitNum >= 0 && halfwayDiff > 0) {
+ if (mf == 0xffffff) {
+ mf = 0x800000
+ e2 += 1
+ } else mf += 1
+ }
+ if (e2 == -149) mf
+ else if (e2 >= 105) 0x7f800000
+ else e2 + 150 << 23 | mf & 0x7fffff
+ }
+ else toFloat(from, newMark, pos)
+ }
+
+ private def toFloat(from: Int, newMark: Int, pos: Int): Float = {
+ var offset = from
+ if (mark == 0) offset -= newMark
+ java.lang.Float.parseFloat(new String(buf, 0, offset, pos - offset))
+ }
+
+ override def double() = readDouble(isToken = true)
+
+ private def readDouble(isToken: Boolean): Double = {
+ var b =
+ if (isToken) searchNextToken(head)
+ else nextByte(head)
+ var isNeg = false
+ if (b == '-') {
+ b = nextByte(head)
+ isNeg = true
+ }
+ if (b < '0' || b > '9') numberError()
+ var pos = head
+ var buf = this.buf
+ val from = pos - 1
+ val oldMark = mark
+ val newMark =
+ if (oldMark < 0) from
+ else oldMark
+ mark = newMark
+ var m10 = (b - '0').toLong
+ var e10 = 0
+ var digits = 1
+ if (isToken && m10 == 0) {
+ if (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) leadingZeroError(pos - 1)
+ } else {
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (m10 < 922337203685477580L) {
+ m10 = m10 * 10 + (b - '0')
+ digits += 1
+ } else e10 += 1
+ pos += 1
+ }
+ }
+ if (b == '.') {
+ pos += 1
+ e10 += digits
+ var noFracDigits = true
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (m10 < 922337203685477580L) {
+ m10 = m10 * 10 + (b - '0')
+ digits += 1
+ }
+ noFracDigits = false
+ pos += 1
+ }
+ e10 -= digits
+ if (noFracDigits) numberError(pos)
+ }
+ if ((b | 0x20) == 'e') {
+ b = nextByte(pos + 1)
+ var s = 0
+ if (b == '-' || b == '+') {
+ s = '+' - b >> 31
+ b = nextByte(head)
+ }
+ if (b < '0' || b > '9') numberError()
+ var exp = b - '0'
+ pos = head
+ buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (exp < 214748364) exp = exp * 10 + (b - '0')
+ pos += 1
+ }
+ exp ^= s
+ exp -= s
+ e10 += exp
+ }
+ head = pos
+ var x: Double =
+ if (e10 == 0 && m10 < 922337203685477580L) m10.toDouble
+ else if (m10 < 4503599627370496L && e10 >= -22 && e10 <= 38 - digits) {
+ val pow10 = pow10Doubles
+ if (e10 < 0) m10 / pow10(-e10)
+ else if (e10 <= 22) m10 * pow10(e10)
+ else {
+ val slop = 16 - digits
+ (m10 * pow10(slop)) * pow10(e10 - slop)
+ }
+ } else toDouble(m10, e10, from, newMark, pos)
+ if (isNeg) x = -x
+ if (mark > oldMark) mark = oldMark
+ x
+ }
+
+ // Based on the 'Moderate Path' algorithm from the awesome library of Alexander Huszagh: https://github.com/Alexhuszagh/rust-lexical
+ // Here is his inspiring post: https://www.reddit.com/r/rust/comments/a6j5j1/making_rust_float_parsing_fast_and_correct
+ private def toDouble(
+ m10: Long,
+ e10: Int,
+ from: Int,
+ newMark: Int,
+ pos: Int
+ ): Double =
+ if (m10 == 0 || e10 < -343) 0.0
+ else if (e10 >= 310) Double.PositiveInfinity
+ else {
+ var shift = java.lang.Long.numberOfLeadingZeros(m10)
+ var m2 = unsignedMultiplyHigh(
+ pow10Mantissas(e10 + 343),
+ m10 << shift
+ ) // FIXME: Use Math.unsignedMultiplyHigh after dropping of JDK 17 support
+ var e2 =
+ (e10 * 108853 >> 15) - shift + 1 // (e10 * Math.log(10) / Math.log(2)).toInt - shift + 1
+ shift = java.lang.Long.numberOfLeadingZeros(m2)
+ m2 <<= shift
+ e2 -= shift
+ val roundingError =
+ (if (m10 < 922337203685477580L) 1
+ else 19) << shift
+ val truncatedBitNum = Math.max(-1074 - e2, 11)
+ val savedBitNum = 64 - truncatedBitNum
+ val mask = -1L >>> Math.max(savedBitNum, 0)
+ val halfwayDiff = (m2 & mask) - (mask >>> 1)
+ if (Math.abs(halfwayDiff) > roundingError || savedBitNum <= 0)
+ java.lang.Double.longBitsToDouble {
+ if (savedBitNum <= 0) m2 = 0
+ m2 >>>= truncatedBitNum
+ e2 += truncatedBitNum
+ if (savedBitNum >= 0 && halfwayDiff > 0) {
+ if (m2 == 0x1fffffffffffffL) {
+ m2 = 0x10000000000000L
+ e2 += 1
+ } else m2 += 1
+ }
+ if (e2 == -1074) m2
+ else if (e2 >= 972) 0x7ff0000000000000L
+ else (e2 + 1075).toLong << 52 | m2 & 0xfffffffffffffL
+ }
+ else toDouble(from, newMark, pos)
+ }
+
+ private def unsignedMultiplyHigh(x: Long, y: Long): Long =
+ Math.multiplyHigh(
+ x,
+ y
+ ) + x + y // Use implementation that works only when both params are negative
+
+ private def toDouble(from: Int, newMark: Int, pos: Int): Double = {
+ var offset = from
+ if (mark == 0) offset -= newMark
+ java.lang.Double.parseDouble(new String(buf, 0, offset, pos - offset))
+ }
+
+ override def boolean() = parseBoolean(isToken = true, head)
+
+ override def skipExpression() = {
+ skip(token)
+ token = nextTethysToken(null)
+ this
+ }
+
+ def nextTethysToken(previous: Token): Token = {
+ if (!skipWhitespaces()) return Token.Empty
+ val b = searchNextToken(head)
+ val token =
+ if (
+ b == '"' &&
+ objectTrace.nonEmpty
+ && objectTrace.head &&
+ previous != null &&
+ !previous.isFieldName
+ )
+ rollbackToken()
+ Token.FieldNameToken
+ else if (b == '"')
+ rollbackToken()
+ Token.StringValueToken
+ else if ((b >= '0' && b <= '9') || b == '-')
+ rollbackToken()
+ Token.NumberValueToken
+ else if (b == 'n') readNullOrError(Token.NullValueToken, "expected null")
+ else if (b == 'f' || b == 't')
+ rollbackToken()
+ Token.BooleanValueToken
+ else if (b == '[')
+ objectTrace.push(false)
+ Token.ArrayStartToken
+ else if (b == ']')
+ objectTrace.pop()
+ Token.ArrayEndToken
+ else if (b == '{')
+ objectTrace.push(true)
+ Token.ObjectStartToken
+ else if (b == '}')
+ objectTrace.pop()
+ Token.ObjectEndToken
+ else decodeError("expected value")
+ token
+ }
+
+ /** Skips whitespace characters in the input.
+ *
+ * @return
+ * `true` if and only if there are non-whitespace characters left in the
+ * input after skipping the whitespace
+ */
+ private def skipWhitespaces(): Boolean = {
+ var pos = head
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ val b = buf(pos)
+ b == ' ' || b == '\n' || (b | 0x4) == '\r'
+ }
+ ) pos += 1
+ head = pos
+ pos != tail
+ }
+
+ /** Finishes reading the `null` JSON value and returns the provided default
+ * value or throws [[JsonReaderException]]. Before calling it the `n` token
+ * should be parsed already.
+ *
+ * @param default
+ * the default value to return
+ * @param msg
+ * the exception message
+ * @tparam A
+ * the type of the default value
+ * @return
+ * the default value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or illegal format of JSON value or
+ * when the provided default value is `null`
+ */
+ @tailrec
+ def readNullOrError[@specialized A](default: A, msg: String): A =
+ if (default != null) {
+ val pos = head
+ if (pos != 0) {
+ if (pos + 2 < tail) {
+ val bs = ByteArrayAccess.getInt(buf, pos - 1)
+ if (bs == 0x6c6c756e) {
+ head = pos + 3
+ default
+ } else decodeError(msg, bs, pos)
+ } else if (buf(pos - 1) == 'n') {
+ head = loadMoreOrError(pos - 1) + 1
+ readNullOrError(default, msg)
+ } else decodeError(msg, pos - 1)
+ } else illegalTokenOperation()
+ } else decodeError(msg)
+
+ private def leadingZeroError(pos: Int): Nothing =
+ decodeError("illegal number with leading zero", pos)
+
+ private def numberError(pos: Int = head - 1): Nothing =
+ decodeError("illegal number", pos)
+
+ private def scaleLimitError(pos: Int = head - 1): Nothing =
+ decodeError("value exceeds limit for scale", pos)
+
+ private def digitsLimitError(pos: Int): Nothing =
+ decodeError("value exceeds limit for number of digits", pos)
+
+ private def longOverflowError(pos: Int): Nothing =
+ decodeError("value is too large for long", pos)
+
+ /** Rolls back the current reading position by one.
+ *
+ * @throws java.lang.IllegalStateException
+ * if no any token was parsed yet
+ */
+ def rollbackToken(): Unit = {
+ val pos = head
+ if (pos == 0) illegalTokenOperation()
+ head = pos - 1
+ }
+
+ private def illegalTokenOperation(): Nothing =
+ throw new IllegalStateException(
+ "expected preceding call of 'nextToken()' or 'isNextToken()'"
+ )
+
+ /** Skips the next JSON value.
+ *
+ * @throws JsonReaderException
+ * in cases of reaching the end of input
+ */
+ def skip(current: Token): Unit = {
+ val b: Byte =
+ if (current.isObjectStart)
+ objectTrace.pop()
+ '{'
+ else if (current.isArrayStart)
+ objectTrace.pop()
+ '['
+ else searchNextToken(head)
+
+ var pos = head
+ if (b == '"') pos = skipString(evenBackSlashes = true, pos)
+ else if ((b >= '0' && b <= '9') || b == '-') pos = skipNumber(pos)
+ else if (b == 'n' || b == 't') pos = skipFixedBytes(3, pos)
+ else if (b == 'f') pos = skipFixedBytes(4, pos)
+ else if (b == '[') pos = skipArray(0, pos)
+ else if (b == '{') pos = skipObject(0, pos)
+ else decodeError("expected value")
+ head = pos
+ }
+
+ @tailrec
+ private def skipObject(level: Int, pos: Int): Int =
+ if (pos < tail) {
+ val b = buf(pos)
+ if (b == '"')
+ skipObject(level, skipString(evenBackSlashes = true, pos + 1))
+ else if (b == '{') skipObject(level + 1, pos + 1)
+ else if (b != '}') skipObject(level, pos + 1)
+ else if (level != 0) skipObject(level - 1, pos + 1)
+ else pos + 1
+ } else skipObject(level, loadMoreOrError(pos))
+
+ @tailrec
+ private def skipArray(level: Int, pos: Int): Int =
+ if (pos < tail) {
+ val b = buf(pos)
+ if (b == '"')
+ skipArray(level, skipString(evenBackSlashes = true, pos + 1))
+ else if (b == '[') skipArray(level + 1, pos + 1)
+ else if (b != ']') skipArray(level, pos + 1)
+ else if (level != 0) skipArray(level - 1, pos + 1)
+ else pos + 1
+ } else skipArray(level, loadMoreOrError(pos))
+
+ @tailrec
+ private def skipString(evenBackSlashes: Boolean, pos: Int): Int =
+ if (pos < tail) {
+ if (evenBackSlashes) {
+ val b = buf(pos)
+ if (b == '"') pos + 1
+ else skipString(b != '\\', pos + 1)
+ } else skipString(evenBackSlashes = true, pos + 1)
+ } else skipString(evenBackSlashes, loadMoreOrError(pos))
+
+ private def skipNumber(p: Int): Int = {
+ var pos = p
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ val b = buf(pos)
+ (b >= '0' && b <= '9') || b == '.' || (b | 0x20) == 'e' || b == '-' || b == '+'
+ }
+ ) pos += 1
+ pos
+ }
+
+ @tailrec
+ private def skipFixedBytes(n: Int, pos: Int): Int = {
+ val newPos = pos + n
+ if (newPos <= tail) newPos
+ else skipFixedBytes(n, loadMoreOrError(pos))
+ }
+
+ @tailrec
+ private def parseBoolean(isToken: Boolean, pos: Int): Boolean =
+ if (pos + 3 < tail) {
+ val bs = ByteArrayAccess.getInt(buf, pos)
+ if (bs == 0x65757274) {
+ head = pos + 4
+ true
+ } else if (bs == 0x736c6166) {
+ if (nextByte(pos + 4) != 'e') booleanError(pos + 4)
+ false
+ } else if (
+ isToken && {
+ val b1 = bs.toByte
+ b1 == ' ' || b1 == '\n' || (b1 | 0x4) == '\r'
+ }
+ ) parseBoolean(isToken, pos + 1)
+ else booleanError(bs, pos)
+ } else parseBoolean(isToken, loadMoreOrError(pos))
+
+ private def booleanError(bs: Int, pos: Int): Nothing =
+ booleanError(
+ (Math.max(
+ java.lang.Integer.numberOfTrailingZeros(bs ^ 0x65757274),
+ java.lang.Integer.numberOfTrailingZeros(bs ^ 0x736c6166)
+ ) >> 3) + pos
+ )
+
+ private def escapeSequenceError(pos: Int): Nothing =
+ decodeError("illegal escape sequence", pos)
+
+ private def unescapedControlCharacterError(pos: Int): Nothing =
+ decodeError("unescaped control character", pos)
+
+ private def booleanError(pos: Int): Nothing =
+ decodeError("illegal boolean", pos)
+
+ @tailrec
+ private def nextByte(pos: Int): Byte =
+ if (pos < tail) {
+ head = pos + 1
+ buf(pos)
+ } else nextByte(loadMoreOrError(pos))
+
+ @tailrec
+ private def nextByteOrError(t: Byte, pos: Int): Unit =
+ if (pos < tail) {
+ if (buf(pos) != t) tokenError(t, pos)
+ head = pos + 1
+ } else nextByteOrError(t, loadMoreOrError(pos))
+
+ private def tokenError(t: Byte, pos: Int = head - 1): Nothing = {
+ var i = appendString("expected '", 0)
+ i = appendChar(t.toChar, i)
+ i = appendChar('\'', i)
+ decodeError(i, pos, null)
+ }
+
+}
+
+object DefaultTokenIterator:
+ private final val hexDigits: Array[Char] =
+ Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+ 'e', 'f')
+
+ private final val pow10Doubles: Array[Double] =
+ Array(1, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11,
+ 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 1e+21,
+ 1e+22)
+
+ /* Use the following code to generate `dumpBorder` in Scala REPL:
+ "\n+----------+-------------------------------------------------+------------------+".toCharArray
+ .grouped(16).map(_.map(_.toInt).mkString(", ")).mkString("Array(\n", ",\n", "\n)")
+ */
+ private final val dumpBorder: Array[Char] = Array(
+ 10, 43, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 43, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 43, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 43
+ )
+
+ /* Use the following code to generate `dumpHeader` in Scala REPL:
+ "\n| | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 0123456789abcdef |".toCharArray
+ .grouped(16).map(_.map(_.toInt).mkString(", ")).mkString("Array(\n", ",\n", "\n)")
+ */
+ private final val dumpHeader: Array[Char] = Array(
+ 10, 124, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 124, 32, 32, 48, 32, 32,
+ 49, 32, 32, 50, 32, 32, 51, 32, 32, 52, 32, 32, 53, 32, 32, 54, 32, 32, 55,
+ 32, 32, 56, 32, 32, 57, 32, 32, 97, 32, 32, 98, 32, 32, 99, 32, 32, 100, 32,
+ 32, 101, 32, 32, 102, 32, 124, 32, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 97, 98, 99, 100, 101, 102, 32, 124
+ )
+
+ /* Use the following code to generate `pow10Mantissas` in Scala REPL:
+ val ms = new Array[Long](653)
+ var pow10 = BigInt(10)
+ var i = 342
+ while (i >= 0) {
+ ms(i) = ((BigInt(1) << (pow10.bitLength + 63)) / pow10).longValue
+ pow10 *= 10
+ i -= 1
+ }
+ pow10 = BigInt(1) << 63
+ i = 343
+ while (i < 653) {
+ ms(i) = (pow10 >> (pow10.bitLength - 64)).longValue
+ pow10 *= 10
+ i += 1
+ }
+ ms.grouped(4).map(_.mkString("L, ")).mkString("Array(\n", "L,\n", "L\n)")
+ */
+ private final val pow10Mantissas: Array[Long] = Array(
+ -4671960508600951122L, -1228264617323800998L, -7685194413468457480L,
+ -4994806998408183946L, -1631822729582842029L, -7937418233630358124L,
+ -5310086773610559751L, -2025922448585811785L, -8183730558007214222L,
+ -5617977179081629873L, -2410785455424649437L, -8424269937281487754L,
+ -5918651403174471789L, -2786628235540701832L, -8659171674854020501L,
+ -6212278575140137722L, -3153662200497784248L, -8888567902952197011L,
+ -6499023860262858360L, -3512093806901185046L, -9112587656954322510L,
+ -6779048552765515233L, -3862124672529506138L, -215969822234494768L,
+ -7052510166537641086L, -4203951689744663454L, -643253593753441413L,
+ -7319562523736982739L, -4537767136243840520L, -1060522901877412746L,
+ -7580355841314464822L, -4863758783215693124L, -1468012460592228501L,
+ -7835036815511224669L, -5182110000961642932L, -1865951482774665761L,
+ -8083748704375247957L, -5492999862041672042L, -2254563809124702148L,
+ -8326631408344020699L, -5796603242002637969L, -2634068034075909558L,
+ -8563821548938525330L, -6093090917745768758L, -3004677628754823043L,
+ -8795452545612846258L, -6382629663588669919L, -3366601061058449494L,
+ -9021654690802612790L, -6665382345075878084L, -3720041912917459700L,
+ -38366372719436721L, -6941508010590729807L, -4065198994811024355L,
+ -469812725086392539L, -7211161980820077193L, -4402266457597708587L,
+ -891147053569747830L, -7474495936122174250L, -4731433901725329908L,
+ -1302606358729274481L, -7731658001846878407L, -5052886483881210105L,
+ -1704422086424124727L, -7982792831656159810L, -5366805021142811859L,
+ -2096820258001126919L, -8228041688891786181L, -5673366092687344822L,
+ -2480021597431793123L, -8467542526035952558L, -5972742139117552794L,
+ -2854241655469553088L, -8701430062309552536L, -6265101559459552766L,
+ -3219690930897053053L, -8929835859451740015L, -6550608805887287114L,
+ -3576574988931720989L, -9152888395723407474L, -6829424476226871438L,
+ -3925094576856201394L, -294682202642863838L, -7101705404292871755L,
+ -4265445736938701790L, -720121152745989333L, -7367604748107325189L,
+ -4597819916706768583L, -1135588877456072824L, -7627272076051127371L,
+ -4922404076636521310L, -1541319077368263733L, -7880853450996246689L,
+ -5239380795317920458L, -1937539975720012668L, -8128491512466089774L,
+ -5548928372155224313L, -2324474446766642487L, -8370325556870233411L,
+ -5851220927660403859L, -2702340141148116920L, -8606491615858654931L,
+ -6146428501395930760L, -3071349608317525546L, -8837122532839535322L,
+ -6434717147622031249L, -3431710416100151157L, -9062348037703676329L,
+ -6716249028702207507L, -3783625267450371480L, -117845565885576446L,
+ -6991182506319567135L, -4127292114472071014L, -547429124662700864L,
+ -7259672230555269896L, -4462904269766699466L, -966944318780986428L,
+ -7521869226879198374L, -4790650515171610063L, -1376627125537124675L,
+ -7777920981101784778L, -5110715207949843068L, -1776707991509915931L,
+ -8027971522334779313L, -5423278384491086237L, -2167411962186469893L,
+ -8272161504007625539L, -5728515861582144020L, -2548958808550292121L,
+ -8510628282985014432L, -6026599335303880135L, -2921563150702462265L,
+ -8743505996830120772L, -6317696477610263061L, -3285434578585440922L,
+ -8970925639256982432L, -6601971030643840136L, -3640777769877412266L,
+ -9193015133814464522L, -6879582898840692749L, -3987792605123478032L,
+ -373054737976959636L, -7150688238876681629L, -4326674280168464132L,
+ -796656831783192261L, -7415439547505577019L, -4657613415954583370L,
+ -1210330751515841308L, -7673985747338482674L, -4980796165745715438L,
+ -1614309188754756393L, -7926472270612804602L, -5296404319838617848L,
+ -2008819381370884406L, -8173041140997884610L, -5604615407819967859L,
+ -2394083241347571919L, -8413831053483314306L, -5905602798426754978L,
+ -2770317479606055818L, -8648977452394866743L, -6199535797066195524L,
+ -3137733727905356501L, -8878612607581929669L, -6486579741050024183L,
+ -3496538657885142324L, -9102865688819295809L, -6766896092596731857L,
+ -3846934097318526917L, -196981603220770742L, -7040642529654063570L,
+ -4189117143640191558L, -624710411122851544L, -7307973034592864071L,
+ -4523280274813692185L, -1042414325089727327L, -7569037980822161435L,
+ -4849611457600313890L, -1450328303573004458L, -7823984217374209643L,
+ -5168294253290374149L, -1848681798185579782L, -8072955151507069220L,
+ -5479507920956448621L, -2237698882768172872L, -8316090829371189901L,
+ -5783427518286599473L, -2617598379430861437L, -8553528014785370254L,
+ -6080224000054324913L, -2988593981640518238L, -8785400266166405755L,
+ -6370064314280619289L, -3350894374423386208L, -9011838011655698236L,
+ -6653111496142234891L, -3704703351750405709L, -19193171260619233L,
+ -6929524759678968877L, -4050219931171323192L, -451088895536766085L,
+ -7199459587351560659L, -4387638465762062920L, -872862063775190746L,
+ -7463067817500576073L, -4717148753448332187L, -1284749923383027329L,
+ -7720497729755473937L, -5038936143766954517L, -1686984161281305242L,
+ -7971894128441897632L, -5353181642124984136L, -2079791034228842266L,
+ -8217398424034108273L, -5660062011615247437L, -2463391496091671392L,
+ -8457148712698376476L, -5959749872445582691L, -2838001322129590460L,
+ -8691279853972075893L, -6252413799037706963L, -3203831230369745799L,
+ -8919923546622172981L, -6538218414850328322L, -3561087000135522498L,
+ -9143208402725783417L, -6817324484979841368L, -3909969587797413806L,
+ -275775966319379353L, -7089889006590693952L, -4250675239810979535L,
+ -701658031336336515L, -7356065297226292178L, -4583395603105477319L,
+ -1117558485454458744L, -7616003081050118571L, -4908317832885260310L,
+ -1523711272679187483L, -7869848573065574033L, -5225624697904579637L,
+ -1920344853953336643L, -8117744561361917258L, -5535494683275008668L,
+ -2307682335666372931L, -8359830487432564938L, -5838102090863318269L,
+ -2685941595151759932L, -8596242524610931813L, -6133617137336276863L,
+ -3055335403242958174L, -8827113654667930715L, -6422206049907525490L,
+ -3416071543957018958L, -9052573742614218705L, -6704031159840385477L,
+ -3768352931373093942L, -98755145788979524L, -6979250993759194058L,
+ -4112377723771604669L, -528786136287117932L, -7248020362820530564L,
+ -4448339435098275301L, -948738275445456222L, -7510490449794491995L,
+ -4776427043815727089L, -1358847786342270957L, -7766808894105001205L,
+ -5096825099203863602L, -1759345355577441598L, -8017119874876982855L,
+ -5409713825168840664L, -2150456263033662926L, -8261564192037121185L,
+ -5715269221619013577L, -2532400508596379068L, -8500279345513818773L,
+ -6013663163464885563L, -2905392935903719049L, -8733399612580906262L,
+ -6305063497298744923L, -3269643353196043250L, -8961056123388608887L,
+ -6589634135808373205L, -3625356651333078602L, -9183376934724255983L,
+ -6867535149977932074L, -3972732919045027189L, -354230130378896082L,
+ -7138922859127891907L, -4311967555482476980L, -778273425925708321L,
+ -7403949918844649557L, -4643251380128424042L, -1192378206733142148L,
+ -7662765406849295699L, -4966770740134231719L, -1596777406740401745L,
+ -7915514906853832947L, -5282707615139903279L, -1991698500497491195L,
+ -8162340590452013853L, -5591239719637629412L, -2377363631119648861L,
+ -8403381297090862394L, -5892540602936190089L, -2753989735242849707L,
+ -8638772612167862923L, -6186779746782440750L, -3121788665050663033L,
+ -8868646943297746252L, -6474122660694794911L, -3480967307441105734L,
+ -9093133594791772940L, -6754730975062328271L, -3831727700400522434L,
+ -177973607073265139L, -7028762532061872568L, -4174267146649952806L,
+ -606147914885053103L, -7296371474444240046L, -4508778324627912153L,
+ -1024286887357502287L, -7557708332239520786L, -4835449396872013078L,
+ -1432625727662628443L, -7812920107430224633L, -5154464115860392887L,
+ -1831394126398103205L, -8062150356639896359L, -5466001927372482545L,
+ -2220816390788215277L, -8305539271883716405L, -5770238071427257602L,
+ -2601111570856684098L, -8543223759426509417L, -6067343680855748868L,
+ -2972493582642298180L, -8775337516792518219L, -6357485877563259869L,
+ -3335171328526686933L, -9002011107970261189L, -6640827866535438582L,
+ -3689348814741910324L, -9223372036854775808L, -6917529027641081856L,
+ -4035225266123964416L, -432345564227567616L, -7187745005283311616L,
+ -4372995238176751616L, -854558029293551616L, -7451627795949551616L,
+ -4702848726509551616L, -1266874889709551616L, -7709325833709551616L,
+ -5024971273709551616L, -1669528073709551616L, -7960984073709551616L,
+ -5339544073709551616L, -2062744073709551616L, -8206744073709551616L,
+ -5646744073709551616L, -2446744073709551616L, -8446744073709551616L,
+ -5946744073709551616L, -2821744073709551616L, -8681119073709551616L,
+ -6239712823709551616L, -3187955011209551616L, -8910000909647051616L,
+ -6525815118631426616L, -3545582879861895366L, -9133518327554766460L,
+ -6805211891016070171L, -3894828845342699810L, -256850038250986858L,
+ -7078060301547948643L, -4235889358507547899L, -683175679707046970L,
+ -7344513827457986212L, -4568956265895094861L, -1099509313941480672L,
+ -7604722348854507276L, -4894216917640746191L, -1506085128623544835L,
+ -7858832233030797378L, -5211854272861108819L, -1903131822648998119L,
+ -8106986416796705681L, -5522047002568494197L, -2290872734783229842L,
+ -8349324486880600507L, -5824969590173362730L, -2669525969289315508L,
+ -8585982758446904049L, -6120792429631242157L, -3039304518611664792L,
+ -8817094351773372351L, -6409681921289327535L, -3400416383184271515L,
+ -9042789267131251553L, -6691800565486676537L, -3753064688430957767L,
+ -79644842111309304L, -6967307053960650171L, -4097447799023424810L,
+ -510123730351893109L, -7236356359111015049L, -4433759430461380907L,
+ -930513269649338230L, -7499099821171918250L, -4762188758037509908L,
+ -1341049929119499481L, -7755685233340769032L, -5082920523248573386L,
+ -1741964635633328828L, -8006256924911912374L, -5396135137712502563L,
+ -2133482903713240300L, -8250955842461857044L, -5702008784649933400L,
+ -2515824962385028846L, -8489919629131724885L, -6000713517987268202L,
+ -2889205879056697349L, -8723282702051517699L, -6292417359137009220L,
+ -3253835680493873621L, -8951176327949752869L, -6577284391509803182L,
+ -3609919470959866074L, -9173728696990998152L, -6855474852811359786L,
+ -3957657547586811828L, -335385916056126881L, -7127145225176161157L,
+ -4297245513042813542L, -759870872876129024L, -7392448323188662496L,
+ -4628874385558440216L, -1174406963520662366L, -7651533379841495835L,
+ -4952730706374481889L, -1579227364540714458L, -7904546130479028392L,
+ -5268996644671397586L, -1974559787411859078L, -8151628894773493780L,
+ -5577850100039479321L, -2360626606621961247L, -8392920656779807636L,
+ -5879464802547371641L, -2737644984756826647L, -8628557143114098510L,
+ -6174010410465235234L, -3105826994654156138L, -8858670899299929442L,
+ -6461652605697523899L, -3465379738694516970L, -9083391364325154962L,
+ -6742553186979055799L, -3816505465296431844L, -158945813193151901L,
+ -7016870160886801794L, -4159401682681114339L, -587566084924005019L,
+ -7284757830718584993L, -4494261269970843337L, -1006140569036166268L,
+ -7546366883288685774L, -4821272585683469313L, -1414904713676948737L,
+ -7801844473689174817L, -5140619573684080617L, -1814088448677712867L,
+ -8051334308064652398L, -5452481866653427593L, -2203916314889396588L,
+ -8294976724446954723L, -5757034887131305500L, -2584607590486743971L,
+ -8532908771695296838L, -6054449946191733143L, -2956376414312278525L,
+ -8765264286586255934L, -6344894339805432014L, -3319431906329402113L,
+ -8992173969096958177L, -6628531442943809817L, -3673978285252374367L,
+ -9213765455923815836L, -6905520801477381891L, -4020214983419339459L,
+ -413582710846786420L, -7176018221920323369L, -4358336758973016307L,
+ -836234930288882479L, -7440175859071633406L, -4688533805412153853L,
+ -1248981238337804412L, -7698142301602209614L, -5010991858575374113L,
+ -1652053804791829737L, -7950062655635975442L, -5325892301117581398L,
+ -2045679357969588844L, -8196078626372074883L, -5633412264537705700L,
+ -2430079312244744221L, -8436328597794046994L, -5933724728815170839L,
+ -2805469892591575644L, -8670947710510816634L, -6226998619711132888L,
+ -3172062256211528206L, -8900067937773286985L, -6513398903789220827L,
+ -3530062611309138130L, -9123818159709293187L, -6793086681209228580L,
+ -3879672333084147821L, -237904397927796872L, -7066219276345954901L,
+ -4221088077005055722L, -664674077828931749L, -7332950326284164199L,
+ -4554501889427817345L, -1081441343357383777L, -7593429867239446717L,
+ -4880101315621920492L, -1488440626100012711L, -7847804418953589800L,
+ -5198069505264599346L, -1885900863153361279L, -8096217067111932656L,
+ -5508585315462527915L, -2274045625900771990L, -8338807543829064350L,
+ -5811823411358942533L, -2653093245771290262L, -8575712306248138270L,
+ -6107954364382784934L, -3023256937051093263L, -8807064613298015146L,
+ -6397144748195131028L, -3384744916816525881L, -9032994600651410532L,
+ -6679557232386875260L, -3737760522056206171L, -60514634142869810L,
+ -6955350673980375487L, -4082502324048081455L, -491441886632713915L,
+ -7224680206786528053L, -4419164240055772162L, -912269281642327298L,
+ -7487697328667536418L, -4747935642407032618L, -1323233534581402868L,
+ -7744549986754458649L, -5069001465015685407L, -1724565812842218855L,
+ -7995382660667468640L, -5382542307406947896L, -2116491865831296966L,
+ -8240336443785642460L, -5688734536304665171L, -2499232151953443560L,
+ -8479549122611984081L, -5987750384837592197L, -2873001962619602342L,
+ -8713155254278333320L, -6279758049420528746L, -3238011543348273028L,
+ -8941286242233752499L, -6564921784364802720L, -3594466212028615495L,
+ -9164070410158966541L, -6843401994271320272L, -3942566474411762436L,
+ -316522074587315140L, -7115355324258153819L, -4282508136895304370L,
+ -741449152691742558L, -7380934748073420955L, -4614482416664388289L,
+ -1156417002403097458L, -7640289654143017767L, -4938676049251384305L,
+ -1561659043136842477L, -7893565929601608404L, -5255271393574622601L,
+ -1957403223540890347L, -8140906042354138323L, -5564446534515285000L,
+ -2343872149716718346L, -8382449121214030822L, -5866375383090150624L,
+ -2721283210435300376L, -8618331034163144591L, -6161227774276542835L,
+ -3089848699418290639L, -8848684464777513506L, -6449169562544503978L,
+ -3449775934753242068L, -9073638986861858149L, -6730362715149934782L,
+ -3801267375510030573L, -139898200960150313L, -7004965403241175802L,
+ -4144520735624081848L, -568964901102714406L, -7273132090830278360L,
+ -4479729095110460046L, -987975350460687153L, -7535013621679011327L,
+ -4807081008671376254L, -1397165242411832414L, -7790757304148477115L,
+ -5126760611758208489L, -1796764746270372707L, -8040506994060064798L,
+ -5438947724147693094L, -2186998636757228463L, -8284403175614349646L,
+ -5743817951090549153L, -2568086420435798537L, -8522583040413455942L,
+ -6041542782089432023L, -2940242459184402125L, -8755180564631333184L,
+ -6332289687361778576L, -3303676090774835316L, -8982326584375353929L,
+ -6616222212041804507L, -3658591746624867729L, -9204148869281624187L,
+ -6893500068174642330L, -4005189066790915008L, -394800315061255856L,
+ -7164279224554366766L, -4343663012265570553L, -817892746904575288L,
+ -7428711994456441411L, -4674203974643163860L, -1231068949876566920L,
+ -7686947121313936181L, -4996997883215032323L, -1634561335591402499L,
+ -7939129862385708418L, -5312226309554747619L, -2028596868516046619L,
+ -8185402070463610993L, -5620066569652125837L
+ )
+
+ /** The default math context used for rounding of `BigDecimal` values when
+ * parsing.
+ */
+ final val bigDecimalMathContext: MathContext = MathContext.DECIMAL128
+
+ /** The default limit for number of decimal digits in mantissa of parsed
+ * `BigDecimal` values.
+ */
+ final val bigDecimalDigitsLimit: Int = 308
+
+ /** The default limit for scale of parsed `BigDecimal` values.
+ */
+ final val bigDecimalScaleLimit: Int = 6178
+
+ /** The maximum number of digits in `BigInt` values.
+ */
+ final val bigIntDigitsLimit: Int = 308
+
+ /* Use the following code to generate `nibbles` in Scala REPL:
+ val ns = new Array[Byte](256)
+ java.util.Arrays.fill(ns, -1: Byte)
+ ns('0') = 0
+ ns('1') = 1
+ ns('2') = 2
+ ns('3') = 3
+ ns('4') = 4
+ ns('5') = 5
+ ns('6') = 6
+ ns('7') = 7
+ ns('8') = 8
+ ns('9') = 9
+ ns('A') = 10
+ ns('B') = 11
+ ns('C') = 12
+ ns('D') = 13
+ ns('E') = 14
+ ns('F') = 15
+ ns('a') = 10
+ ns('b') = 11
+ ns('c') = 12
+ ns('d') = 13
+ ns('e') = 14
+ ns('f') = 15
+ ns.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)")
+ */
+ private final val nibbles: Array[Byte] = Array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
+ -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+ )
diff --git a/modules/core/src/main/scala/tethys/readers/tokens/JsonReader.scala b/modules/core/src/main/scala/tethys/readers/tokens/JsonReader.scala
new file mode 100644
index 00000000..21645e96
--- /dev/null
+++ b/modules/core/src/main/scala/tethys/readers/tokens/JsonReader.scala
@@ -0,0 +1,3247 @@
+package tethys.readers.tokens
+/*
+import tethys.commons.Token
+
+import java.io.InputStream
+import java.math.MathContext
+import java.nio.ByteBuffer
+import java.time.*
+import java.util.concurrent.ConcurrentHashMap
+import java.nio.charset.StandardCharsets.UTF_8
+import scala.annotation.{switch, tailrec}
+
+/** The reader to parse JSON input iteratively.
+ *
+ * @param buf
+ * the internal buffer with JSON input
+ * @param head
+ * the head position in the internal buffer
+ * @param tail
+ * the tail position in the internal buffer
+ * @param mark
+ * the current mark position
+ * @param charBuf
+ * the internal char buffer for parsed strings
+ * @param bbuf
+ * the byte buffer with JSON input
+ * @param in
+ * the input stream with JSON input
+ * @param totalRead
+ * the total number of read bytes
+ * @param config
+ * the JSON reader configuration
+ */
+final class JsonReader(
+ private[this] var buf: Array[Byte] = new Array[Byte](32768),
+ private[this] var head: Int = 0,
+ private[this] var tail: Int = 0,
+ private[this] var mark: Int = -1,
+ private[this] var charBuf: Array[Char] = new Array[Char](4096),
+ private[this] var bbuf: ByteBuffer = null,
+ private[this] var in: InputStream = null,
+ private[this] var totalRead: Long = 0,
+ private[this] var config: ReaderConfig = null
+) {
+ import JsonReader._
+ private[this] var magnitude: Array[Byte] = _
+ private[this] val objectTrace = new scala.collection.mutable.Stack[Boolean](6)
+
+ /** Throws a [[JsonReaderException]] indicating that a required field with the
+ * given name is missing.
+ *
+ * @param reqField
+ * the name of the missing required field
+ * @throws JsonReaderException
+ * always
+ */
+ def requiredFieldError(reqField: String): Nothing = {
+ var i = appendString("missing required field \"", 0)
+ i = appendString(reqField, i)
+ i = appendChar('"', i)
+ decodeError(i, head - 1, null)
+ }
+
+ /** Throws a [[JsonReaderException]] indicating that a field with the given
+ * name is duplicated.
+ *
+ * @param len
+ * the length of the duplicated field name in the internal char buffer
+ * @throws JsonReaderException
+ * always
+ */
+ def duplicatedKeyError(len: Int): Nothing = {
+ var i = prependString("duplicated field \"", len)
+ i = appendChar('"', i)
+ decodeError(i, head - 1, null)
+ }
+
+ /** Throws a [[JsonReaderException]] indicating that an unexpected field with
+ * the given name was encountered.
+ *
+ * @param len
+ * the length of the unexpected field name in the internal char buffer
+ * @throws JsonReaderException
+ * always
+ */
+ def unexpectedKeyError(len: Int): Nothing = {
+ var i = prependString("unexpected field \"", len)
+ i = appendChar('"', i)
+ decodeError(i, head - 1, null)
+ }
+
+ /** Throws a [[JsonReaderException]] indicating that an illegal discriminator
+ * field name was encountered.
+ */
+ def discriminatorError(): Nothing = decodeError("illegal discriminator")
+
+ /** Throws a [[JsonReaderException]] indicating that an illegal value was
+ * encountered for the given discriminator field.
+ *
+ * @param discriminatorFieldName
+ * the name of the discriminator field
+ * @throws JsonReaderException
+ * always
+ */
+ def discriminatorValueError(discriminatorFieldName: String): Nothing = {
+ var i = appendString("illegal value of discriminator field \"", 0)
+ i = appendString(discriminatorFieldName, i)
+ i = appendChar('"', i)
+ decodeError(i, head - 1, null)
+ }
+
+ /** Throws a [[JsonReaderException]] indicating that an illegal enum value was
+ * encountered.
+ *
+ * @param value
+ * an illegal enum value
+ * @throws JsonReaderException
+ * always
+ */
+ def enumValueError(value: String): Nothing = {
+ var i = appendString("illegal enum value \"", 0)
+ i = appendString(value, i)
+ i = appendChar('"', i)
+ decodeError(i, head - 1, null)
+ }
+
+ /** Throws a [[JsonReaderException]] indicating that an illegal enum value
+ * with the given length was encountered.
+ *
+ * @param len
+ * the length of the illegal enum value in the internal char buffer.
+ * @throws JsonReaderException
+ * always
+ */
+ def enumValueError(len: Int): Nothing = {
+ var i = prependString("illegal enum value \"", len)
+ i = appendChar('"', i)
+ decodeError(i, head - 1, null)
+ }
+
+ /** Sets the current read head position as a mark.
+ */
+ def setMark(): Unit = mark = head
+
+ /** Skips tokens with in the current JSON object until a key with the given
+ * name is encountered.
+ *
+ * @param key
+ * the name of the JSON key to skip to
+ * @return
+ * `true` if the key was found, `false` otherwise
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or invalid encoding of JSON key
+ */
+ def skipToKey(key: String): Boolean = {
+ while (!isCharBufEqualsTo(readKeyAsCharBuf(), key)) {
+ skip(current = Token.Empty)
+ if (!isNextToken(',', head)) return false
+ }
+ true
+ }
+
+ /** Rolls back the read head position to the previously set mark.
+ *
+ * @throws java.lang.IllegalStateException
+ * in cases of reaching the end of input or invalid encoding of JSON key
+ */
+ def rollbackToMark(): Unit = {
+ if (mark < 0) missingSetMarkOperation()
+ head = mark
+ mark = -1
+ }
+
+ /** Reads a JSON key into the internal char buffer and returns the length of
+ * the key.
+ *
+ * @return
+ * the length of the key in the internal char buffer
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or invalid encoding of JSON key
+ */
+ def readKeyAsCharBuf(): Int = {
+ nextTokenOrError('"', head)
+ val pos = head
+ val len = parseString(0, Math.min(tail - pos, charBuf.length), charBuf, pos)
+ nextTokenOrError(':', head)
+ len
+ }
+
+ /** Reads a JSON key into the internal char buffer and returns a `String`
+ * instance.
+ *
+ * @return
+ * a `String` instance of the parsed JSON key
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or invalid encoding of JSON key
+ */
+ def readKeyAsString(): String = {
+ nextTokenOrError('"', head)
+ val pos = head
+ val len = parseString(0, Math.min(tail - pos, charBuf.length), charBuf, pos)
+ nextTokenOrError(':', head)
+ new String(charBuf, 0, len)
+ }
+
+ /** Reads a JSON number value into a `Byte` value.
+ *
+ * @return
+ * a `Byte` value of the parsed JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or dection of leading zero or
+ * illegal format of JSON value or exceeding capacity of `Byte`
+ */
+ def readByte(): Byte = readByte(isToken = true)
+
+ /** Reads a JSON string value into a `Char` value.
+ *
+ * @return
+ * a `Char` value of the parsed JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or invalid encoding of JSON value
+ * or exceeding capacity of `Char` or when parsed char is a part of a
+ * surrogate pair
+ */
+ def readChar(): Char = {
+ nextTokenOrError('"', head)
+ val x = parseChar(head)
+ nextByteOrError('"', head)
+ x
+ }
+
+ /** Reads a JSON number value into a `Short` value.
+ *
+ * @return
+ * a `Short` value of the parsed JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value or exceeding capacity of `Short`
+ */
+ def readShort(): Short = readShort(isToken = true)
+
+ /** Reads a JSON value into a `Int` value.
+ *
+ * @return
+ * a `Int` value of the parsed JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value or exceeding capacity of `Int`
+ */
+ def readInt(): Int = readInt(isToken = true)
+
+ /** Reads a JSON number value into a `Long` value.
+ *
+ * @return
+ * a `Long` value of the parsed JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value or exceeding capacity of `Long`
+ */
+ def readLong(): Long = readLong(isToken = true)
+
+ /** Reads a JSON number value into a `Double` value.
+ *
+ * @return
+ * a `Double` value of the parsed JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value
+ */
+ def readDouble(): Double = readDouble(isToken = true)
+
+ /** Reads a JSON number value into a `Float` value.
+ *
+ * @return
+ * a `Float` value of the parsed JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value
+ */
+ def readFloat(): Float = readFloat(isToken = true)
+
+ /** Reads a JSON number value into a `BigInt` instance with the default limit
+ * of allowed digits. In case of `null` JSON value returns the provided
+ * default value or throws a [[JsonReaderException]] if the provided default
+ * value is `null`.
+ *
+ * @param default
+ * the default `BigInt` value to return if the JSON value is `null`
+ * @return
+ * a `BigInt` instance of the parsed JSON value or the provided default
+ * value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value or exceeding of the default limit or when
+ * both the JSON value and the provided default value are `null`
+ */
+ def readBigInt(default: BigInt): BigInt =
+ readBigInt(isToken = true, default, bigIntDigitsLimit)
+
+ /** Reads a JSON number value into a `BigInt` instance with the provided limit
+ * of allowed digits. In case of `null` JSON value returns the provided
+ * default value or throws a [[JsonReaderException]] if the provided default
+ * value is `null`.
+ *
+ * @param default
+ * the default `BigInt` value to return if the JSON value is `null`
+ * @param digitsLimit
+ * the maximum number of decimal digits allowed in the parsed `BigInt`
+ * value
+ * @return
+ * a `BigInt` instance of the parsed JSON value or the provided default
+ * value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value or exceeding of the default limit or when
+ * both the JSON value and the provided default value are `null`
+ */
+ def readBigInt(default: BigInt, digitsLimit: Int): BigInt =
+ readBigInt(isToken = true, default, digitsLimit)
+
+ /** Reads a JSON number value into a `BigDecimal` instance with the default
+ * limit of allowed digits for mantissa, the default limit for scale, and the
+ * default instance of [[java.math.MathContext]] for precision. In case of
+ * `null` JSON value returns the provided default value or throws a
+ * [[JsonReaderException]] if the provided default value is `null`.
+ *
+ * @param default
+ * the default `BigDecimal` value to return if the JSON value is `null`
+ * @return
+ * a `BigDecimal` instance of the parsed JSON value or the provided default
+ * value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value or exceeding of default limits or when both
+ * the JSON value and the provided default value are `null`
+ */
+ def readBigDecimal(default: BigDecimal): BigDecimal =
+ readBigDecimal(
+ isToken = true,
+ default,
+ bigDecimalMathContext,
+ bigDecimalScaleLimit,
+ bigDecimalDigitsLimit
+ )
+
+ /** Reads a JSON number value into a `BigDecimal` instance with the provided
+ * limit of allowed digits for mantissa, the provided limit for scale, and
+ * the provided instance of [[java.math.MathContext]] for precision. In case
+ * of `null` JSON value returns the provided default value or throws a
+ * [[JsonReaderException]] if the provided default value is `null`.
+ *
+ * @param default
+ * the default `BigDecimal` value to return if the JSON value is `null`
+ * @param mc
+ * the precision to use
+ * @param scaleLimit
+ * the maximum number of decimal places (scale) allowed
+ * @param digitsLimit
+ * the maximum number of decimal digits allowed
+ * @return
+ * a `BigDecimal` instance of the parsed JSON value or the provided default
+ * value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or detection of leading zero or
+ * illegal format of JSON value or exceeding of provided limits or when
+ * both the JSON value and the provided default value are `null`
+ */
+ def readBigDecimal(
+ default: BigDecimal,
+ mc: MathContext,
+ scaleLimit: Int,
+ digitsLimit: Int
+ ): BigDecimal =
+ readBigDecimal(isToken = true, default, mc, scaleLimit, digitsLimit)
+
+ /** Reads a JSON string value into a `String` instance. In case of `null` JSON
+ * value returns the provided default value or throws a
+ * [[JsonReaderException]] if the provided default value is `null`.
+ *
+ * @param default
+ * the default `String` value to return if the JSON value is `null`.
+ * @return
+ * a `String` instance of the parsed JSON value or the default value if the
+ * JSON value is `null`.
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or invalid encoding of JSON value
+ * or when both the JSON value and the provided default value are `null`
+ */
+ def readString(default: String): String = {
+ val pos = head + 1
+ val len =
+ parseString(0, Math.min(tail - pos, charBuf.length), charBuf, pos)
+ new String(charBuf, 0, len)
+ }
+
+ /** Reads a JSON boolean value into a `Boolean` value.
+ *
+ * @return
+ * a `Boolean` value of the parsed JSON value.
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or unexpected JSON value
+ */
+ def readBoolean(): Boolean = parseBoolean(isToken = true, head)
+
+ /** Reads a raw JSON value into a `Array[Byte]` instance without parsing.
+ *
+ * @return
+ * a `Array[Byte]` instance containing the raw bytes of the JSON value.
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or invalid type of JSON value
+ */
+ def readRawValAsBytes(): Array[Byte] = {
+ var from = head
+ val oldMark = mark
+ val newMark =
+ if (oldMark < 0) from
+ else oldMark
+ mark = newMark
+ skip(Token.Empty)
+ if (mark == 0) from -= newMark
+ if (mark > oldMark) mark = oldMark
+ val len = head - from
+ val x = new Array[Byte](len)
+ System.arraycopy(buf, from, x, 0, len)
+ x
+ }
+
+ /** Finishes reading the `null` JSON value and returns the provided default
+ * value or throws [[JsonReaderException]]. Before calling it the `n` token
+ * should be parsed already.
+ *
+ * @param default
+ * the default value to return
+ * @param msg
+ * the exception message
+ * @tparam A
+ * the type of the default value
+ * @return
+ * the default value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or illegal format of JSON value or
+ * when the provided default value is `null`
+ */
+ @tailrec
+ def readNullOrError[@specialized A](default: A, msg: String): A =
+ if (default != null) {
+ val pos = head
+ if (pos != 0) {
+ if (pos + 2 < tail) {
+ val bs = ByteArrayAccess.getInt(buf, pos - 1)
+ if (bs == 0x6c6c756e) {
+ head = pos + 3
+ default
+ } else decodeError(msg, bs, pos)
+ } else if (buf(pos - 1) == 'n') {
+ head = loadMoreOrError(pos - 1) + 1
+ readNullOrError(default, msg)
+ } else decodeError(msg, pos - 1)
+ } else illegalTokenOperation()
+ } else decodeError(msg)
+
+ /** Finishes reading the `null` JSON value and returns the provided default
+ * value or throws [[JsonReaderException]] with a message of expecting `null`
+ * or the provided token. Before calling it the `n` token should be parsed
+ * already.
+ *
+ * @param default
+ * the default value to return
+ * @param t
+ * the token for an error message
+ * @tparam A
+ * the type of the default value
+ * @return
+ * the default value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input or illegal format of JSON value or
+ * when the provided default value is `null`
+ */
+ @tailrec
+ def readNullOrTokenError[@specialized A](default: A, t: Byte): A =
+ if (default != null) {
+ val pos = head
+ if (pos != 0) {
+ if (pos + 2 < tail) {
+ val bs = ByteArrayAccess.getInt(buf, pos - 1)
+ if (bs == 0x6c6c756e) {
+ head = pos + 3
+ default
+ } else tokenOrNullError(t, bs, pos)
+ } else if (buf(pos - 1) == 'n') {
+ head = loadMoreOrError(pos - 1) + 1
+ readNullOrTokenError(default, t)
+ } else tokenOrNullError(t)
+ } else illegalTokenOperation()
+ } else tokenError(t)
+
+ /** Reads and returns the next byte from the input.
+ *
+ * @return
+ * the next byte from the input
+ * @throws JsonReaderException
+ * in cases of reaching the end of input
+ */
+ def nextByte(): Byte = nextByte(head)
+
+ /** Skips whitespaces, then reads and returns the next byte from the input.
+ *
+ * @return
+ * the next token from the input
+ * @throws JsonReaderException
+ * in cases of reaching the end of input
+ */
+ def nextToken(): Byte = nextToken(head)
+
+ /** Skips whitespaces, then checks if the next token in the input matches the
+ * given one.
+ *
+ * @param t
+ * the token to match
+ * @return
+ * `true` if the next token matches `t`, `false` otherwise
+ * @throws JsonReaderException
+ * in cases of reaching the end of input
+ */
+ def isNextToken(t: Byte): Boolean = isNextToken(t, head)
+
+ /** Checks if the current token in the input matches the given one.
+ *
+ * @param t
+ * the token to match
+ * @return
+ * `true` if the current token matches `t`, `false` otherwise.
+ * @throws java.lang.IllegalStateException
+ * if no any token was parsed yet
+ */
+ def isCurrentToken(t: Byte): Boolean = isCurrentToken(t, head)
+
+ /** Checks if there are more bytes available for reading in the input.
+ *
+ * @return
+ * `true` if there are more bytes available, `false` otherwise
+ */
+ def hasRemaining(): Boolean = head < tail || loadMore(head) < tail
+
+ /** Rolls back the current reading position by one.
+ *
+ * @throws java.lang.IllegalStateException
+ * if no any token was parsed yet
+ */
+ def rollbackToken(): Unit = {
+ val pos = head
+ if (pos == 0) illegalTokenOperation()
+ head = pos - 1
+ }
+
+ /** Calculates a hash code for the internal char buffer of the given length.
+ *
+ * @param len
+ * the length of the char buffer to hash
+ * @return
+ * the hash code for the char buffer
+ */
+ def charBufToHashCode(len: Int): Int = {
+ val cs = charBuf
+ var h, i = 0
+ while (i < len) {
+ h = (h << 5) + (cs(i) - h)
+ i += 1
+ }
+ h
+ }
+
+ /** Checks if the internal char buffer contains the given string.
+ *
+ * @param len
+ * the length of the char buffer to check
+ * @param s
+ * the string to match
+ * @return
+ * `true` if the char buffer contains `s`, `false` otherwise
+ */
+ def isCharBufEqualsTo(len: Int, s: String): Boolean = {
+ if (s.length != len) return false
+ val charBuf = this.charBuf
+ var i = 0
+ while (i < len) {
+ if (s.charAt(i) != charBuf(i)) return false
+ i += 1
+ }
+ true
+ }
+
+ /** Skips the next JSON value.
+ *
+ * @throws JsonReaderException
+ * in cases of reaching the end of input
+ */
+ def skip(current: Token): Unit = {
+ val b: Byte =
+ if (current.isObjectStart)
+ objectTrace.pop()
+ '{'
+ else if (current.isArrayStart)
+ objectTrace.pop()
+ '['
+ else nextToken(head)
+
+ var pos = head
+ if (b == '"') pos = skipString(evenBackSlashes = true, pos)
+ else if ((b >= '0' && b <= '9') || b == '-') pos = skipNumber(pos)
+ else if (b == 'n' || b == 't') pos = skipFixedBytes(3, pos)
+ else if (b == 'f') pos = skipFixedBytes(4, pos)
+ else if (b == '[') pos = skipArray(0, pos)
+ else if (b == '{') pos = skipObject(0, pos)
+ else decodeError("expected value")
+ head = pos
+ }
+
+ def init(s: String): this.type = {
+ objectTrace.clear()
+ this.buf = s.getBytes(UTF_8)
+ this.config = config
+ head = 0
+ val to = buf.length
+ tail = to
+ totalRead = 0
+ mark = -1
+ this
+ }
+
+ def nextTethysToken(previous: Token): Token = {
+ if (!skipWhitespaces()) return Token.Empty
+ val b = nextToken(head)
+ val token =
+ if (
+ b == '"' &&
+ objectTrace.nonEmpty
+ && objectTrace.head &&
+ previous != null &&
+ !previous.isFieldName
+ )
+ rollbackToken()
+ Token.FieldNameToken
+ else if (b == '"')
+ rollbackToken()
+ Token.StringValueToken
+ else if ((b >= '0' && b <= '9') || b == '-')
+ rollbackToken()
+ Token.NumberValueToken
+ else if (b == 'n') readNullOrError(Token.NullValueToken, "expected null")
+ else if (b == 'f' || b == 't')
+ rollbackToken()
+ Token.BooleanValueToken
+ else if (b == '[')
+ objectTrace.push(false)
+ Token.ArrayStartToken
+ else if (b == ']')
+ objectTrace.pop()
+ Token.ArrayEndToken
+ else if (b == '{')
+ objectTrace.push(true)
+ Token.ObjectStartToken
+ else if (b == '}')
+ objectTrace.pop()
+ Token.ObjectEndToken
+ else decodeError("expected value")
+ token
+ }
+
+ /** Throws a [[JsonReaderException]] with the message `expected ','`.
+ *
+ * @throws JsonReaderException
+ * always
+ */
+ def commaError(): Nothing = decodeError("expected ','")
+
+ /** Throws a [[JsonReaderException]] with the message `expected '[' or null`.
+ *
+ * @throws JsonReaderException
+ * always
+ */
+ def arrayStartOrNullError(): Nothing = decodeError("expected '[' or null")
+
+ /** Throws a [[JsonReaderException]] with the message `expected ']'`.
+ *
+ * @throws JsonReaderException
+ * always
+ */
+ def arrayEndError(): Nothing = decodeError("expected ']'")
+
+ /** Throws a [[JsonReaderException]] with the message `expected ']' or ','`.
+ *
+ * @throws JsonReaderException
+ * always
+ */
+ def arrayEndOrCommaError(): Nothing = decodeError("expected ']' or ','")
+
+ /** Throws a [[JsonReaderException]] with the message `expected '{' or null`.
+ *
+ * @throws JsonReaderException
+ * always
+ */
+ def objectStartOrNullError(): Nothing = decodeError("expected '{' or null")
+
+ /** Throws a [[JsonReaderException]] with the message `expected '}' or ','`.
+ *
+ * @throws JsonReaderException
+ * always
+ */
+ def objectEndOrCommaError(): Nothing = decodeError("expected '}' or ','")
+
+ /** Throws a [[JsonReaderException]] with the given message.
+ *
+ * @param msg
+ * the exception message
+ * @throws JsonReaderException
+ * always
+ */
+ def decodeError(msg: String): Nothing = decodeError(msg, head - 1)
+
+ /* /** Reads a JSON value from the given byte array slice into an instance of
+ * type `A` using the given [[JsonValueCodec]].
+ *
+ * @param codec
+ * the JSON value codec
+ * @param buf
+ * the byte array with JSON input
+ * @param from
+ * the start index of the slice (inclusive)
+ * @param to
+ * the end index of the slice (exclusive)
+ * @param config
+ * the reader configuration
+ * @tparam A
+ * the type of the value to read
+ * @return
+ * an instance of type `A` containing the decoded JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input during parsing of JSON value or
+ * unexpected format of JSON value or when configured checking of reaching
+ * the end of input doesn't pass after reading of the whole JSON value
+ */
+ private[tethys] def read[@sp A](
+ codec: JsonValueCodec[A],
+ buf: Array[Byte],
+ from: Int,
+ to: Int,
+ config: ReaderConfig
+ ): A = {
+ val currBuf = this.buf
+ try {
+ this.buf = buf
+ this.config = config
+ head = from
+ tail = to
+ totalRead = 0
+ mark = -1
+ val x = codec.decodeValue(this, codec.nullValue)
+ if (head != to && config.checkForEndOfInput) endOfInputOrError()
+ x
+ } finally {
+ this.buf = currBuf
+ if (charBuf.length > config.preferredCharBufSize)
+ reallocateCharBufToPreferredSize()
+ }
+ }
+
+ /** Reads a JSON value from the given input stream into an instance of type
+ * `A` using the given [[JsonValueCodec]].
+ *
+ * @param codec
+ * the JSON value codec
+ * @param in
+ * the input stream with the JSON input
+ * @param config
+ * the reader configuration
+ * @tparam A
+ * the type of the value to read
+ * @return
+ * an instance of type `A` containing the decoded JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input during parsing of JSON value or
+ * unexpected format of JSON value or when configured checking of reaching
+ * the end of input doesn't pass after reading of the whole JSON value
+ */
+ private[tethys] def read[@sp A](
+ codec: JsonValueCodec[A],
+ in: InputStream,
+ config: ReaderConfig
+ ): A =
+ try {
+ this.config = config
+ this.in = in
+ head = 0
+ tail = 0
+ totalRead = 0
+ mark = -1
+ if (buf.length < config.preferredBufSize) reallocateBufToPreferredSize()
+ val x = codec.decodeValue(this, codec.nullValue)
+ if (config.checkForEndOfInput) endOfInputOrError()
+ x
+ } finally {
+ this.in = null
+ if (buf.length > config.preferredBufSize) reallocateBufToPreferredSize()
+ if (charBuf.length > config.preferredCharBufSize)
+ reallocateCharBufToPreferredSize()
+ }
+
+ /** Reads a JSON value from the given byte buffer into an instance of type `A`
+ * using the given [[JsonValueCodec]].
+ *
+ * @param codec
+ * the JSON value codec
+ * @param bbuf
+ * the byte buffer with the JSON input
+ * @param config
+ * the reader configuration
+ * @tparam A
+ * the type of the value to read
+ * @return
+ * an instance of type `A` containing the decoded JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input during parsing of JSON value or
+ * unexpected format of JSON value or when configured checking of reaching
+ * the end of input doesn't pass after reading of the whole JSON value
+ */
+ private[tethys] def read[@sp A](
+ codec: JsonValueCodec[A],
+ bbuf: ByteBuffer,
+ config: ReaderConfig
+ ): A =
+ if (bbuf.hasArray) {
+ val offset = bbuf.arrayOffset
+ val to = offset + bbuf.limit()
+ val currBuf = this.buf
+ try {
+ this.buf = bbuf.array
+ this.config = config
+ head = offset + bbuf.position()
+ tail = to
+ totalRead = 0
+ mark = -1
+ val x = codec.decodeValue(this, codec.nullValue)
+ if (head != to && config.checkForEndOfInput) endOfInputOrError()
+ x
+ } finally {
+ this.buf = currBuf
+ if (charBuf.length > config.preferredCharBufSize)
+ reallocateCharBufToPreferredSize()
+ bbuf.position(head - offset)
+ }
+ } else {
+ val position = bbuf.position()
+ try {
+ this.config = config
+ this.bbuf = bbuf
+ head = 0
+ tail = 0
+ totalRead = 0
+ mark = -1
+ if (buf.length < config.preferredBufSize) reallocateBufToPreferredSize()
+ val x = codec.decodeValue(this, codec.nullValue)
+ if (config.checkForEndOfInput) endOfInputOrError()
+ x
+ } finally {
+ this.bbuf = null
+ if (buf.length > config.preferredBufSize) reallocateBufToPreferredSize()
+ if (charBuf.length > config.preferredCharBufSize)
+ reallocateCharBufToPreferredSize()
+ bbuf.position(totalRead.toInt - tail + head + position)
+ }
+ }
+
+ /** Reads a JSON value from the given string into an instance of type `A`
+ * using the given [[JsonValueCodec]].
+ *
+ * @param codec
+ * the JSON value codec
+ * @param s
+ * the string with the JSON input
+ * @param config
+ * The reader configuration.
+ * @tparam A
+ * the type of the value to read
+ * @return
+ * an instance of type `A` containing the decoded JSON value.
+ * @throws JsonReaderException
+ * in cases of reaching the end of input during parsing of JSON value or
+ * unexpected format of JSON value or when configured checking of reaching
+ * the end of input doesn't pass after reading of the whole JSON value
+ */
+ private[tethys] def read[@sp A](
+ codec: JsonValueCodec[A],
+ s: String,
+ config: ReaderConfig
+ ): A = {
+ val currBuf = this.buf
+ try {
+ this.buf = s.getBytes(UTF_8)
+ this.config = config
+ head = 0
+ val to = buf.length
+ tail = to
+ totalRead = 0
+ mark = -1
+ val x = codec.decodeValue(this, codec.nullValue)
+ if (head != to && config.checkForEndOfInput) endOfInputOrError()
+ x
+ } finally {
+ this.buf = currBuf
+ if (charBuf.length > config.preferredCharBufSize)
+ reallocateCharBufToPreferredSize()
+ }
+ }
+
+ /** Scans JSON values separated by whitespaces from the given input stream and
+ * applies the given function to each decoded value until it returns `false`.
+ *
+ * @param codec
+ * the JSON value codec
+ * @param in
+ * the input stream with the JSON input
+ * @param config
+ * the reader configuration
+ * @param f
+ * the callback function to apply to each decoded JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input during parsing of JSON value or
+ * unexpected format of JSON value
+ */
+ private[tethys] def scanValueStream[@sp A](
+ codec: JsonValueCodec[A],
+ in: InputStream,
+ config: ReaderConfig
+ )(f: A => Boolean): Unit =
+ try {
+ this.config = config
+ this.in = in
+ head = 0
+ tail = 0
+ totalRead = 0
+ mark = -1
+ if (buf.length < config.preferredBufSize) reallocateBufToPreferredSize()
+ while (f(codec.decodeValue(this, codec.nullValue)) && skipWhitespaces())
+ ()
+ } finally {
+ this.in = null
+ if (buf.length > config.preferredBufSize) reallocateBufToPreferredSize()
+ if (charBuf.length > config.preferredCharBufSize)
+ reallocateCharBufToPreferredSize()
+ }
+
+ /** Scans JSON array from the given input stream and applies the given
+ * function to each decoded value until it returns `false`.
+ *
+ * @param codec
+ * the JSON value codec
+ * @param in
+ * the input stream with the JSON input
+ * @param config
+ * the reader configuration
+ * @param f
+ * the function to apply to each decoded JSON value
+ * @throws JsonReaderException
+ * in cases of reaching the end of input during parsing of JSON value or
+ * unexpected format of JSON value or when configured checking of reaching
+ * the end of input doesn't pass after reading of the whole JSON array
+ */
+ private[tethys] def scanArray[@sp A](
+ codec: JsonValueCodec[A],
+ in: InputStream,
+ config: ReaderConfig
+ )(f: A => Boolean): Unit =
+ try {
+ this.config = config
+ this.in = in
+ head = 0
+ tail = 0
+ totalRead = 0
+ mark = -1
+ if (buf.length < config.preferredBufSize) reallocateBufToPreferredSize()
+ var continue = true
+ if (isNextToken('[', head)) {
+ if (!isNextToken(']', head)) {
+ head -= 1
+ while ({
+ continue = f(codec.decodeValue(this, codec.nullValue))
+ continue && isNextToken(',', head)
+ }) ()
+ if (continue && !isCurrentToken(']', head)) arrayEndOrCommaError()
+ }
+ } else readNullOrTokenError((), '[')
+ if (continue && config.checkForEndOfInput) endOfInputOrError()
+ } finally {
+ this.in = null
+ if (buf.length > config.preferredBufSize) reallocateBufToPreferredSize()
+ if (charBuf.length > config.preferredCharBufSize)
+ reallocateCharBufToPreferredSize()
+ }
+ */
+ /** Skips whitespace characters and checks if there are non-whitespace
+ * characters left in the input.
+ *
+ * @throws JsonReaderException
+ * when there is at least one non-whitespace character left in the input
+ */
+ private[tethys] def endOfInputOrError(): Unit =
+ if (skipWhitespaces()) decodeError("expected end of input", head)
+
+ /** Skips whitespace characters in the input.
+ *
+ * @return
+ * `true` if and only if there are non-whitespace characters left in the
+ * input after skipping the whitespace
+ */
+ private[tethys] def skipWhitespaces(): Boolean = {
+ var pos = head
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ val b = buf(pos)
+ b == ' ' || b == '\n' || (b | 0x4) == '\r'
+ }
+ ) pos += 1
+ head = pos
+ pos != tail
+ }
+
+ private[this] def tokenOrDigitError(t: Byte, pos: Int = head - 1): Nothing = {
+ var i = appendString("expected '", 0)
+ i = appendChar(t.toChar, i)
+ i = appendString("' or digit", i)
+ decodeError(i, pos, null)
+ }
+
+ private[this] def tokensError(
+ t1: Byte,
+ t2: Byte,
+ pos: Int = head - 1
+ ): Nothing = {
+ var i = appendString("expected '", 0)
+ i = appendChar(t1.toChar, i)
+ i = appendString("' or '", i)
+ i = appendChar(t2.toChar, i)
+ i = appendChar('\'', i)
+ decodeError(i, pos, null)
+ }
+
+ private[this] def tokenOrNullError(t: Byte, pos: Int = head - 1): Nothing = {
+ var i = appendString("expected '", 0)
+ i = appendChar(t.toChar, i)
+ i = appendString("' or null", i)
+ decodeError(i, pos, null)
+ }
+
+ private[this] def tokenError(t: Byte, pos: Int = head - 1): Nothing = {
+ var i = appendString("expected '", 0)
+ i = appendChar(t.toChar, i)
+ i = appendChar('\'', i)
+ decodeError(i, pos, null)
+ }
+
+ private[this] def decodeError(
+ msg: String,
+ pos: Int,
+ cause: Throwable = null
+ ): Nothing =
+ decodeError(appendString(msg, 0), pos, cause)
+
+ private[this] def decodeError(
+ from: Int,
+ pos: Int,
+ cause: Throwable
+ ): Nothing = {
+ var i = appendString(", offset: 0x", from)
+ val offset =
+ if ((bbuf eq null) && (in eq null)) 0
+ else totalRead - tail
+ i = appendHexOffset(offset + pos, i)
+ if (config.appendHexDumpToParseException) {
+ i = appendString(", buf:", i)
+ i = appendHexDump(pos, offset.toInt, i)
+ }
+ throw new JsonReaderException(
+ new String(charBuf, 0, i),
+ cause,
+ config.throwReaderExceptionWithStackTrace
+ )
+ }
+
+ @tailrec
+ private[this] def nextByte(pos: Int): Byte =
+ if (pos < tail) {
+ head = pos + 1
+ buf(pos)
+ } else nextByte(loadMoreOrError(pos))
+
+ @tailrec
+ private[this] def nextByteOrError(t: Byte, pos: Int): Unit =
+ if (pos < tail) {
+ if (buf(pos) != t) tokenError(t, pos)
+ head = pos + 1
+ } else nextByteOrError(t, loadMoreOrError(pos))
+
+ @tailrec
+ private[this] def nextToken(pos: Int): Byte =
+ if (pos < tail) {
+ val b = buf(pos)
+ if (b == ' ' || b == ',' || b == '\n' || (b | 0x4) == '\r')
+ nextToken(pos + 1)
+ else {
+ head = pos + 1
+ b
+ }
+ } else nextToken(loadMoreOrError(pos))
+
+ @tailrec
+ private[this] def nextTokenOrError(t: Byte, pos: Int): Unit =
+ if (pos < tail) {
+ val b = buf(pos)
+ head = pos + 1
+ if (
+ b != t && ((b != ' ' && b != '\n' && (b | 0x4) != '\r') || nextToken(
+ pos + 1
+ ) != t)
+ ) tokenError(t, head - 1)
+ } else nextTokenOrError(t, loadMoreOrError(pos))
+
+ @tailrec
+ private[this] def isNextToken(t: Byte, pos: Int): Boolean =
+ if (pos < tail) {
+ val b = buf(pos)
+ head = pos + 1
+ b == t || ((b == ' ' || b == '\n' || (b | 0x4) == '\r') && nextToken(
+ pos + 1
+ ) == t)
+ } else isNextToken(t, loadMoreOrError(pos))
+
+ private[this] def isCurrentToken(t: Byte, pos: Int): Boolean = {
+ if (pos == 0) illegalTokenOperation()
+ buf(pos - 1) == t
+ }
+
+ private[this] def illegalTokenOperation(): Nothing =
+ throw new IllegalStateException(
+ "expected preceding call of 'nextToken()' or 'isNextToken()'"
+ )
+
+ private[this] def missingSetMarkOperation(): Nothing =
+ throw new IllegalStateException("expected preceding call of 'setMark()'")
+
+ private[this] def appendChar(ch: Char, i: Int): Int = {
+ ensureCharBufCapacity(i + 1)
+ charBuf(i) = ch
+ i + 1
+ }
+
+ private[this] def appendChars(cs: Array[Char], i: Int): Int = {
+ val len = cs.length
+ val required = i + len
+ ensureCharBufCapacity(required)
+ System.arraycopy(cs, 0, charBuf, i, len)
+ required
+ }
+
+ private[this] def appendString(s: String, i: Int): Int = {
+ val len = s.length
+ val required = i + len
+ ensureCharBufCapacity(required)
+ s.getChars(0, len, charBuf, i)
+ required
+ }
+
+ private[this] def prependString(s: String, i: Int): Int = {
+ val len = s.length
+ val required = i + len
+ ensureCharBufCapacity(required)
+ var i1 = required - 1
+ var i2 = i1 - len
+ while (i2 >= 0) {
+ charBuf(i1) = charBuf(i2)
+ i1 -= 1
+ i2 -= 1
+ }
+ s.getChars(0, len, charBuf, 0)
+ required
+ }
+
+ @tailrec
+ private[this] def parseBoolean(isToken: Boolean, pos: Int): Boolean =
+ if (pos + 3 < tail) {
+ val bs = ByteArrayAccess.getInt(buf, pos)
+ if (bs == 0x65757274) {
+ head = pos + 4
+ true
+ } else if (bs == 0x736c6166) {
+ if (nextByte(pos + 4) != 'e') booleanError(pos + 4)
+ false
+ } else if (
+ isToken && {
+ val b1 = bs.toByte
+ b1 == ' ' || b1 == '\n' || (b1 | 0x4) == '\r'
+ }
+ ) parseBoolean(isToken, pos + 1)
+ else booleanError(bs, pos)
+ } else parseBoolean(isToken, loadMoreOrError(pos))
+
+ private[this] def booleanError(bs: Int, pos: Int): Nothing =
+ booleanError(
+ (Math.max(
+ java.lang.Integer.numberOfTrailingZeros(bs ^ 0x65757274),
+ java.lang.Integer.numberOfTrailingZeros(bs ^ 0x736c6166)
+ ) >> 3) + pos
+ )
+
+ private[this] def booleanError(pos: Int): Nothing =
+ decodeError("illegal boolean", pos)
+
+ def readByte(isToken: Boolean): Byte = {
+ var b =
+ if (isToken) nextToken(head)
+ else nextByte(head)
+ var s = 0
+ if (b == '-') {
+ b = nextByte(head)
+ s = -1
+ }
+ if (b < '0' || b > '9') numberError()
+ var x = b - '0'
+ if (isToken && x == 0) ensureNotLeadingZero()
+ else {
+ var pos = head
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ x = x * 10 + (b - '0')
+ if (x > 128) byteOverflowError(pos)
+ pos += 1
+ }
+ head = pos
+ x ^= s
+ x -= s
+ if (x == 128) byteOverflowError(pos - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ }
+ x.toByte
+ }
+
+ def readShort(isToken: Boolean): Short = {
+ var b =
+ if (isToken) nextToken(head)
+ else nextByte(head)
+ var s = 0
+ if (b == '-') {
+ b = nextByte(head)
+ s = -1
+ }
+ if (b < '0' || b > '9') numberError()
+ var x = b - '0'
+ if (isToken && x == 0) ensureNotLeadingZero()
+ else {
+ var pos = head
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ x = x * 10 + (b - '0')
+ if (x > 32768) shortOverflowError(pos)
+ pos += 1
+ }
+ head = pos
+ x ^= s
+ x -= s
+ if (x == 32768) shortOverflowError(pos - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ }
+ x.toShort
+ }
+
+ def readInt(isToken: Boolean): Int = {
+ var b =
+ if (isToken) nextToken(head)
+ else nextByte(head)
+ var s = -1
+ if (b == '-') {
+ b = nextByte(head)
+ s = 0
+ }
+ if (b < '0' || b > '9') numberError()
+ var x = '0' - b
+ if (isToken && x == 0) ensureNotLeadingZero()
+ else {
+ var pos = head
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (
+ x < -214748364 || {
+ x = x * 10 + ('0' - b)
+ x > 0
+ }
+ ) intOverflowError(pos)
+ pos += 1
+ }
+ head = pos
+ x ^= s
+ x -= s
+ if ((s & x) == -2147483648) intOverflowError(pos - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ }
+ x
+ }
+
+ def readLong(isToken: Boolean): Long = {
+ var b =
+ if (isToken) nextToken(head)
+ else nextByte(head)
+ var s = -1L
+ if (b == '-') {
+ b = nextByte(head)
+ s = 0L
+ }
+ if (b < '0' || b > '9') numberError()
+ var x = ('0' - b).toLong
+ if (isToken && x == 0) ensureNotLeadingZero()
+ else {
+ var pos = head
+ var buf = this.buf
+ var dec = 0L
+ while (
+ (pos + 7 < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos + 7 < tail
+ }) && {
+ val bs = ByteArrayAccess.getLong(
+ buf,
+ pos
+ ) // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ dec = bs - 0x3030303030303030L
+ ((bs + 0x4646464646464646L | dec) & 0x8080808080808080L) == 0
+ }
+ ) {
+ if (
+ x < -92233720368L || {
+ dec *= 2561
+ x *= 100000000
+ x -= ((dec >> 8 & 0xff000000ffL) * 4294967296000100L + (dec >> 24 & 0xff000000ffL) * 42949672960001L >> 32)
+ x > 0
+ }
+ ) longOverflowError(pos + 2)
+ pos += 8
+ }
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (
+ x < -922337203685477580L || {
+ x = x * 10 + ('0' - b)
+ x > 0
+ }
+ ) longOverflowError(pos)
+ pos += 1
+ }
+ head = pos
+ x ^= s
+ x -= s
+ if ((s & x) == -9223372036854775808L) longOverflowError(pos - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ }
+ x
+ }
+
+ private[this] def ensureNotLeadingZero(): Unit = {
+ var pos = head
+ if (
+ (pos < tail || {
+ pos = loadMore(pos)
+ pos < tail
+ }) && {
+ val b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) leadingZeroError(pos - 1)
+ }
+
+
+
+ def readFloat(isToken: Boolean): Float = {
+ var b =
+ if (isToken) nextToken(head)
+ else nextByte(head)
+ var isNeg = false
+ if (b == '-') {
+ b = nextByte(head)
+ isNeg = true
+ }
+ if (b < '0' || b > '9') numberError()
+ var pos = head
+ var buf = this.buf
+ val from = pos - 1
+ val oldMark = mark
+ val newMark =
+ if (oldMark < 0) from
+ else oldMark
+ mark = newMark
+ var m10 = (b - '0').toLong
+ var e10 = 0
+ var digits = 1
+ if (isToken && m10 == 0) {
+ if (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) leadingZeroError(pos - 1)
+ } else {
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (m10 < 922337203685477580L) {
+ m10 = m10 * 10 + (b - '0')
+ digits += 1
+ } else e10 += 1
+ pos += 1
+ }
+ }
+ if (b == '.') {
+ pos += 1
+ e10 += digits
+ var noFracDigits = true
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (m10 < 922337203685477580L) {
+ m10 = m10 * 10 + (b - '0')
+ digits += 1
+ }
+ noFracDigits = false
+ pos += 1
+ }
+ e10 -= digits
+ if (noFracDigits) numberError(pos)
+ }
+ if ((b | 0x20) == 'e') {
+ b = nextByte(pos + 1)
+ var s = 0
+ if (b == '-' || b == '+') {
+ s = '+' - b >> 31
+ b = nextByte(head)
+ }
+ if (b < '0' || b > '9') numberError()
+ var exp = b - '0'
+ pos = head
+ buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (exp < 214748364) exp = exp * 10 + (b - '0')
+ pos += 1
+ }
+ exp ^= s
+ exp -= s
+ e10 += exp
+ }
+ head = pos
+ var x: Float =
+ if (e10 == 0 && m10 < 922337203685477580L) m10.toFloat
+ else if (m10 < 4294967296L && e10 >= digits - 23 && e10 <= 19 - digits) {
+ val pow10 = pow10Doubles
+ (if (e10 < 0) m10 / pow10(-e10)
+ else m10 * pow10(e10)).toFloat
+ } else toFloat(m10, e10, from, newMark, pos)
+ if (isNeg) x = -x
+ if (mark > oldMark) mark = oldMark
+ x
+ }
+
+ // Based on the 'Moderate Path' algorithm from the awesome library of Alexander Huszagh: https://github.com/Alexhuszagh/rust-lexical
+ // Here is his inspiring post: https://www.reddit.com/r/rust/comments/a6j5j1/making_rust_float_parsing_fast_and_correct
+ private[this] def toFloat(
+ m10: Long,
+ e10: Int,
+ from: Int,
+ newMark: Int,
+ pos: Int
+ ): Float =
+ if (m10 == 0 || e10 < -64) 0.0f
+ else if (e10 >= 39) Float.PositiveInfinity
+ else {
+ var shift = java.lang.Long.numberOfLeadingZeros(m10)
+ var m2 = unsignedMultiplyHigh(
+ pow10Mantissas(e10 + 343),
+ m10 << shift
+ ) // FIXME: Use Math.unsignedMultiplyHigh after dropping of JDK 17 support
+ var e2 =
+ (e10 * 108853 >> 15) - shift + 1 // (e10 * Math.log(10) / Math.log(2)).toInt - shift + 1
+ shift = java.lang.Long.numberOfLeadingZeros(m2)
+ m2 <<= shift
+ e2 -= shift
+ val roundingError =
+ (if (m10 < 922337203685477580L) 1
+ else 19) << shift
+ val truncatedBitNum = Math.max(-149 - e2, 40)
+ val savedBitNum = 64 - truncatedBitNum
+ val mask = -1L >>> Math.max(savedBitNum, 0)
+ val halfwayDiff = (m2 & mask) - (mask >>> 1)
+ if (Math.abs(halfwayDiff) > roundingError || savedBitNum <= 0)
+ java.lang.Float.intBitsToFloat {
+ var mf = 0
+ if (savedBitNum > 0) mf = (m2 >>> truncatedBitNum).toInt
+ e2 += truncatedBitNum
+ if (savedBitNum >= 0 && halfwayDiff > 0) {
+ if (mf == 0xffffff) {
+ mf = 0x800000
+ e2 += 1
+ } else mf += 1
+ }
+ if (e2 == -149) mf
+ else if (e2 >= 105) 0x7f800000
+ else e2 + 150 << 23 | mf & 0x7fffff
+ }
+ else toFloat(from, newMark, pos)
+ }
+
+ private[this] def toFloat(from: Int, newMark: Int, pos: Int): Float = {
+ var offset = from
+ if (mark == 0) offset -= newMark
+ java.lang.Float.parseFloat(new String(buf, 0, offset, pos - offset))
+ }
+
+ private[this] def unsignedMultiplyHigh(x: Long, y: Long): Long =
+ Math.multiplyHigh(
+ x,
+ y
+ ) + x + y // Use implementation that works only when both params are negative
+
+ def readBigInt(
+ isToken: Boolean,
+ default: BigInt,
+ digitsLimit: Int
+ ): BigInt = {
+ var b =
+ if (isToken) nextToken(head)
+ else nextByte(head)
+ if (isToken && b == 'n') readNullOrNumberError(default, head)
+ else {
+ var s = 0
+ if (b == '-') {
+ b = nextByte(head)
+ s = -1
+ }
+ if (b < '0' || b > '9') numberError()
+ if (isToken && b == '0') {
+ ensureNotLeadingZero()
+ BigInt(0)
+ } else {
+ var pos = head
+ var buf = this.buf
+ var from = pos - 1
+ val oldMark = mark
+ val newMark =
+ if (oldMark < 0) from
+ else oldMark
+ mark = newMark
+ var m, bs = 0L
+ while (
+ (pos + 7 < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos + 7 < tail
+ }) && {
+ bs = ByteArrayAccess.getLong(
+ buf,
+ pos
+ ) // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ m = (bs + 0x4646464646464646L | bs - 0x3030303030303030L) & 0x8080808080808080L
+ m == 0
+ }
+ ) pos += 8
+ if (m == 0) {
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) pos += 1
+ } else {
+ val offset = java.lang.Long.numberOfTrailingZeros(m) >> 3
+ pos += offset
+ b = (bs >> (offset << 3)).toByte
+ }
+ head = pos
+ if (mark == 0) from -= newMark
+ if (mark > oldMark) mark = oldMark
+ val len = pos - from
+ if (len >= digitsLimit) digitsLimitError(from + digitsLimit - 1)
+ if ((b | 0x20) == 'e' || b == '.') numberError(pos)
+ if (len < 19) {
+ var x = (buf(from) - '0').toLong
+ from += 1
+ while (from < pos) {
+ x = x * 10 + (buf(from) - '0')
+ from += 1
+ }
+ BigInt((x ^ s) - s)
+ } else if (len <= 36) toBigInt36(buf, from, pos, s)
+ else
+ new BigInt({
+ if (len <= 308) toBigInteger308(buf, from, pos, s)
+ else {
+ // Based on the great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing huge numbers
+ // with O(n^1.5) complexity instead of O(n^2) when using the constructor for the decimal representation from JDK:
+ // https://github.com/eobermuhlner/big-math/commit/7a5419aac8b2adba2aa700ccf00197f97b2ad89f
+ val mid = len >> 1
+ val midPos = pos - mid
+ toBigDecimal(buf, from, midPos, s, -mid)
+ .add(toBigDecimal(buf, midPos, pos, s, 0))
+ .unscaledValue
+ }
+ })
+ }
+ }
+ }
+
+ def readBigDecimal(
+ isToken: Boolean,
+ default: BigDecimal,
+ mc: MathContext,
+ scaleLimit: Int,
+ digitsLimit: Int
+ ): BigDecimal = {
+ var b =
+ if (isToken) nextToken(head)
+ else nextByte(head)
+ if (isToken && b == 'n') readNullOrNumberError(default, head)
+ else {
+ var s = 0
+ if (b == '-') {
+ b = nextByte(head)
+ s = -1
+ }
+ if (b < '0' || b > '9') numberError()
+ var pos = head
+ var buf = this.buf
+ var from = pos - 1
+ val oldMark = mark
+ val newMark =
+ if (oldMark < 0) from
+ else oldMark
+ mark = newMark
+ var digits = 1
+ if (isToken && b == '0') {
+ if (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) leadingZeroError(pos - 1)
+ } else {
+ digits -= pos
+ var m, bs = 0L
+ while (
+ (pos + 7 < tail || {
+ digits += pos
+ pos = loadMore(pos)
+ digits -= pos
+ buf = this.buf
+ pos + 7 < tail
+ }) && {
+ bs = ByteArrayAccess.getLong(
+ buf,
+ pos
+ ) // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ m = (bs + 0x4646464646464646L | bs - 0x3030303030303030L) & 0x8080808080808080L
+ m == 0
+ }
+ ) pos += 8
+ if (m == 0) {
+ while (
+ (pos < tail || {
+ digits += pos
+ pos = loadMore(pos)
+ digits -= pos
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) pos += 1
+ } else {
+ val offset = java.lang.Long.numberOfTrailingZeros(m) >> 3
+ pos += offset
+ b = (bs >> (offset << 3)).toByte
+ }
+ digits += pos
+ }
+ var fracLen, scale = 0
+ if (digits >= digitsLimit)
+ digitsLimitError(pos + digitsLimit - digits - 1)
+ if (b == '.') {
+ pos += 1
+ fracLen -= pos
+ var m, bs = 0L
+ while (
+ (pos + 7 < tail || {
+ fracLen += pos
+ pos = loadMore(pos)
+ fracLen -= pos
+ buf = this.buf
+ pos + 7 < tail
+ }) && {
+ bs = ByteArrayAccess.getLong(
+ buf,
+ pos
+ ) // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ m = (bs + 0x4646464646464646L | bs - 0x3030303030303030L) & 0x8080808080808080L
+ m == 0
+ }
+ ) pos += 8
+ if (m == 0) {
+ while (
+ (pos < tail || {
+ fracLen += pos
+ pos = loadMore(pos)
+ fracLen -= pos
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) pos += 1
+ } else {
+ val offset = java.lang.Long.numberOfTrailingZeros(m) >> 3
+ pos += offset
+ b = (bs >> (offset << 3)).toByte
+ }
+ fracLen += pos
+ digits += fracLen
+ if (fracLen == 0) numberError(pos)
+ if (digits >= digitsLimit)
+ digitsLimitError(pos + digitsLimit - digits - 1)
+ }
+ if ((b | 0x20) == 'e') {
+ b = nextByte(pos + 1)
+ var ss = 0
+ if (b == '-' || b == '+') {
+ ss = '+' - b >> 31
+ b = nextByte(head)
+ }
+ if (b < '0' || b > '9') numberError()
+ scale = '0' - b
+ pos = head
+ buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ b = buf(pos)
+ b >= '0' && b <= '9'
+ }
+ ) {
+ if (
+ scale < -214748364 || {
+ scale = scale * 10 + ('0' - b)
+ scale > 0
+ }
+ ) numberError(pos)
+ pos += 1
+ }
+ scale ^= ss
+ scale -= ss
+ if (scale == -2147483648) numberError(pos - 1)
+ }
+ head = pos
+ if (mark == 0) from -= newMark
+ if (mark > oldMark) mark = oldMark
+ var d =
+ if (fracLen != 0) {
+ val limit = from + digits + 1
+ val fracPos = limit - fracLen
+ val fracLimit = fracPos - 1
+ if (digits < 19) {
+ var x = (buf(from) - '0').toLong
+ from += 1
+ while (from < fracLimit) {
+ x = x * 10 + (buf(from) - '0')
+ from += 1
+ }
+ from += 1
+ while (from < limit) {
+ x = x * 10 + (buf(from) - '0')
+ from += 1
+ }
+ java.math.BigDecimal.valueOf((x ^ s) - s, scale + fracLen)
+ } else
+ toBigDecimal(buf, from, fracLimit, s, scale).add(
+ toBigDecimal(buf, fracPos, limit, s, scale + fracLen)
+ )
+ } else toBigDecimal(buf, from, from + digits, s, scale)
+ if (mc.getPrecision < digits) d = d.plus(mc)
+ if (Math.abs(d.scale) >= scaleLimit) scaleLimitError()
+ new BigDecimal(d, mc)
+ }
+ }
+
+ private[this] def toBigDecimal(
+ buf: Array[Byte],
+ p: Int,
+ limit: Int,
+ s: Int,
+ scale: Int
+ ): java.math.BigDecimal = {
+ val len = limit - p
+ if (len < 19) {
+ var pos = p
+ var x = (buf(pos) - '0').toLong
+ pos += 1
+ while (pos < limit) {
+ x = x * 10 + (buf(pos) - '0')
+ pos += 1
+ }
+ java.math.BigDecimal.valueOf((x ^ s) - s, scale)
+ } else if (len <= 36) toBigDecimal36(buf, p, limit, s, scale)
+ else if (len <= 308)
+ new java.math.BigDecimal(toBigInteger308(buf, p, limit, s), scale)
+ else {
+ // Based on the great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing really big numbers
+ // with O(n^1.5) complexity instead of O(n^2) when using the constructor for the decimal representation from JDK:
+ // https://github.com/eobermuhlner/big-math/commit/7a5419aac8b2adba2aa700ccf00197f97b2ad89f
+ val mid = len >> 1
+ val midPos = limit - mid
+ toBigDecimal(buf, p, midPos, s, scale - mid).add(
+ toBigDecimal(buf, midPos, limit, s, scale)
+ )
+ }
+ }
+
+ private[this] def toBigInt36(
+ buf: Array[Byte],
+ p: Int,
+ limit: Int,
+ s: Int
+ ): BigInt = {
+ val firstBlockLimit = limit - 18
+ var pos = p
+ var x1 = (buf(pos) - '0').toLong
+ pos += 1
+ while (pos < firstBlockLimit) {
+ x1 = x1 * 10 + (buf(pos) - '0')
+ pos += 1
+ }
+ val x2 =
+ ({ // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 8)) * 1000000000 + {
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos + 9) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 17) - 48000000048L
+ val q = x1 * 1000000000000000000L
+ val l = q + x2
+ val h = Math.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
+ if (l >= 0 && h == 0) BigInt((l ^ s) - s)
+ else {
+ var magnitude = this.magnitude
+ if (magnitude eq null) {
+ magnitude = new Array[Byte](128)
+ this.magnitude = magnitude
+ }
+ ByteArrayAccess.setLongReversed(magnitude, 0, h)
+ ByteArrayAccess.setLongReversed(magnitude, 8, l)
+ new BigInt(new java.math.BigInteger(s | 1, magnitude, 0, 16))
+ }
+ }
+
+ private[this] def toBigDecimal36(
+ buf: Array[Byte],
+ p: Int,
+ limit: Int,
+ s: Int,
+ scale: Int
+ ): java.math.BigDecimal = {
+ val firstBlockLimit = limit - 18
+ var pos = p
+ var x1 = (buf(pos) - '0').toLong
+ pos += 1
+ while (pos < firstBlockLimit) {
+ x1 = x1 * 10 + (buf(pos) - '0')
+ pos += 1
+ }
+ val x2 =
+ ({ // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 8)) * 1000000000 + {
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos + 9) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 17) - 48000000048L
+ val q = x1 * 1000000000000000000L
+ val l = q + x2
+ val h = Math.multiplyHigh(x1, 1000000000000000000L) + ((~l & q) >>> 63)
+ if (l >= 0 && h == 0) java.math.BigDecimal.valueOf((l ^ s) - s, scale)
+ else {
+ var magnitude = this.magnitude
+ if (magnitude eq null) {
+ magnitude = new Array[Byte](128)
+ this.magnitude = magnitude
+ }
+ ByteArrayAccess.setLongReversed(magnitude, 0, h)
+ ByteArrayAccess.setLongReversed(magnitude, 8, l)
+ new java.math.BigDecimal(
+ new java.math.BigInteger(s | 1, magnitude, 0, 16),
+ scale
+ )
+ }
+ }
+
+ private[this] def toBigInteger308(
+ buf: Array[Byte],
+ p: Int,
+ limit: Int,
+ s: Int
+ ): java.math.BigInteger = {
+ val len = limit - p
+ val last =
+ (len * 222930821L >> 32).toInt << 3 // (len * Math.log(10) / Math.log(1L << 64)).toInt * 8
+ var magnitude = this.magnitude
+ if (magnitude eq null) {
+ magnitude = new Array[Byte](128)
+ this.magnitude = magnitude
+ } else {
+ var i = 0
+ while (i < last) {
+ ByteArrayAccess.setLong(magnitude, i, 0L)
+ i += 8
+ }
+ }
+ var x = 0L
+ val firstBlockLimit = len % 18 + p
+ var pos = p
+ while (pos < firstBlockLimit) {
+ x = x * 10 + (buf(pos) - '0')
+ pos += 1
+ }
+ ByteArrayAccess.setLong(magnitude, last, x)
+ var first = last
+ while (pos < limit) {
+ x =
+ ({ // Based on the fast parsing of numbers by 8-byte words: https://github.com/wrandelshofer/FastDoubleParser/blob/0903817a765b25e654f02a5a9d4f1476c98a80c9/src/main/java/ch.randelshofer.fastdoubleparser/ch/randelshofer/fastdoubleparser/FastDoubleSimd.java#L114-L130
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 8)) * 1000000000 + {
+ val dec =
+ (ByteArrayAccess.getLong(buf, pos + 9) - 0x3030303030303030L) * 2561
+ (dec >> 8 & 0xff000000ffL) * 42949672960001000L + (dec >> 24 & 0xff000000ffL) * 429496729600010L >> 32
+ } + buf(pos + 17) - 48000000048L
+ pos += 18
+ first = Math.max(first - 8, 0)
+ var i = last
+ val q = 1000000000000000000L
+ var m, mq = 0L
+ while ({
+ m = ByteArrayAccess.getLong(magnitude, i)
+ mq = m * q
+ x += mq
+ ByteArrayAccess.setLong(magnitude, i, x)
+ i -= 8
+ i >= first
+ }) {
+ x = Math.multiplyHigh(
+ m,
+ q
+ ) + (m >> 63 & q) + ((~x & mq) >>> 63) // TODO: when dropping JDK 17 support replace by Math.unsignedMultiplyHigh(m, q) + ((~x & mq) >>> 63)
+ }
+ }
+ var i = 0
+ while (i <= last) {
+ ByteArrayAccess.setLongReversed(
+ magnitude,
+ i,
+ ByteArrayAccess.getLong(magnitude, i)
+ )
+ i += 8
+ }
+ new java.math.BigInteger(s | 1, magnitude, 0, last + 8)
+ }
+
+ @tailrec
+ private[this] def readNullOrNumberError[@specialized A](
+ default: A,
+ pos: Int
+ ): A =
+ if (default != null) {
+ if (pos + 2 < tail) {
+ val bs = ByteArrayAccess.getInt(buf, pos - 1)
+ if (bs == 0x6c6c756e) {
+ head = pos + 3
+ default
+ } else decodeError("expected number or null", bs, pos)
+ } else readNullOrNumberError(default, loadMoreOrError(pos - 1) + 1)
+ } else numberError(pos - 1)
+
+ private[this] def numberError(pos: Int = head - 1): Nothing =
+ decodeError("illegal number", pos)
+
+ private[this] def digitsLimitError(pos: Int): Nothing =
+ decodeError("value exceeds limit for number of digits", pos)
+
+ private[this] def scaleLimitError(pos: Int = head - 1): Nothing =
+ decodeError("value exceeds limit for scale", pos)
+
+ private[this] def leadingZeroError(pos: Int): Nothing =
+ decodeError("illegal number with leading zero", pos)
+
+ private[this] def byteOverflowError(pos: Int): Nothing =
+ decodeError("value is too large for byte", pos)
+
+ private[this] def shortOverflowError(pos: Int): Nothing =
+ decodeError("value is too large for short", pos)
+
+ private[this] def intOverflowError(pos: Int): Nothing =
+ decodeError("value is too large for int", pos)
+
+ private[this] def longOverflowError(pos: Int): Nothing =
+ decodeError("value is too large for long", pos)
+
+ private[this] def decodeError(msg: String, bs: Int, pos: Int): Nothing =
+ decodeError(
+ msg,
+ (java.lang.Integer.numberOfTrailingZeros(bs ^ 0x6c6c756e) >> 3) + pos - 1
+ )
+
+ private[this] def tokenOrNullError(t: Byte, bs: Int, pos: Int): Nothing =
+ tokenOrNullError(
+ t,
+ (java.lang.Integer.numberOfTrailingZeros(bs ^ 0x6c6c756e) >> 3) + pos - 1
+ )
+
+ private[this] def digitError(pos: Int): Nothing =
+ decodeError("expected digit", pos)
+
+ @tailrec
+ private[this] def parseString(
+ i: Int,
+ minLim: Int,
+ charBuf: Array[Char],
+ pos: Int
+ ): Int =
+ if (i + 3 < minLim) { // Based on SWAR routine of JSON string parsing: https://github.com/sirthias/borer/blob/fde9d1ce674d151b0fee1dd0c2565020c3f6633a/core/src/main/scala/io/bullet/borer/json/JsonParser.scala#L456
+ val bs = ByteArrayAccess.getInt(buf, pos)
+ val m =
+ ((bs - 0x20202020 ^ 0x3c3c3c3c) - 0x1010101 | (bs ^ 0x5d5d5d5d) + 0x1010101) & 0x80808080
+ charBuf(i) = (bs & 0xff).toChar
+ charBuf(i + 1) = (bs >> 8 & 0xff).toChar
+ charBuf(i + 2) = (bs >> 16 & 0xff).toChar
+ charBuf(i + 3) = (bs >> 24).toChar
+ if (m != 0) {
+ val offset = java.lang.Integer.numberOfTrailingZeros(m) >> 3
+ if ((bs >> (offset << 3)).toByte == '"') {
+ head = pos + offset + 1
+ i + offset
+ } else
+ parseEncodedString(
+ i + offset,
+ charBuf.length - 1,
+ charBuf,
+ pos + offset
+ )
+ } else parseString(i + 4, minLim, charBuf, pos + 4)
+ } else if (i < minLim) {
+ val b = buf(pos)
+ charBuf(i) = b.toChar
+ if (b == '"') {
+ head = pos + 1
+ i
+ } else if ((b - 0x20 ^ 0x3c) <= 0)
+ parseEncodedString(i, charBuf.length - 1, charBuf, pos)
+ else parseString(i + 1, minLim, charBuf, pos + 1)
+ } else if (pos >= tail) {
+ val newPos = loadMoreOrError(pos)
+ parseString(
+ i,
+ Math.min(charBuf.length, i + tail - newPos),
+ charBuf,
+ newPos
+ )
+ } else
+ parseString(
+ i,
+ Math.min(growCharBuf(i + 1), i + tail - pos),
+ this.charBuf,
+ pos
+ )
+
+ @tailrec
+ private[this] def parseEncodedString(
+ i: Int,
+ lim: Int,
+ charBuf: Array[Char],
+ pos: Int
+ ): Int = {
+ val remaining = tail - pos
+ if (i < lim) {
+ if (remaining > 0) {
+ val b1 = buf(pos)
+ if (b1 >= 0) {
+ if (b1 == '"') {
+ head = pos + 1
+ i
+ } else if (b1 != '\\') { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char)
+ if (b1 < ' ') unescapedControlCharacterError(pos)
+ charBuf(i) = b1.toChar
+ parseEncodedString(i + 1, lim, charBuf, pos + 1)
+ } else if (remaining > 1) {
+ val b2 = buf(pos + 1)
+ if (b2 != 'u') {
+ charBuf(i) = (b2: @switch) match {
+ case '"' => '"'
+ case 'n' => '\n'
+ case 'r' => '\r'
+ case 't' => '\t'
+ case 'b' => '\b'
+ case 'f' => '\f'
+ case '\\' => '\\'
+ case '/' => '/'
+ case _ => escapeSequenceError(pos + 1)
+ }
+ parseEncodedString(i + 1, lim, charBuf, pos + 2)
+ } else if (remaining > 5) {
+ val ch1 = readEscapedUnicode(pos + 2, buf)
+ charBuf(i) = ch1
+ if ((ch1 & 0xf800) != 0xd800)
+ parseEncodedString(i + 1, lim, charBuf, pos + 6)
+ else if (remaining > 11) {
+ if (buf(pos + 6) != '\\') escapeSequenceError(pos + 6)
+ if (buf(pos + 7) != 'u') escapeSequenceError(pos + 7)
+ val ch2 = readEscapedUnicode(pos + 8, buf)
+ charBuf(i + 1) = ch2
+ if (ch1 >= 0xdc00 || (ch2 & 0xfc00) != 0xdc00)
+ decodeError("illegal surrogate character pair", pos + 11)
+ parseEncodedString(i + 2, lim, charBuf, pos + 12)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else if ((b1 & 0xe0) == 0xc0) { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char)
+ if (remaining > 1) {
+ val b2 = buf(pos + 1)
+ val ch =
+ (b1 << 6 ^ b2 ^ 0xf80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte
+ charBuf(i) = ch
+ if ((b2 & 0xc0) != 0x80 || ch < 0x80)
+ malformedBytesError(b1, b2, pos)
+ parseEncodedString(i + 1, lim, charBuf, pos + 2)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else if ((b1 & 0xf0) == 0xe0) { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char)
+ if (remaining > 2) {
+ val b2 = buf(pos + 1)
+ val b3 = buf(pos + 2)
+ val ch =
+ (b1 << 12 ^ b2 << 6 ^ b3 ^ 0x1f80).toChar // 0x1F80 == (0x80.toByte << 6 ^ 0x80.toByte).toChar
+ charBuf(i) = ch
+ if (
+ (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || ch < 0x800 ||
+ (ch & 0xf800) == 0xd800
+ ) malformedBytesError(b1, b2, b3, pos)
+ parseEncodedString(i + 1, lim, charBuf, pos + 3)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else if ((b1 & 0xf8) == 0xf0) { // 11110ddd 10ddcccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> 110110uuuuccccbb 110111bbbbaaaaaa (UTF-16 chars), where uuuu = ddddd - 1
+ if (remaining > 3) {
+ val b2 = buf(pos + 1)
+ val b3 = buf(pos + 2)
+ val b4 = buf(pos + 3)
+ val cp =
+ b1 << 18 ^ b2 << 12 ^ b3 << 6 ^ b4 ^ 0x381f80 // 0x381F80 == 0xF0.toByte << 18 ^ 0x80.toByte << 12 ^ 0x80.toByte << 6 ^ 0x80.toByte
+ val ch1 =
+ ((cp >>> 10) + 0xd7c0).toChar // 0xD7C0 == 0xD800 - (0x10000 >>> 10)
+ charBuf(i) = ch1
+ charBuf(i + 1) = ((cp & 0x3ff) | 0xdc00).toChar
+ if (
+ (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || (b4 & 0xc0) != 0x80 ||
+ (ch1 & 0xf800) != 0xd800
+ ) malformedBytesError(b1, b2, b3, b4, pos)
+ parseEncodedString(i + 2, lim, charBuf, pos + 4)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else malformedBytesError(b1, pos)
+ } else parseEncodedString(i, lim, charBuf, loadMoreOrError(pos))
+ } else
+ parseEncodedString(
+ i,
+ growCharBuf(i + 2) - 1,
+ this.charBuf,
+ pos
+ ) // 2 is length of surrogate pair
+ }
+
+ @tailrec
+ private[this] def parseChar(pos: Int): Char = {
+ val remaining = tail - pos
+ if (remaining > 0) {
+ val b1 = buf(pos)
+ if (b1 >= 0) {
+ if (b1 == '"') characterError(pos)
+ else if (b1 != '\\') { // 0aaaaaaa (UTF-8 byte) -> 000000000aaaaaaa (UTF-16 char)
+ if (b1 < ' ') unescapedControlCharacterError(pos)
+ head = pos + 1
+ return b1.toChar
+ } else if (remaining > 1) {
+ val b2 = buf(pos + 1)
+ if (b2 != 'u') {
+ head = pos + 2
+ (b2: @switch) match {
+ case 'b' => return '\b'
+ case 'f' => return '\f'
+ case 'n' => return '\n'
+ case 'r' => return '\r'
+ case 't' => return '\t'
+ case '"' => return '"'
+ case '/' => return '/'
+ case '\\' => return '\\'
+ case _ => escapeSequenceError(pos + 1)
+ }
+ } else if (remaining > 5) {
+ val ch = readEscapedUnicode(pos + 2, buf)
+ head = pos + 6
+ if ((ch & 0xf800) == 0xd800) surrogateCharacterError(pos + 5)
+ return ch
+ }
+ }
+ } else if ((b1 & 0xe0) == 0xc0) { // 110bbbbb 10aaaaaa (UTF-8 bytes) -> 00000bbbbbaaaaaa (UTF-16 char)
+ if (remaining > 1) {
+ val b2 = buf(pos + 1)
+ val ch =
+ (b1 << 6 ^ b2 ^ 0xf80).toChar // 0xF80 == 0xC0.toByte << 6 ^ 0x80.toByte
+ head = pos + 2
+ if ((b2 & 0xc0) != 0x80 || ch < 0x80) malformedBytesError(b1, b2, pos)
+ return ch
+ }
+ } else if ((b1 & 0xf0) == 0xe0) { // 1110cccc 10bbbbbb 10aaaaaa (UTF-8 bytes) -> ccccbbbbbbaaaaaa (UTF-16 char)
+ if (remaining > 2) {
+ val b2 = buf(pos + 1)
+ val b3 = buf(pos + 2)
+ val ch =
+ (b1 << 12 ^ b2 << 6 ^ b3 ^ 0x1f80).toChar // 0x1F80 == (0x80.toByte << 6 ^ 0x80.toByte).toChar
+ head = pos + 3
+ if (
+ (b2 & 0xc0) != 0x80 || (b3 & 0xc0) != 0x80 || ch < 0x800 ||
+ (ch & 0xf800) == 0xd800
+ ) malformedBytesError(b1, b2, b3, pos)
+ return ch
+ }
+ } else if ((b1 & 0xf8) == 0xf0) surrogateCharacterError(pos + 3)
+ else malformedBytesError(b1, pos)
+ }
+ parseChar(loadMoreOrError(pos))
+ }
+
+ private[this] def readEscapedUnicode(pos: Int, buf: Array[Byte]): Char = {
+ val ns = nibbles
+ val x =
+ ns(buf(pos) & 0xff) << 12 |
+ ns(buf(pos + 1) & 0xff) << 8 |
+ ns(buf(pos + 2) & 0xff) << 4 |
+ ns(buf(pos + 3) & 0xff)
+ if (x < 0) hexDigitError(pos)
+ x.toChar
+ }
+
+ private[this] def parseBase16(ns: Array[Byte]): Array[Byte] = {
+ var charBuf = this.charBuf
+ var len = charBuf.length
+ var pos = head
+ var buf = this.buf
+ var i, bits = 0
+ while (
+ bits >= 0 && (pos + 3 < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos + 3 < tail
+ })
+ ) {
+ if (i >= len) {
+ len = growCharBuf(i + 1)
+ charBuf = this.charBuf
+ }
+ val posLim = Math.min(tail - 3, (len - i << 2) + pos)
+ while (
+ pos < posLim && {
+ bits = ns(buf(pos) & 0xff) << 12 |
+ ns(buf(pos + 1) & 0xff) << 8 |
+ ns(buf(pos + 2) & 0xff) << 4 |
+ ns(buf(pos + 3) & 0xff)
+ bits >= 0
+ }
+ ) {
+ charBuf(i) = bits.toChar
+ i += 1
+ pos += 4
+ }
+ }
+ val bLen = i << 1
+ var bs: Array[Byte] = null
+ var b = nextByte(pos)
+ if (b == '"') bs = new Array[Byte](bLen)
+ else {
+ bits = ns(b & 0xff).toInt
+ if (bits < 0) decodeError("expected '\"' or hex digit")
+ b = nextByte(head)
+ bits = bits << 4 | ns(b & 0xff)
+ if (bits < 0) decodeError("expected hex digit")
+ b = nextByte(head)
+ if (b != '"') {
+ if (ns(b & 0xff) < 0) decodeError("expected '\"' or hex digit")
+ b = nextByte(head)
+ decodeError("expected hex digit")
+ }
+ bs = new Array[Byte](bLen + 1)
+ bs(bLen) = bits.toByte
+ }
+ i = 0
+ var j = 0
+ while (j < bLen) {
+ val ch = charBuf(i)
+ bs(j) = (ch >> 8).toByte
+ bs(j + 1) = ch.toByte
+ i += 1
+ j += 2
+ }
+ bs
+ }
+
+ private[this] def parseBase64(ds: Array[Byte]): Array[Byte] = {
+ var charBuf = this.charBuf
+ var lenM1 = charBuf.length - 1
+ var pos = head
+ var buf = this.buf
+ var i, bits = 0
+ while (
+ bits >= 0 && (pos + 3 < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos + 3 < tail
+ })
+ ) {
+ if (i >= lenM1) {
+ lenM1 = growCharBuf(i + 1) - 1
+ charBuf = this.charBuf
+ }
+ val posLim = Math.min(tail - 3, (lenM1 - i << 1) + pos)
+ while (
+ pos < posLim && {
+ bits = ds(buf(pos) & 0xff) << 18 |
+ ds(buf(pos + 1) & 0xff) << 12 |
+ ds(buf(pos + 2) & 0xff) << 6 |
+ ds(buf(pos + 3) & 0xff)
+ bits >= 0
+ }
+ ) {
+ charBuf(i) = (bits >> 8).toChar
+ charBuf(i + 1) = bits.toChar
+ i += 2
+ pos += 4
+ }
+ }
+ val bLen = i + (i >> 1)
+ var bs: Array[Byte] = null
+ var b = nextByte(pos)
+ if (b == '"') bs = new Array[Byte](bLen)
+ else {
+ bits = ds(b & 0xff).toInt
+ if (bits < 0) decodeError("expected '\"' or base64 digit")
+ b = nextByte(head)
+ bits = bits << 6 | ds(b & 0xff)
+ if (bits < 0) decodeError("expected base64 digit")
+ b = nextByte(head)
+ if (b == '"' || b == '=') {
+ if (b == '=') {
+ nextByteOrError('=', head)
+ nextByteOrError('"', head)
+ }
+ bs = new Array[Byte](bLen + 1)
+ bs(bLen) = (bits >> 4).toByte
+ } else {
+ bits = bits << 6 | ds(b & 0xff)
+ if (bits < 0) decodeError("expected '\"' or '=' or base64 digit")
+ b = nextByte(head)
+ if (b == '=') nextByteOrError('"', head)
+ else if (b != '"') tokensError('"', '=')
+ bs = new Array[Byte](bLen + 2)
+ bs(bLen) = (bits >> 10).toByte
+ bs(bLen + 1) = (bits >> 2).toByte
+ }
+ }
+ i = 0
+ var j = 0
+ while (j < bLen) {
+ val ch = charBuf(i)
+ bs(j) = (ch >> 8).toByte
+ bs(j + 1) = ch.toByte
+ bs(j + 2) = charBuf(i + 1).toByte
+ i += 2
+ j += 3
+ }
+ bs
+ }
+
+ @tailrec
+ private[this] def hexDigitError(pos: Int): Nothing = {
+ if (nibbles(buf(pos) & 0xff) < 0) decodeError("expected hex digit", pos)
+ hexDigitError(pos + 1)
+ }
+
+ private[this] def characterError(pos: Int): Nothing =
+ decodeError("illegal character", pos)
+
+ private[this] def escapeSequenceError(pos: Int): Nothing =
+ decodeError("illegal escape sequence", pos)
+
+ private[this] def surrogateCharacterError(pos: Int): Nothing =
+ decodeError("illegal surrogate character", pos)
+
+ private[this] def unescapedControlCharacterError(pos: Int): Nothing =
+ decodeError("unescaped control character", pos)
+
+ private[this] def malformedBytesError(b1: Byte, pos: Int): Nothing = {
+ var i = appendString("malformed byte(s): 0x", 0)
+ i = appendHexByte(b1, i, hexDigits)
+ decodeError(i, pos, null)
+ }
+
+ private[this] def malformedBytesError(
+ b1: Byte,
+ b2: Byte,
+ pos: Int
+ ): Nothing = {
+ val ds = hexDigits
+ var i = appendString("malformed byte(s): 0x", 0)
+ i = appendHexByte(b1, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b2, i, ds)
+ decodeError(i, pos + 1, null)
+ }
+
+ private[this] def malformedBytesError(
+ b1: Byte,
+ b2: Byte,
+ b3: Byte,
+ pos: Int
+ ): Nothing = {
+ val ds = hexDigits
+ var i = appendString("malformed byte(s): 0x", 0)
+ i = appendHexByte(b1, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b2, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b3, i, ds)
+ decodeError(i, pos + 2, null)
+ }
+
+ private[this] def malformedBytesError(
+ b1: Byte,
+ b2: Byte,
+ b3: Byte,
+ b4: Byte,
+ pos: Int
+ ): Nothing = {
+ val ds = hexDigits
+ var i = appendString("malformed byte(s): 0x", 0)
+ i = appendHexByte(b1, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b2, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b3, i, ds)
+ i = appendString(", 0x", i)
+ i = appendHexByte(b4, i, ds)
+ decodeError(i, pos + 3, null)
+ }
+
+ private[this] def appendHexDump(pos: Int, offset: Int, from: Int): Int = {
+ val hexDumpSizeInBytes = config.hexDumpSize << 4
+ val start = Math.max(pos - hexDumpSizeInBytes & 0xfffffff0, 0)
+ val end = Math.min(pos + hexDumpSizeInBytes + 16 & 0xfffffff0, tail)
+ val alignedAbsFrom = start + offset & 0xfffffff0
+ val alignedAbsTo = end + offset + 15 & 0xfffffff0
+ val len = alignedAbsTo - alignedAbsFrom
+ val bufOffset = alignedAbsFrom - offset
+ var i = appendChars(dumpBorder, from)
+ i = appendChars(dumpHeader, i)
+ i = appendChars(dumpBorder, i)
+ val buf = this.buf
+ val ds = hexDigits
+ var charBuf = this.charBuf
+ var lim = charBuf.length
+ var j = 0
+ while (j < len) {
+ val linePos = j & 0xf
+ if (linePos == 0) {
+ if (i + 81 >= lim) { // 81 == dumpBorder.length
+ lim = growCharBuf(i + 81)
+ charBuf = this.charBuf
+ }
+ charBuf(i) = '\n'
+ charBuf(i + 1) = '|'
+ charBuf(i + 2) = ' '
+ putHexInt(alignedAbsFrom + j, i + 3, charBuf, ds)
+ charBuf(i + 11) = ' '
+ charBuf(i + 12) = '|'
+ charBuf(i + 13) = ' '
+ i += 14
+ }
+ val pos = bufOffset + j
+ charBuf(i + 50 - (linePos << 1)) = if (pos >= start && pos < end) {
+ val b = buf(pos)
+ charBuf(i) = ds(b >> 4 & 0xf)
+ charBuf(i + 1) = ds(b & 0xf)
+ charBuf(i + 2) = ' '
+ if (b <= 31 || b >= 127) '.'
+ else b.toChar
+ } else {
+ charBuf(i) = ' '
+ charBuf(i + 1) = ' '
+ charBuf(i + 2) = ' '
+ ' '
+ }
+ i += 3
+ if (linePos == 15) {
+ charBuf(i) = '|'
+ charBuf(i + 1) = ' '
+ charBuf(i + 18) = ' '
+ charBuf(i + 19) = '|'
+ i += 20
+ }
+ j += 1
+ }
+ appendChars(dumpBorder, i)
+ }
+
+ private[this] def appendHexOffset(d: Long, i: Int): Int = {
+ ensureCharBufCapacity(i + 16)
+ val ds = hexDigits
+ var j = i
+ val dl = d.toInt
+ if (dl != d) {
+ val dh = (d >> 32).toInt
+ var shift = 32 - java.lang.Integer.numberOfLeadingZeros(dh) & 0x1c
+ while (shift >= 0) {
+ charBuf(j) = ds(dh >> shift & 0xf)
+ shift -= 4
+ j += 1
+ }
+ }
+ putHexInt(dl, j, charBuf, ds)
+ j + 8
+ }
+
+ private[this] def appendHexByte(b: Byte, i: Int, ds: Array[Char]): Int = {
+ ensureCharBufCapacity(i + 2)
+ charBuf(i) = ds(b >> 4 & 0xf)
+ charBuf(i + 1) = ds(b & 0xf)
+ i + 2
+ }
+
+ private[this] def putHexInt(
+ d: Int,
+ i: Int,
+ charBuf: Array[Char],
+ ds: Array[Char]
+ ): Unit = {
+ charBuf(i) = ds(d >>> 28)
+ charBuf(i + 1) = ds(d >> 24 & 0xf)
+ charBuf(i + 2) = ds(d >> 20 & 0xf)
+ charBuf(i + 3) = ds(d >> 16 & 0xf)
+ charBuf(i + 4) = ds(d >> 12 & 0xf)
+ charBuf(i + 5) = ds(d >> 8 & 0xf)
+ charBuf(i + 6) = ds(d >> 4 & 0xf)
+ charBuf(i + 7) = ds(d & 0xf)
+ }
+
+ private[this] def growCharBuf(required: Int): Int = {
+ var charBufLen = charBuf.length
+ val maxCharBufSize = config.maxCharBufSize
+ if (charBufLen == maxCharBufSize) tooLongStringError()
+ charBufLen =
+ (-1 >>> Integer.numberOfLeadingZeros(charBufLen | required)) + 1
+ if (Integer.compareUnsigned(charBufLen, maxCharBufSize) > 0)
+ charBufLen = maxCharBufSize
+ charBuf = java.util.Arrays.copyOf(charBuf, charBufLen)
+ charBufLen
+ }
+
+ private[this] def ensureCharBufCapacity(required: Int): Unit =
+ if (charBuf.length < required) growCharBuf(required): Unit
+
+ @tailrec
+ private[this] def skipString(evenBackSlashes: Boolean, pos: Int): Int =
+ if (pos < tail) {
+ if (evenBackSlashes) {
+ val b = buf(pos)
+ if (b == '"') pos + 1
+ else skipString(b != '\\', pos + 1)
+ } else skipString(evenBackSlashes = true, pos + 1)
+ } else skipString(evenBackSlashes, loadMoreOrError(pos))
+
+ private[this] def skipNumber(p: Int): Int = {
+ var pos = p
+ var buf = this.buf
+ while (
+ (pos < tail || {
+ pos = loadMore(pos)
+ buf = this.buf
+ pos < tail
+ }) && {
+ val b = buf(pos)
+ (b >= '0' && b <= '9') || b == '.' || (b | 0x20) == 'e' || b == '-' || b == '+'
+ }
+ ) pos += 1
+ pos
+ }
+
+ @tailrec
+ private[this] def skipObject(level: Int, pos: Int): Int =
+ if (pos < tail) {
+ val b = buf(pos)
+ if (b == '"')
+ skipObject(level, skipString(evenBackSlashes = true, pos + 1))
+ else if (b == '{') skipObject(level + 1, pos + 1)
+ else if (b != '}') skipObject(level, pos + 1)
+ else if (level != 0) skipObject(level - 1, pos + 1)
+ else pos + 1
+ } else skipObject(level, loadMoreOrError(pos))
+
+ @tailrec
+ private[this] def skipArray(level: Int, pos: Int): Int =
+ if (pos < tail) {
+ val b = buf(pos)
+ if (b == '"')
+ skipArray(level, skipString(evenBackSlashes = true, pos + 1))
+ else if (b == '[') skipArray(level + 1, pos + 1)
+ else if (b != ']') skipArray(level, pos + 1)
+ else if (level != 0) skipArray(level - 1, pos + 1)
+ else pos + 1
+ } else skipArray(level, loadMoreOrError(pos))
+
+ @tailrec
+ private[this] def skipFixedBytes(n: Int, pos: Int): Int = {
+ val newPos = pos + n
+ if (newPos <= tail) newPos
+ else skipFixedBytes(n, loadMoreOrError(pos))
+ }
+
+ private[this] def loadMoreOrError(pos: Int): Int = {
+ if ((bbuf eq null) && (in eq null)) endOfInputError()
+ loadMore(pos, throwOnEndOfInput = true)
+ }
+
+ private[this] def loadMore(pos: Int): Int =
+ if ((bbuf eq null) && (in eq null)) pos
+ else loadMore(pos, throwOnEndOfInput = false)
+
+ private[this] def loadMore(pos: Int, throwOnEndOfInput: Boolean): Int = {
+ var newPos = pos
+ val offset =
+ if (mark < 0) pos
+ else mark
+ if (offset > 0) {
+ newPos -= offset
+ val buf = this.buf
+ val remaining = tail - offset
+ var i = 0
+ while (i < remaining) {
+ buf(i) = buf(i + offset)
+ i += 1
+ }
+ if (mark > 0) mark = 0
+ tail = remaining
+ head = newPos
+ } else growBuf()
+ var len = buf.length - tail
+ if (bbuf ne null) {
+ len = Math.min(bbuf.remaining, len)
+ bbuf.get(buf, tail, len)
+ } else len = Math.max(in.read(buf, tail, len), 0)
+ if (throwOnEndOfInput && len == 0) endOfInputError()
+ tail += len
+ totalRead += len
+ newPos
+ }
+
+ private[this] def growBuf(): Unit = {
+ var bufLen = buf.length
+ val maxBufSize = config.maxBufSize
+ if (bufLen == maxBufSize) tooLongInputError()
+ bufLen <<= 1
+ if (Integer.compareUnsigned(bufLen, maxBufSize) > 0) bufLen = maxBufSize
+ buf = java.util.Arrays.copyOf(buf, bufLen)
+ }
+
+ private[this] def tooLongInputError(): Nothing =
+ decodeError("too long part of input exceeded 'maxBufSize'", tail)
+
+ private[this] def tooLongStringError(): Nothing =
+ decodeError("too long string exceeded 'maxCharBufSize'", tail)
+
+ private[this] def endOfInputError(): Nothing =
+ decodeError("unexpected end of input", tail)
+
+ private[this] def reallocateBufToPreferredSize(): Unit = buf =
+ new Array[Byte](config.preferredBufSize)
+
+ private[this] def reallocateCharBufToPreferredSize(): Unit = charBuf =
+ new Array[Char](config.preferredCharBufSize)
+}
+
+object JsonReader {
+ private final val pow10Doubles: Array[Double] =
+ Array(1, 1e+1, 1e+2, 1e+3, 1e+4, 1e+5, 1e+6, 1e+7, 1e+8, 1e+9, 1e+10, 1e+11,
+ 1e+12, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19, 1e+20, 1e+21,
+ 1e+22)
+ /* Use the following code to generate `pow10Mantissas` in Scala REPL:
+ val ms = new Array[Long](653)
+ var pow10 = BigInt(10)
+ var i = 342
+ while (i >= 0) {
+ ms(i) = ((BigInt(1) << (pow10.bitLength + 63)) / pow10).longValue
+ pow10 *= 10
+ i -= 1
+ }
+ pow10 = BigInt(1) << 63
+ i = 343
+ while (i < 653) {
+ ms(i) = (pow10 >> (pow10.bitLength - 64)).longValue
+ pow10 *= 10
+ i += 1
+ }
+ ms.grouped(4).map(_.mkString("L, ")).mkString("Array(\n", "L,\n", "L\n)")
+ */
+ private final val pow10Mantissas: Array[Long] = Array(
+ -4671960508600951122L, -1228264617323800998L, -7685194413468457480L,
+ -4994806998408183946L, -1631822729582842029L, -7937418233630358124L,
+ -5310086773610559751L, -2025922448585811785L, -8183730558007214222L,
+ -5617977179081629873L, -2410785455424649437L, -8424269937281487754L,
+ -5918651403174471789L, -2786628235540701832L, -8659171674854020501L,
+ -6212278575140137722L, -3153662200497784248L, -8888567902952197011L,
+ -6499023860262858360L, -3512093806901185046L, -9112587656954322510L,
+ -6779048552765515233L, -3862124672529506138L, -215969822234494768L,
+ -7052510166537641086L, -4203951689744663454L, -643253593753441413L,
+ -7319562523736982739L, -4537767136243840520L, -1060522901877412746L,
+ -7580355841314464822L, -4863758783215693124L, -1468012460592228501L,
+ -7835036815511224669L, -5182110000961642932L, -1865951482774665761L,
+ -8083748704375247957L, -5492999862041672042L, -2254563809124702148L,
+ -8326631408344020699L, -5796603242002637969L, -2634068034075909558L,
+ -8563821548938525330L, -6093090917745768758L, -3004677628754823043L,
+ -8795452545612846258L, -6382629663588669919L, -3366601061058449494L,
+ -9021654690802612790L, -6665382345075878084L, -3720041912917459700L,
+ -38366372719436721L, -6941508010590729807L, -4065198994811024355L,
+ -469812725086392539L, -7211161980820077193L, -4402266457597708587L,
+ -891147053569747830L, -7474495936122174250L, -4731433901725329908L,
+ -1302606358729274481L, -7731658001846878407L, -5052886483881210105L,
+ -1704422086424124727L, -7982792831656159810L, -5366805021142811859L,
+ -2096820258001126919L, -8228041688891786181L, -5673366092687344822L,
+ -2480021597431793123L, -8467542526035952558L, -5972742139117552794L,
+ -2854241655469553088L, -8701430062309552536L, -6265101559459552766L,
+ -3219690930897053053L, -8929835859451740015L, -6550608805887287114L,
+ -3576574988931720989L, -9152888395723407474L, -6829424476226871438L,
+ -3925094576856201394L, -294682202642863838L, -7101705404292871755L,
+ -4265445736938701790L, -720121152745989333L, -7367604748107325189L,
+ -4597819916706768583L, -1135588877456072824L, -7627272076051127371L,
+ -4922404076636521310L, -1541319077368263733L, -7880853450996246689L,
+ -5239380795317920458L, -1937539975720012668L, -8128491512466089774L,
+ -5548928372155224313L, -2324474446766642487L, -8370325556870233411L,
+ -5851220927660403859L, -2702340141148116920L, -8606491615858654931L,
+ -6146428501395930760L, -3071349608317525546L, -8837122532839535322L,
+ -6434717147622031249L, -3431710416100151157L, -9062348037703676329L,
+ -6716249028702207507L, -3783625267450371480L, -117845565885576446L,
+ -6991182506319567135L, -4127292114472071014L, -547429124662700864L,
+ -7259672230555269896L, -4462904269766699466L, -966944318780986428L,
+ -7521869226879198374L, -4790650515171610063L, -1376627125537124675L,
+ -7777920981101784778L, -5110715207949843068L, -1776707991509915931L,
+ -8027971522334779313L, -5423278384491086237L, -2167411962186469893L,
+ -8272161504007625539L, -5728515861582144020L, -2548958808550292121L,
+ -8510628282985014432L, -6026599335303880135L, -2921563150702462265L,
+ -8743505996830120772L, -6317696477610263061L, -3285434578585440922L,
+ -8970925639256982432L, -6601971030643840136L, -3640777769877412266L,
+ -9193015133814464522L, -6879582898840692749L, -3987792605123478032L,
+ -373054737976959636L, -7150688238876681629L, -4326674280168464132L,
+ -796656831783192261L, -7415439547505577019L, -4657613415954583370L,
+ -1210330751515841308L, -7673985747338482674L, -4980796165745715438L,
+ -1614309188754756393L, -7926472270612804602L, -5296404319838617848L,
+ -2008819381370884406L, -8173041140997884610L, -5604615407819967859L,
+ -2394083241347571919L, -8413831053483314306L, -5905602798426754978L,
+ -2770317479606055818L, -8648977452394866743L, -6199535797066195524L,
+ -3137733727905356501L, -8878612607581929669L, -6486579741050024183L,
+ -3496538657885142324L, -9102865688819295809L, -6766896092596731857L,
+ -3846934097318526917L, -196981603220770742L, -7040642529654063570L,
+ -4189117143640191558L, -624710411122851544L, -7307973034592864071L,
+ -4523280274813692185L, -1042414325089727327L, -7569037980822161435L,
+ -4849611457600313890L, -1450328303573004458L, -7823984217374209643L,
+ -5168294253290374149L, -1848681798185579782L, -8072955151507069220L,
+ -5479507920956448621L, -2237698882768172872L, -8316090829371189901L,
+ -5783427518286599473L, -2617598379430861437L, -8553528014785370254L,
+ -6080224000054324913L, -2988593981640518238L, -8785400266166405755L,
+ -6370064314280619289L, -3350894374423386208L, -9011838011655698236L,
+ -6653111496142234891L, -3704703351750405709L, -19193171260619233L,
+ -6929524759678968877L, -4050219931171323192L, -451088895536766085L,
+ -7199459587351560659L, -4387638465762062920L, -872862063775190746L,
+ -7463067817500576073L, -4717148753448332187L, -1284749923383027329L,
+ -7720497729755473937L, -5038936143766954517L, -1686984161281305242L,
+ -7971894128441897632L, -5353181642124984136L, -2079791034228842266L,
+ -8217398424034108273L, -5660062011615247437L, -2463391496091671392L,
+ -8457148712698376476L, -5959749872445582691L, -2838001322129590460L,
+ -8691279853972075893L, -6252413799037706963L, -3203831230369745799L,
+ -8919923546622172981L, -6538218414850328322L, -3561087000135522498L,
+ -9143208402725783417L, -6817324484979841368L, -3909969587797413806L,
+ -275775966319379353L, -7089889006590693952L, -4250675239810979535L,
+ -701658031336336515L, -7356065297226292178L, -4583395603105477319L,
+ -1117558485454458744L, -7616003081050118571L, -4908317832885260310L,
+ -1523711272679187483L, -7869848573065574033L, -5225624697904579637L,
+ -1920344853953336643L, -8117744561361917258L, -5535494683275008668L,
+ -2307682335666372931L, -8359830487432564938L, -5838102090863318269L,
+ -2685941595151759932L, -8596242524610931813L, -6133617137336276863L,
+ -3055335403242958174L, -8827113654667930715L, -6422206049907525490L,
+ -3416071543957018958L, -9052573742614218705L, -6704031159840385477L,
+ -3768352931373093942L, -98755145788979524L, -6979250993759194058L,
+ -4112377723771604669L, -528786136287117932L, -7248020362820530564L,
+ -4448339435098275301L, -948738275445456222L, -7510490449794491995L,
+ -4776427043815727089L, -1358847786342270957L, -7766808894105001205L,
+ -5096825099203863602L, -1759345355577441598L, -8017119874876982855L,
+ -5409713825168840664L, -2150456263033662926L, -8261564192037121185L,
+ -5715269221619013577L, -2532400508596379068L, -8500279345513818773L,
+ -6013663163464885563L, -2905392935903719049L, -8733399612580906262L,
+ -6305063497298744923L, -3269643353196043250L, -8961056123388608887L,
+ -6589634135808373205L, -3625356651333078602L, -9183376934724255983L,
+ -6867535149977932074L, -3972732919045027189L, -354230130378896082L,
+ -7138922859127891907L, -4311967555482476980L, -778273425925708321L,
+ -7403949918844649557L, -4643251380128424042L, -1192378206733142148L,
+ -7662765406849295699L, -4966770740134231719L, -1596777406740401745L,
+ -7915514906853832947L, -5282707615139903279L, -1991698500497491195L,
+ -8162340590452013853L, -5591239719637629412L, -2377363631119648861L,
+ -8403381297090862394L, -5892540602936190089L, -2753989735242849707L,
+ -8638772612167862923L, -6186779746782440750L, -3121788665050663033L,
+ -8868646943297746252L, -6474122660694794911L, -3480967307441105734L,
+ -9093133594791772940L, -6754730975062328271L, -3831727700400522434L,
+ -177973607073265139L, -7028762532061872568L, -4174267146649952806L,
+ -606147914885053103L, -7296371474444240046L, -4508778324627912153L,
+ -1024286887357502287L, -7557708332239520786L, -4835449396872013078L,
+ -1432625727662628443L, -7812920107430224633L, -5154464115860392887L,
+ -1831394126398103205L, -8062150356639896359L, -5466001927372482545L,
+ -2220816390788215277L, -8305539271883716405L, -5770238071427257602L,
+ -2601111570856684098L, -8543223759426509417L, -6067343680855748868L,
+ -2972493582642298180L, -8775337516792518219L, -6357485877563259869L,
+ -3335171328526686933L, -9002011107970261189L, -6640827866535438582L,
+ -3689348814741910324L, -9223372036854775808L, -6917529027641081856L,
+ -4035225266123964416L, -432345564227567616L, -7187745005283311616L,
+ -4372995238176751616L, -854558029293551616L, -7451627795949551616L,
+ -4702848726509551616L, -1266874889709551616L, -7709325833709551616L,
+ -5024971273709551616L, -1669528073709551616L, -7960984073709551616L,
+ -5339544073709551616L, -2062744073709551616L, -8206744073709551616L,
+ -5646744073709551616L, -2446744073709551616L, -8446744073709551616L,
+ -5946744073709551616L, -2821744073709551616L, -8681119073709551616L,
+ -6239712823709551616L, -3187955011209551616L, -8910000909647051616L,
+ -6525815118631426616L, -3545582879861895366L, -9133518327554766460L,
+ -6805211891016070171L, -3894828845342699810L, -256850038250986858L,
+ -7078060301547948643L, -4235889358507547899L, -683175679707046970L,
+ -7344513827457986212L, -4568956265895094861L, -1099509313941480672L,
+ -7604722348854507276L, -4894216917640746191L, -1506085128623544835L,
+ -7858832233030797378L, -5211854272861108819L, -1903131822648998119L,
+ -8106986416796705681L, -5522047002568494197L, -2290872734783229842L,
+ -8349324486880600507L, -5824969590173362730L, -2669525969289315508L,
+ -8585982758446904049L, -6120792429631242157L, -3039304518611664792L,
+ -8817094351773372351L, -6409681921289327535L, -3400416383184271515L,
+ -9042789267131251553L, -6691800565486676537L, -3753064688430957767L,
+ -79644842111309304L, -6967307053960650171L, -4097447799023424810L,
+ -510123730351893109L, -7236356359111015049L, -4433759430461380907L,
+ -930513269649338230L, -7499099821171918250L, -4762188758037509908L,
+ -1341049929119499481L, -7755685233340769032L, -5082920523248573386L,
+ -1741964635633328828L, -8006256924911912374L, -5396135137712502563L,
+ -2133482903713240300L, -8250955842461857044L, -5702008784649933400L,
+ -2515824962385028846L, -8489919629131724885L, -6000713517987268202L,
+ -2889205879056697349L, -8723282702051517699L, -6292417359137009220L,
+ -3253835680493873621L, -8951176327949752869L, -6577284391509803182L,
+ -3609919470959866074L, -9173728696990998152L, -6855474852811359786L,
+ -3957657547586811828L, -335385916056126881L, -7127145225176161157L,
+ -4297245513042813542L, -759870872876129024L, -7392448323188662496L,
+ -4628874385558440216L, -1174406963520662366L, -7651533379841495835L,
+ -4952730706374481889L, -1579227364540714458L, -7904546130479028392L,
+ -5268996644671397586L, -1974559787411859078L, -8151628894773493780L,
+ -5577850100039479321L, -2360626606621961247L, -8392920656779807636L,
+ -5879464802547371641L, -2737644984756826647L, -8628557143114098510L,
+ -6174010410465235234L, -3105826994654156138L, -8858670899299929442L,
+ -6461652605697523899L, -3465379738694516970L, -9083391364325154962L,
+ -6742553186979055799L, -3816505465296431844L, -158945813193151901L,
+ -7016870160886801794L, -4159401682681114339L, -587566084924005019L,
+ -7284757830718584993L, -4494261269970843337L, -1006140569036166268L,
+ -7546366883288685774L, -4821272585683469313L, -1414904713676948737L,
+ -7801844473689174817L, -5140619573684080617L, -1814088448677712867L,
+ -8051334308064652398L, -5452481866653427593L, -2203916314889396588L,
+ -8294976724446954723L, -5757034887131305500L, -2584607590486743971L,
+ -8532908771695296838L, -6054449946191733143L, -2956376414312278525L,
+ -8765264286586255934L, -6344894339805432014L, -3319431906329402113L,
+ -8992173969096958177L, -6628531442943809817L, -3673978285252374367L,
+ -9213765455923815836L, -6905520801477381891L, -4020214983419339459L,
+ -413582710846786420L, -7176018221920323369L, -4358336758973016307L,
+ -836234930288882479L, -7440175859071633406L, -4688533805412153853L,
+ -1248981238337804412L, -7698142301602209614L, -5010991858575374113L,
+ -1652053804791829737L, -7950062655635975442L, -5325892301117581398L,
+ -2045679357969588844L, -8196078626372074883L, -5633412264537705700L,
+ -2430079312244744221L, -8436328597794046994L, -5933724728815170839L,
+ -2805469892591575644L, -8670947710510816634L, -6226998619711132888L,
+ -3172062256211528206L, -8900067937773286985L, -6513398903789220827L,
+ -3530062611309138130L, -9123818159709293187L, -6793086681209228580L,
+ -3879672333084147821L, -237904397927796872L, -7066219276345954901L,
+ -4221088077005055722L, -664674077828931749L, -7332950326284164199L,
+ -4554501889427817345L, -1081441343357383777L, -7593429867239446717L,
+ -4880101315621920492L, -1488440626100012711L, -7847804418953589800L,
+ -5198069505264599346L, -1885900863153361279L, -8096217067111932656L,
+ -5508585315462527915L, -2274045625900771990L, -8338807543829064350L,
+ -5811823411358942533L, -2653093245771290262L, -8575712306248138270L,
+ -6107954364382784934L, -3023256937051093263L, -8807064613298015146L,
+ -6397144748195131028L, -3384744916816525881L, -9032994600651410532L,
+ -6679557232386875260L, -3737760522056206171L, -60514634142869810L,
+ -6955350673980375487L, -4082502324048081455L, -491441886632713915L,
+ -7224680206786528053L, -4419164240055772162L, -912269281642327298L,
+ -7487697328667536418L, -4747935642407032618L, -1323233534581402868L,
+ -7744549986754458649L, -5069001465015685407L, -1724565812842218855L,
+ -7995382660667468640L, -5382542307406947896L, -2116491865831296966L,
+ -8240336443785642460L, -5688734536304665171L, -2499232151953443560L,
+ -8479549122611984081L, -5987750384837592197L, -2873001962619602342L,
+ -8713155254278333320L, -6279758049420528746L, -3238011543348273028L,
+ -8941286242233752499L, -6564921784364802720L, -3594466212028615495L,
+ -9164070410158966541L, -6843401994271320272L, -3942566474411762436L,
+ -316522074587315140L, -7115355324258153819L, -4282508136895304370L,
+ -741449152691742558L, -7380934748073420955L, -4614482416664388289L,
+ -1156417002403097458L, -7640289654143017767L, -4938676049251384305L,
+ -1561659043136842477L, -7893565929601608404L, -5255271393574622601L,
+ -1957403223540890347L, -8140906042354138323L, -5564446534515285000L,
+ -2343872149716718346L, -8382449121214030822L, -5866375383090150624L,
+ -2721283210435300376L, -8618331034163144591L, -6161227774276542835L,
+ -3089848699418290639L, -8848684464777513506L, -6449169562544503978L,
+ -3449775934753242068L, -9073638986861858149L, -6730362715149934782L,
+ -3801267375510030573L, -139898200960150313L, -7004965403241175802L,
+ -4144520735624081848L, -568964901102714406L, -7273132090830278360L,
+ -4479729095110460046L, -987975350460687153L, -7535013621679011327L,
+ -4807081008671376254L, -1397165242411832414L, -7790757304148477115L,
+ -5126760611758208489L, -1796764746270372707L, -8040506994060064798L,
+ -5438947724147693094L, -2186998636757228463L, -8284403175614349646L,
+ -5743817951090549153L, -2568086420435798537L, -8522583040413455942L,
+ -6041542782089432023L, -2940242459184402125L, -8755180564631333184L,
+ -6332289687361778576L, -3303676090774835316L, -8982326584375353929L,
+ -6616222212041804507L, -3658591746624867729L, -9204148869281624187L,
+ -6893500068174642330L, -4005189066790915008L, -394800315061255856L,
+ -7164279224554366766L, -4343663012265570553L, -817892746904575288L,
+ -7428711994456441411L, -4674203974643163860L, -1231068949876566920L,
+ -7686947121313936181L, -4996997883215032323L, -1634561335591402499L,
+ -7939129862385708418L, -5312226309554747619L, -2028596868516046619L,
+ -8185402070463610993L, -5620066569652125837L
+ )
+ /* Use the following code to generate `nibbles` in Scala REPL:
+ val ns = new Array[Byte](256)
+ java.util.Arrays.fill(ns, -1: Byte)
+ ns('0') = 0
+ ns('1') = 1
+ ns('2') = 2
+ ns('3') = 3
+ ns('4') = 4
+ ns('5') = 5
+ ns('6') = 6
+ ns('7') = 7
+ ns('8') = 8
+ ns('9') = 9
+ ns('A') = 10
+ ns('B') = 11
+ ns('C') = 12
+ ns('D') = 13
+ ns('E') = 14
+ ns('F') = 15
+ ns('a') = 10
+ ns('b') = 11
+ ns('c') = 12
+ ns('d') = 13
+ ns('e') = 14
+ ns('f') = 15
+ ns.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)")
+ */
+ private final val nibbles: Array[Byte] = Array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1,
+ -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+ )
+ /* Use the following code to generate `base64Bytes` in Scala REPL:
+ val bs = new Array[Byte](256)
+ java.util.Arrays.fill(bs, -1: Byte)
+ val ds = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
+ var i = 0
+ while (i < ds.length) {
+ bs(ds.charAt(i).toInt) = i.toByte
+ i += 1
+ }
+ bs.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)")
+ */
+ private final val base64Bytes: Array[Byte] = Array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, -1, -1, -1, -1, -1, -1, -1, 0, 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, -1, -1, -1, -1, -1, -1,
+ 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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+ )
+ /* Use the following code to generate `base64UrlBytes` in Scala REPL:
+ val bs = new Array[Byte](256)
+ java.util.Arrays.fill(bs, -1: Byte)
+ val ds = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
+ var i = 0
+ while (i < ds.length) {
+ bs(ds.charAt(i).toInt) = i.toByte
+ i += 1
+ }
+ bs.grouped(16).map(_.mkString(", ")).mkString("Array(\n", ",\n", "\n)")
+ */
+ private final val base64UrlBytes: Array[Byte] = Array(
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, 52, 53, 54, 55, 56, 57, 58, 59, 60,
+ 61, -1, -1, -1, -1, -1, -1, -1, 0, 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, -1, -1, -1, -1, 63, -1,
+ 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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+ )
+ private final val zoneOffsets: Array[ZoneOffset] = new Array(145)
+ private final val zoneIds: ConcurrentHashMap[Key, ZoneId] =
+ new ConcurrentHashMap(256)
+ private final val hexDigits: Array[Char] =
+ Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+ 'e', 'f')
+ /* Use the following code to generate `dumpBorder` in Scala REPL:
+ "\n+----------+-------------------------------------------------+------------------+".toCharArray
+ .grouped(16).map(_.map(_.toInt).mkString(", ")).mkString("Array(\n", ",\n", "\n)")
+ */
+ private final val dumpBorder: Array[Char] = Array(
+ 10, 43, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 43, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 43, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
+ 45, 45, 45, 45, 45, 43
+ )
+ /* Use the following code to generate `dumpHeader` in Scala REPL:
+ "\n| | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 0123456789abcdef |".toCharArray
+ .grouped(16).map(_.map(_.toInt).mkString(", ")).mkString("Array(\n", ",\n", "\n)")
+ */
+ private final val dumpHeader: Array[Char] = Array(
+ 10, 124, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 124, 32, 32, 48, 32, 32,
+ 49, 32, 32, 50, 32, 32, 51, 32, 32, 52, 32, 32, 53, 32, 32, 54, 32, 32, 55,
+ 32, 32, 56, 32, 32, 57, 32, 32, 97, 32, 32, 98, 32, 32, 99, 32, 32, 100, 32,
+ 32, 101, 32, 32, 102, 32, 124, 32, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+ 97, 98, 99, 100, 101, 102, 32, 124
+ )
+
+ /** The default math context used for rounding of `BigDecimal` values when
+ * parsing.
+ */
+ final val bigDecimalMathContext: MathContext = MathContext.DECIMAL128
+
+ /** The default limit for number of decimal digits in mantissa of parsed
+ * `BigDecimal` values.
+ */
+ final val bigDecimalDigitsLimit: Int = 308
+
+ /** The default limit for scale of parsed `BigDecimal` values.
+ */
+ final val bigDecimalScaleLimit: Int = 6178
+
+ /** The maximum number of digits in `BigInt` values.
+ */
+ final val bigIntDigitsLimit: Int = 308
+
+ /** Calculates hash code value string represented by sequence of characters
+ * from beginning of the provided char array up to limit position.
+ *
+ * @param cs
+ * a char array
+ * @param len
+ * an exclusive limit
+ * @return
+ * a hash code value
+ * @throws java.lang.NullPointerException
+ * if the `cs` is null
+ * @throws java.lang.ArrayIndexOutOfBoundsException
+ * if the length of `cs` is less than the provided `len`
+ */
+ final def toHashCode(cs: Array[Char], len: Int): Int = {
+ var h, i = 0
+ while (i < len) {
+ h = (h << 5) + (cs(i) - h)
+ i += 1
+ }
+ h
+ }
+}
+
+private class Key {
+ private[this] var hash: Int = _
+ private[this] var bs: Array[Byte] = _
+ private[this] var from: Int = _
+ private[this] var to: Int = _
+
+ def set(hash: Int, bs: Array[Byte], from: Int, to: Int): Unit = {
+ this.hash = hash
+ this.bs = bs
+ this.from = from
+ this.to = to
+ }
+
+ def copy: Key = {
+ val len = to - from
+ val bs1 = new Array[Byte](len)
+ System.arraycopy(bs, from, bs1, 0, len)
+ val k = new Key
+ k.set(hash, bs1, 0, len)
+ k
+ }
+
+ override def hashCode: Int = hash
+
+ override def equals(obj: Any): Boolean = {
+ val k = obj.asInstanceOf[Key]
+ val off = from
+ val koff = k.fromIndex
+ val len = to - off
+ k.toIndex - koff == len && {
+ val bs = this.bs
+ val kbs = k.bytes
+ var i = 0
+ while (i < len && kbs(koff + i) == bs(off + i)) i += 1
+ i == len
+ }
+ }
+
+ override def toString: String = new String(bs, 0, from, to - from)
+
+ private def bytes: Array[Byte] = bs
+
+ private def fromIndex: Int = from
+
+ private def toIndex: Int = to
+}
+*/
\ No newline at end of file
diff --git a/modules/core/src/main/scala/tethys/readers/tokens/JsonReaderConfig.scala b/modules/core/src/main/scala/tethys/readers/tokens/JsonReaderConfig.scala
new file mode 100644
index 00000000..447cfbe4
--- /dev/null
+++ b/modules/core/src/main/scala/tethys/readers/tokens/JsonReaderConfig.scala
@@ -0,0 +1,164 @@
+package tethys.readers.tokens
+
+
+/** Configuration for [[com.github.plokhotnyuk.jsoniter_scala.core.JsonReader]]
+ * that contains flags for tuning of parsing exceptions and preferred sizes for
+ * internal buffers that are created on the reader instantiation and reused in
+ * runtime for parsing of messages.
All configuration params already
+ * initialized by recommended default values, but in some cases they should be
+ * altered for performance reasons: