Skip to content

Commit

Permalink
Fix HttpContent converters (Netty 4)
Browse files Browse the repository at this point in the history
  • Loading branch information
vkostyukov committed Feb 18, 2017
1 parent 04975db commit ba98564
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 20 deletions.
24 changes: 12 additions & 12 deletions core/src/main/scala/io/finch/internal/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,34 +104,34 @@ package object internal {

implicit class HttpContent(val self: Buf) extends AnyVal {
// Returns content as ByteArray (tries to avoid copying).
def asByteArrayWithOffsetAndLength: (Array[Byte], Int, Int) = {
def asByteArrayWithBeginAndEnd: (Array[Byte], Int, Int) = {
// Finagle guarantees to have the payload on heap when it enters the
// user land. With a cost of a tuple allocation we're making this agnostic
// to the underlying Netty version.
val Buf.ByteArray.Owned(array, offset, length) = Buf.ByteArray.coerce(self)
(array, offset, length)
val Buf.ByteArray.Owned(array, begin, end) = Buf.ByteArray.coerce(self)
(array, begin, end)
}

// Returns content as ByteBuffer (tries to avoid copying).
def asByteBuffer: ByteBuffer = {
val (array, offset, length) = asByteArrayWithOffsetAndLength
ByteBuffer.wrap(array, offset, length)
val (array, begin, end) = asByteArrayWithBeginAndEnd
ByteBuffer.wrap(array, begin, end - begin)
}

// Returns content as ByteArray (tries to avoid copying).
def asByteArray: Array[Byte] = asByteArrayWithOffsetAndLength match {
case (array, offset, length) if offset == 0 && length == array.length => array
case (array, offset, length) =>
val result = new Array[Byte](length)
System.arraycopy(array, offset, result, 0, length)
def asByteArray: Array[Byte] = asByteArrayWithBeginAndEnd match {
case (array, begin, end) if begin == 0 && end == array.length => array
case (array, begin, end) =>
val result = new Array[Byte](end - begin)
System.arraycopy(array, begin, result, 0, end - begin)

result
}

// Returns content as String (tries to avoid copying).
def asString(cs: Charset): String = {
val (array, offset, length) = asByteArrayWithOffsetAndLength
new String(array, offset, length, cs.name)
val (array, begin, end) = asByteArrayWithBeginAndEnd
new String(array, begin, end - begin, cs.name)
}
}
}
11 changes: 5 additions & 6 deletions core/src/test/scala/io/finch/FinchSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,11 @@ trait FinchSpec extends FlatSpec with Matchers with Checkers with AllInstances
} yield Path("/" + ss.mkString("/"))

def genBuf: Gen[Buf] = for {
s <- Arbitrary.arbitrary[String]
b <- Gen.oneOf(
Buf.Utf8(s),
Buf.ByteArray.Owned(s.getBytes("UTF-8"))
)
} yield b
size <- Gen.choose(1, 100)
bytes <- Gen.listOfN(size, Arbitrary.arbByte.arbitrary)
begin <- Gen.choose(0, size)
end <- Gen.choose(begin, size)
} yield Buf.ByteArray.Owned(bytes.toArray, begin, end)

implicit def arbitraryRequest: Arbitrary[Request] = Arbitrary(
for {
Expand Down
32 changes: 32 additions & 0 deletions core/src/test/scala/io/finch/internal/HttpContentSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.finch.internal

import com.twitter.io.Buf
import io.finch.FinchSpec
import java.nio.charset.Charset

class HttpContentSpec extends FinchSpec {
"HttContent" should "asByteArrayWithBeginAndEnd" in {
check { b: Buf =>
val (array, begin, end) = b.asByteArrayWithBeginAndEnd
Buf.ByteArray.Owned.extract(b) === array.slice(begin, end)
}
}

it should "asByteBuffer" in {
check { b: Buf =>
b.asByteBuffer === Buf.ByteBuffer.Owned.extract(b)
}
}

it should "asByteArray" in {
check { b: Buf =>
b.asByteArray === Buf.ByteArray.Owned.extract(b)
}
}

it should "asString" in {
check { (b: Buf, cs: Charset) =>
b.asString(cs) === new String(Buf.ByteArray.Owned.extract(b), cs)
}
}
}
4 changes: 2 additions & 2 deletions jackson/src/main/scala/io/finch/jackson/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ package object jackson {

implicit def decodeJackson[A](implicit m: Manifest[A]): Decode.Json[A] = Decode.json {
case (buf, StandardCharsets.UTF_8 | StandardCharsets.UTF_16 | Utf32) =>
val (array, offset, length) = buf.asByteArrayWithOffsetAndLength
Try(objectMapper.readValue[A](array, offset, length))
val (array, begin, end) = buf.asByteArrayWithBeginAndEnd
Try(objectMapper.readValue[A](array, begin, end - begin))
case (buf, cs) =>
Try(objectMapper.readValue[A](buf.asString(cs)))
}
Expand Down

0 comments on commit ba98564

Please sign in to comment.