Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Encode to ByteBuffer in Circe #717

Merged
merged 1 commit into from Jan 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 27 additions & 3 deletions benchmarks/src/main/scala/io/finch/benchmarks.scala
@@ -1,16 +1,18 @@
package io.finch

import com.twitter.finagle.Service
import com.twitter.finagle.http.{Request, Response}
import com.twitter.io.Buf
import com.twitter.util.Future
import com.twitter.util.{Await, Future}
import io.finch.data.Foo
import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import shapeless._

@BenchmarkMode(Array(Mode.AverageTime))
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Warmup(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
@Fork(2)
abstract class FinchBenchmark {
val postPayload: Input = Input.post("/").withBody[Text.Plain](Buf.Utf8("x" * 1024))
Expand Down Expand Up @@ -130,3 +132,25 @@ class MapBenchmark extends FinchBenchmark {
@Benchmark
def mapOutputAsync: Option[Int] = mapTenOutputAsync(getRoot).awaitValueUnsafe()
}

@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.Throughput))
@OutputTimeUnit(TimeUnit.SECONDS)
class ToServiceBenchmark extends FinchBenchmark {
import io.circe.generic.auto._
import io.finch.circe._

val fooService: Service[Request, Response] = Endpoint.const(
List.fill(2048)(Foo(scala.util.Random.alphanumeric.take(100).mkString))
).toService

val intService: Service[Request, Response] = Endpoint.const(
List.fill(4096)(scala.util.Random.nextInt)
).toService

@Benchmark
def foos: Response = Await.result(fooService(Request()))

@Benchmark
def ints: Response = Await.result(intService(Request()))
}
3 changes: 1 addition & 2 deletions circe/src/main/scala/io/finch/circe/Decoders.scala
Expand Up @@ -14,8 +14,7 @@ trait Decoders {
*/
implicit def decodeCirce[A: Decoder]: Decode.Json[A] = Decode.json { (b, cs) =>
val attemptJson = cs match {
case StandardCharsets.UTF_8 =>
parseByteBuffer(b.asByteBuffer).right.flatMap(_.as[A])
case StandardCharsets.UTF_8 => decodeByteBuffer[A](b.asByteBuffer)
case _ => decode[A](BufText.extract(b, cs))
}

Expand Down
12 changes: 9 additions & 3 deletions circe/src/main/scala/io/finch/circe/Encoders.scala
@@ -1,18 +1,24 @@
package io.finch.circe

import com.twitter.io.Buf
import io.circe.{Encoder, Json}
import io.finch.Encode
import io.finch.internal.BufText
import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets

trait Encoders {

protected def print(json: Json): String
protected def printString(json: Json): String
protected def printBytes(json: Json): ByteBuffer

/**
* Maps Circe's [[Encoder]] to Finch's [[Encode]].
*/
implicit def encodeCirce[A](implicit e: Encoder[A]): Encode.Json[A] =
Encode.json((a, cs) => BufText(print(e(a)), cs))
implicit def encodeCirce[A](implicit e: Encoder[A]): Encode.Json[A] = Encode.json {
case (a, StandardCharsets.UTF_8) => Buf.ByteBuffer.Owned(printBytes(e(a)))
case (a, cs) => BufText(printString(e(a)), cs)
}

implicit def encodeExceptionCirce[A <: Exception]: Encoder[A] = new Encoder[A] {
override def apply(e: A): Json =
Expand Down
15 changes: 10 additions & 5 deletions circe/src/main/scala/io/finch/circe/package.scala
@@ -1,25 +1,30 @@
package io.finch

import io.circe.{Json, Printer}
import io.circe.jackson._
import java.nio.ByteBuffer

package object circe extends Encoders with Decoders {

override protected def print(json: Json): String = Printer.noSpaces.pretty(json)
override protected def printString(json: Json): String = Printer.noSpaces.pretty(json)
override protected def printBytes(json: Json): ByteBuffer = Printer.noSpaces.prettyByteBuffer(json)

/**
* Provides a [[Printer]] that drops null keys.
*/
object dropNullKeys extends Encoders with Decoders {
private val printer: Printer = Printer.noSpaces.copy(dropNullKeys = true)
override protected def print(json: Json): String = printer.pretty(json)
private[this] val printer: Printer = Printer.noSpaces.copy(dropNullKeys = true)
override protected def printString(json: Json): String = printer.pretty(json)
override protected def printBytes(json: Json): ByteBuffer = printer.prettyByteBuffer(json)
}

/**
* Provides Jackson Serializer.
*/
object jacksonSerializer extends Encoders with Decoders {
override protected def print(json: Json): String = jacksonPrint(json)
override protected def printString(json: Json): String =
io.circe.jackson.jacksonPrint(json)
override protected def printBytes(json: Json): ByteBuffer =
io.circe.jackson.jacksonPrintByteBuffer(json)
}
}