Remove String conversion in decoders. #671

Merged
merged 1 commit into from Nov 21, 2016

Conversation

Projects
None yet
4 participants
@rpless
Contributor

rpless commented Nov 19, 2016

Proposed fix for #511. The only thing I was unsure about was whether to use Buf.ByteArray.Owned or Buf.ByteArray.Shared for the libraries that can convert Array[Byte]. I wound up using Shared, but I'm not sure this is necessary.

@codecov-io

This comment has been minimized.

Show comment
Hide comment
@codecov-io

codecov-io Nov 19, 2016

Current coverage is 74.34% (diff: 76.00%)

Merging #671 into master will decrease coverage by 0.32%

@@             master       #671   diff @@
==========================================
  Files            30         30          
  Lines           592        608    +16   
  Methods         566        586    +20   
  Messages          0          0          
  Branches         26         22     -4   
==========================================
+ Hits            442        452    +10   
- Misses          150        156     +6   
  Partials          0          0          

Powered by Codecov. Last update 2d479db...a5335d1

codecov-io commented Nov 19, 2016

Current coverage is 74.34% (diff: 76.00%)

Merging #671 into master will decrease coverage by 0.32%

@@             master       #671   diff @@
==========================================
  Files            30         30          
  Lines           592        608    +16   
  Methods         566        586    +20   
  Messages          0          0          
  Branches         26         22     -4   
==========================================
+ Hits            442        452    +10   
- Misses          150        156     +6   
  Partials          0          0          

Powered by Codecov. Last update 2d479db...a5335d1

@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 19, 2016

Member

@rpless Thanks a lot for looking into this!

Some suggestions:

  • Instead of Buf.ByteArray.Shared (which always copy), let's use this trick that allows retrieving a byte array from Netty's channel buffer. This heavily depends on the underlying implementation, but I'm fine with that for now.
  • Let run some benchmarks (both JMH and wrk) to see how this affects throughput (I'm excited to see the numbers!)
Member

vkostyukov commented Nov 19, 2016

@rpless Thanks a lot for looking into this!

Some suggestions:

  • Instead of Buf.ByteArray.Shared (which always copy), let's use this trick that allows retrieving a byte array from Netty's channel buffer. This heavily depends on the underlying implementation, but I'm fine with that for now.
  • Let run some benchmarks (both JMH and wrk) to see how this affects throughput (I'm excited to see the numbers!)
@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 19, 2016

Member

Trying to be more specific here.

If you need ByteBuffer, the cheapest way (at least for Netty 3) to get it is:

val byteBuffer = ChannelBufferBuf.Owned.extract(buf).toByteBuffer()

If you need Array[Byte], the cheapest way to get it is:

val channelBuffer = ChannelBufferBuf.Owned.extract(buf)
val (byteArray, offset, length) = 
  // assert channelBuffer.hasArray()
  (channelBuffer.array(), channelBuffer.readerIndex(), channelBuffer.readableBytes()) 
Member

vkostyukov commented Nov 19, 2016

Trying to be more specific here.

If you need ByteBuffer, the cheapest way (at least for Netty 3) to get it is:

val byteBuffer = ChannelBufferBuf.Owned.extract(buf).toByteBuffer()

If you need Array[Byte], the cheapest way to get it is:

val channelBuffer = ChannelBufferBuf.Owned.extract(buf)
val (byteArray, offset, length) = 
  // assert channelBuffer.hasArray()
  (channelBuffer.array(), channelBuffer.readerIndex(), channelBuffer.readableBytes()) 
@rpless

This comment has been minimized.

Show comment
Hide comment
@rpless

rpless Nov 19, 2016

Contributor

I ran the wrk benchmarks for Finch + circe. The results are here.
As for jmh benchmarks, unless I missed something, it doesn't look like we have ones that exercise the the json decoders anymore.

Contributor

rpless commented Nov 19, 2016

I ran the wrk benchmarks for Finch + circe. The results are here.
As for jmh benchmarks, unless I missed something, it doesn't look like we have ones that exercise the the json decoders anymore.

@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 19, 2016

Member

Nice!

@rpless Do you mind keep the server running and run wrk for 3 times. Usually, it takes some time for JVM to warmup so using the results from 3rd run seems like the closest we can get to the steady state.

Member

vkostyukov commented Nov 19, 2016

Nice!

@rpless Do you mind keep the server running and run wrk for 3 times. Usually, it takes some time for JVM to warmup so using the results from 3rd run seems like the closest we can get to the steady state.

@vkostyukov

Just a few nits from me. Really nice job!

@@ -1,6 +1,8 @@
package io.finch.argonaut
import argonaut.{CursorHistory, DecodeJson, Json}
+import com.twitter.finagle.netty3.ChannelBufferBuf
+import com.twitter.io.Charsets

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Please, use StandardCharsets from JDK. c.t.i.Charsets are deprecated in the most recent Finagle release.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Please, use StandardCharsets from JDK. c.t.i.Charsets are deprecated in the most recent Finagle release.

- )
+ implicit def decodeCirce[A: Decoder]: Decode.Json[A] = Decode.json({ (b, cs) =>
+ val attemptJson = cs match {
+ case Charsets.Utf8 => parseByteBuffer(ChannelBufferBuf.Owned.extract(b).toByteBuffer()).right.flatMap(_.as[A])

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Is scalacheck happy with that line length?

@vkostyukov

vkostyukov Nov 19, 2016

Member

Is scalacheck happy with that line length?

This comment has been minimized.

@rpless

rpless Nov 19, 2016

Contributor

Yes, its under the line limit by 3 character. Happy to move it down a line if you think that's more readable.

@rpless

rpless Nov 19, 2016

Contributor

Yes, its under the line limit by 3 character. Happy to move it down a line if you think that's more readable.

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Nah, it's fine. Thanks!

@vkostyukov

vkostyukov Nov 19, 2016

Member

Nah, it's fine. Thanks!

@rpless

This comment has been minimized.

Show comment
Hide comment
@rpless

rpless Nov 19, 2016

Contributor

Updated benckmarks, results after 3 runs:

Without String Conversion (this PR)

$ wrk -t4 -c24 -d30s -s examples/src/main/scala/io/finch/wrk/wrk.lua http://localhost:8081/
Running 30s test @ http://localhost:8081/
  4 threads and 24 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.68ms    3.67ms  52.43ms   90.18%
    Req/Sec    12.20k     2.26k   23.58k    67.25%
  1458406 requests in 30.09s, 137.69MB read
Requests/sec:  48471.67
Transfer/sec:      4.58MB

With String Conversion (Current Finch)

$ wrk -t4 -c24 -d30s -s examples/src/main/scala/io/finch/wrk/wrk.lua http://localhost:8081/
Running 30s test @ http://localhost:8081/
  4 threads and 24 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.62ms    3.22ms  62.64ms   89.91%
    Req/Sec    10.84k     2.44k   22.20k    68.33%
  1296147 requests in 30.10s, 122.37MB read
Requests/sec:  43067.06
Transfer/sec:      4.07MB
Contributor

rpless commented Nov 19, 2016

Updated benckmarks, results after 3 runs:

Without String Conversion (this PR)

$ wrk -t4 -c24 -d30s -s examples/src/main/scala/io/finch/wrk/wrk.lua http://localhost:8081/
Running 30s test @ http://localhost:8081/
  4 threads and 24 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.68ms    3.67ms  52.43ms   90.18%
    Req/Sec    12.20k     2.26k   23.58k    67.25%
  1458406 requests in 30.09s, 137.69MB read
Requests/sec:  48471.67
Transfer/sec:      4.58MB

With String Conversion (Current Finch)

$ wrk -t4 -c24 -d30s -s examples/src/main/scala/io/finch/wrk/wrk.lua http://localhost:8081/
Running 30s test @ http://localhost:8081/
  4 threads and 24 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.62ms    3.22ms  62.64ms   89.91%
    Req/Sec    10.84k     2.44k   22.20k    68.33%
  1296147 requests in 30.10s, 122.37MB read
Requests/sec:  43067.06
Transfer/sec:      4.07MB
@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 19, 2016

Member

Looks like 12.5% improvement in the throughput! Sweet!

Member

vkostyukov commented Nov 19, 2016

Looks like 12.5% improvement in the throughput! Sweet!

+ val buf = ChannelBufferBuf.Owned.extract(b)
+ if (buf.hasArray) Try(Json.parse(new String(buf.array(), 0, buf.readableBytes(), cs)).as[A])
+ else Try(Json.parse(buf.toString(cs)).as[A])
+ })

This comment has been minimized.

@clhodapp

clhodapp Nov 19, 2016

Contributor

It looks like you might be parsing twice here by accident?

@clhodapp

clhodapp Nov 19, 2016

Contributor

It looks like you might be parsing twice here by accident?

This comment has been minimized.

@rpless

rpless Nov 19, 2016

Contributor

No accident. Its based on this trick that @vkostyukov mentioned in earlier. There may be a better to express this (i.e. only having on call to Json.parse once and having the if/ else only return a Try[String]).

@rpless

rpless Nov 19, 2016

Contributor

No accident. Its based on this trick that @vkostyukov mentioned in earlier. There may be a better to express this (i.e. only having on call to Json.parse once and having the if/ else only return a Try[String]).

This comment has been minimized.

@rpless

rpless Nov 19, 2016

Contributor

@clhodapp I played around with it a little. Do you think this version makes the intent more clear?

val buf = ChannelBufferBuf.Owned.extract(b)
val bufAsString = Try(
  if (buf.hasArray) new String(buf.array(), 0, buf.readableBytes(), cs)
  else buf.toString(cs)
)
bufAsString.map(Json.parse(_).as[A])
@rpless

rpless Nov 19, 2016

Contributor

@clhodapp I played around with it a little. Do you think this version makes the intent more clear?

val buf = ChannelBufferBuf.Owned.extract(b)
val bufAsString = Try(
  if (buf.hasArray) new String(buf.array(), 0, buf.readableBytes(), cs)
  else buf.toString(cs)
)
bufAsString.map(Json.parse(_).as[A])
@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 19, 2016

Member

I think Chris is right. We call Json.parse two times and drop the first
result on the floor.

On Sat, Nov 19, 2016 at 11:14 AM Ryan Plessner notifications@github.com
wrote:

@rpless commented on this pull request.

In playjson/src/main/scala/io/finch/playjson/package.scala
#671:

@@ -11,10 +13,11 @@ package object playjson {
* @tparam A the type of the data to decode into
*/
implicit def decodePlayJson[A](implicit reads: Reads[A]): Decode.Json[A] =

  • // TODO: Eliminate toString conversion
  • // See #511
  • // PlayJson can parse from byte[] automatically detecting the charset.
  • Decode.json((b, cs) => Try(Json.parse(BufText.extract(b, cs)).as[A]))
  • Decode.json({ (b, cs) => Try(Json.parse(Buf.ByteArray.Shared.extract(b)).as[A])
  •  val buf = ChannelBufferBuf.Owned.extract(b)
    
  •  if (buf.hasArray) Try(Json.parse(new String(buf.array(), 0, buf.readableBytes(), cs)).as[A])
    
  •  else Try(Json.parse(buf.toString(cs)).as[A])
    
  • })

@clhodapp https://github.com/clhodapp I played around with it a little.
Do you think this version makes the intent more clear?

val buf = ChannelBufferBuf.Owned.extract(b)val bufAsString = Try(
if (buf.hasArray) new String(buf.array(), 0, buf.readableBytes(), cs)
else buf.toString(cs)
)
bufAsString.map(Json.parse(_).as[A])


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#671, or mute the thread
https://github.com/notifications/unsubscribe-auth/ABDDKwEmt9Qm3tSF-W2xoHHcsjNTIvoyks5q_0p5gaJpZM4K3Isa
.

Member

vkostyukov commented Nov 19, 2016

I think Chris is right. We call Json.parse two times and drop the first
result on the floor.

On Sat, Nov 19, 2016 at 11:14 AM Ryan Plessner notifications@github.com
wrote:

@rpless commented on this pull request.

In playjson/src/main/scala/io/finch/playjson/package.scala
#671:

@@ -11,10 +13,11 @@ package object playjson {
* @tparam A the type of the data to decode into
*/
implicit def decodePlayJson[A](implicit reads: Reads[A]): Decode.Json[A] =

  • // TODO: Eliminate toString conversion
  • // See #511
  • // PlayJson can parse from byte[] automatically detecting the charset.
  • Decode.json((b, cs) => Try(Json.parse(BufText.extract(b, cs)).as[A]))
  • Decode.json({ (b, cs) => Try(Json.parse(Buf.ByteArray.Shared.extract(b)).as[A])
  •  val buf = ChannelBufferBuf.Owned.extract(b)
    
  •  if (buf.hasArray) Try(Json.parse(new String(buf.array(), 0, buf.readableBytes(), cs)).as[A])
    
  •  else Try(Json.parse(buf.toString(cs)).as[A])
    
  • })

@clhodapp https://github.com/clhodapp I played around with it a little.
Do you think this version makes the intent more clear?

val buf = ChannelBufferBuf.Owned.extract(b)val bufAsString = Try(
if (buf.hasArray) new String(buf.array(), 0, buf.readableBytes(), cs)
else buf.toString(cs)
)
bufAsString.map(Json.parse(_).as[A])


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#671, or mute the thread
https://github.com/notifications/unsubscribe-auth/ABDDKwEmt9Qm3tSF-W2xoHHcsjNTIvoyks5q_0p5gaJpZM4K3Isa
.

@rpless

This comment has been minimized.

Show comment
Hide comment
@rpless

rpless Nov 19, 2016

Contributor

Ah I see it now. The line above is the old code. I'll remove it.

Contributor

rpless commented Nov 19, 2016

Ah I see it now. The line above is the old code. I'll remove it.

- Decode.json((b, cs) => Try(Json.parse(BufText.extract(b, cs)).as[A]))
+ Decode.json({ (b, cs) =>
+ val buf = ChannelBufferBuf.Owned.extract(b)
+ if (buf.hasArray) Try(Json.parse(new String(buf.array(), 0, buf.readableBytes(), cs)).as[A])

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

I think, Json.parse can parse byte[] directly. No need for new String.

@vkostyukov

vkostyukov Nov 19, 2016

Member

I think, Json.parse can parse byte[] directly. No need for new String.

This comment has been minimized.

@rpless

rpless Nov 19, 2016

Contributor

Json.Parse can take a byte[], but when I convert that line to Try(Json.parse(buf.array()).as[A]) it gives me the following in the

Throw(spray.json.JsonParser$ParsingException: Unexpected character '\u0000'

I think I'm missing something about how the ChannelBufferBuf holds onto the underlying array.

@rpless

rpless Nov 19, 2016

Contributor

Json.Parse can take a byte[], but when I convert that line to Try(Json.parse(buf.array()).as[A]) it gives me the following in the

Throw(spray.json.JsonParser$ParsingException: Unexpected character '\u0000'

I think I'm missing something about how the ChannelBufferBuf holds onto the underlying array.

+ cs match {
+ case StandardCharsets.UTF_8 =>
+ val buf = ChannelBufferBuf.Owned.extract(b)
+ if (buf.hasArray) Try(JsonParser(new String(buf.array(), 0, buf.readableBytes(), cs)).convertTo[A])

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

No need for new String.

@vkostyukov

vkostyukov Nov 19, 2016

Member

No need for new String.

This comment has been minimized.

@rpless

rpless Nov 19, 2016

Contributor

A similar issue exists here. The tests raise this error.

Throw(com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 0)): only regular white space (\r, \n, \t) is allowed between tokens
@rpless

rpless Nov 19, 2016

Contributor

A similar issue exists here. The tests raise this error.

Throw(com.fasterxml.jackson.core.JsonParseException: Illegal character ((CTRL-CHAR, code 0)): only regular white space (\r, \n, \t) is allowed between tokens
- value => Return(value)
- )
- )
+ implicit def decodeCirce[A: Decoder]: Decode.Json[A] = Decode.json({ (b, cs) =>

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Let's remove wrapping (). Just Decode.json { ... } is fine.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Let's remove wrapping (). Just Decode.json { ... } is fine.

- // Jackson can parse from byte[] automatically detecting the encoding.
- Try(mapper.readValue(BufText.extract(b, cs), ct.runtimeClass.asInstanceOf[Class[A]]))
- )
+ ): Decode.Json[A] = Decode.json({ (b, cs) =>

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Please, remove (): Decode.json { ... }.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Please, remove (): Decode.json { ... }.

- // See https://github.com/finagle/finch/issues/511
- // PlayJson can parse from byte[] automatically detecting the charset.
- Decode.json((b, cs) => Try(Json.parse(BufText.extract(b, cs)).as[A]))
+ Decode.json({ (b, cs) =>

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Please, remove wrapping (): Decode.json { ... }.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Please, remove wrapping (): Decode.json { ... }.

- // See https://github.com/finagle/finch/issues/511
- // SprayJson can parse from byte[] if it represents a UTF-8 string.
- Decode.json((b, cs) => Try(BufText.extract(b, cs).parseJson.convertTo[A]))
+ Decode.json({ (b, cs) =>

This comment has been minimized.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Please, remove wrapping (): Decode.json { ... }.

@vkostyukov

vkostyukov Nov 19, 2016

Member

Please, remove wrapping (): Decode.json { ... }.

@rpless

This comment has been minimized.

Show comment
Hide comment
@rpless

rpless Nov 19, 2016

Contributor

Ok, it looks like using slice like this works: buf.array().slice(0, buf.readableBytes()). Does that make sense?

Contributor

rpless commented Nov 19, 2016

Ok, it looks like using slice like this works: buf.array().slice(0, buf.readableBytes()). Does that make sense?

@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 19, 2016

Member

Slice works but it allocates a new array. You should be able to bypass
readableBytes to the parse function.

On Sat, Nov 19, 2016 at 12:48 PM Ryan Plessner notifications@github.com
wrote:

Ok, it looks like using slice like this works: buf.array().slice(0,
buf.readableBytes()). Does that make sense?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#671 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABDDK-0w2n0qAoIwWMcWBZ8h16aUD1ooks5q_2CrgaJpZM4K3Isa
.

Member

vkostyukov commented Nov 19, 2016

Slice works but it allocates a new array. You should be able to bypass
readableBytes to the parse function.

On Sat, Nov 19, 2016 at 12:48 PM Ryan Plessner notifications@github.com
wrote:

Ok, it looks like using slice like this works: buf.array().slice(0,
buf.readableBytes()). Does that make sense?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#671 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABDDK-0w2n0qAoIwWMcWBZ8h16aUD1ooks5q_2CrgaJpZM4K3Isa
.

@rpless

This comment has been minimized.

Show comment
Hide comment
Contributor

rpless commented Nov 19, 2016

@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 19, 2016

Member

Play takes InputStream though. Can we wrap our byte array with
ByteArrayInputStream and specify the length there?

For Spray, let's do System.arraycopy it's still better than 'new String'.

On Sat, Nov 19, 2016 at 1:10 PM Ryan Plessner notifications@github.com
wrote:

Yeah, I'm not a fan of the new array allocation, but it seems like neither
Spray Json nor Play Json expose a parsing function that takes the number of
bytes to read:

https://www.playframework.com/documentation/2.5.x/api/scala/index.html#play.api.libs.json.Json$

https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/JsonParser.scala#L28
(and also
https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/JsonParser.scala#L237
)


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#671 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABDDK_tX58WsqNgUMHNRIP01LhdK2MDxks5q_2WagaJpZM4K3Isa
.

Member

vkostyukov commented Nov 19, 2016

Play takes InputStream though. Can we wrap our byte array with
ByteArrayInputStream and specify the length there?

For Spray, let's do System.arraycopy it's still better than 'new String'.

On Sat, Nov 19, 2016 at 1:10 PM Ryan Plessner notifications@github.com
wrote:

Yeah, I'm not a fan of the new array allocation, but it seems like neither
Spray Json nor Play Json expose a parsing function that takes the number of
bytes to read:

https://www.playframework.com/documentation/2.5.x/api/scala/index.html#play.api.libs.json.Json$

https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/JsonParser.scala#L28
(and also
https://github.com/spray/spray-json/blob/master/src/main/scala/spray/json/JsonParser.scala#L237
)


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#671 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABDDK_tX58WsqNgUMHNRIP01LhdK2MDxks5q_2WagaJpZM4K3Isa
.

@rpless

This comment has been minimized.

Show comment
Hide comment
@rpless

rpless Nov 19, 2016

Contributor

Can do for Spray, but it looks like I gave you the wrong docs for Play Json. We're currently on 2.3.x which does not provide a parse with InputStream. So we can either bump the Play version or System.arraycopy.

Contributor

rpless commented Nov 19, 2016

Can do for Spray, but it looks like I gave you the wrong docs for Play Json. We're currently on 2.3.x which does not provide a parse with InputStream. So we can either bump the Play version or System.arraycopy.

@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 19, 2016

Member

Arraycopy works for me. Let's update it later.

On Sat, Nov 19, 2016 at 1:50 PM Ryan Plessner notifications@github.com
wrote:

Can do for Spray, but it looks like I gave you the wrong docs for Play
Json. We're currently on 2.3.x
https://www.playframework.com/documentation/2.3.x/api/scala/index.html#play.api.libs.json.Json%24
which does not provide a parse with InputStream. So we can either bump the
Play version or System.arraycopy.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#671 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABDDK22ipWPepiBj6q_IqnkB5M6wpEX7ks5q_28_gaJpZM4K3Isa
.

Member

vkostyukov commented Nov 19, 2016

Arraycopy works for me. Let's update it later.

On Sat, Nov 19, 2016 at 1:50 PM Ryan Plessner notifications@github.com
wrote:

Can do for Spray, but it looks like I gave you the wrong docs for Play
Json. We're currently on 2.3.x
https://www.playframework.com/documentation/2.3.x/api/scala/index.html#play.api.libs.json.Json%24
which does not provide a parse with InputStream. So we can either bump the
Play version or System.arraycopy.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#671 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/ABDDK22ipWPepiBj6q_IqnkB5M6wpEX7ks5q_28_gaJpZM4K3Isa
.

- Decode.json((b, cs) => Try(Json.parse(BufText.extract(b, cs)).as[A]))
+ Decode.json { (b, cs) =>
+ val buf = ChannelBufferBuf.Owned.extract(b)
+ if (buf.hasArray) {

This comment has been minimized.

@vkostyukov

vkostyukov Nov 20, 2016

Member

Sorry to be a burden, but can we extract that into a function so we can reuse it?

Also, let's check that if array.length equals readableBytes we can skip copying.

@vkostyukov

vkostyukov Nov 20, 2016

Member

Sorry to be a burden, but can we extract that into a function so we can reuse it?

Also, let's check that if array.length equals readableBytes we can skip copying.

This comment has been minimized.

@rpless

rpless Nov 20, 2016

Contributor

Its not a burden. Any particular place we should extract it to? I was thinking io.finch.Decode.

@rpless

rpless Nov 20, 2016

Contributor

Its not a burden. Any particular place we should extract it to? I was thinking io.finch.Decode.

This comment has been minimized.

@rpless

rpless Nov 20, 2016

Contributor

Or maybe the internal package object?

@rpless

rpless Nov 20, 2016

Contributor

Or maybe the internal package object?

This comment has been minimized.

@vkostyukov

vkostyukov Nov 20, 2016

Member

Yeah, let's make it part of internal (whatever format you prefer).

@vkostyukov

vkostyukov Nov 20, 2016

Member

Yeah, let's make it part of internal (whatever format you prefer).

@vkostyukov

This comment has been minimized.

Show comment
Hide comment
@vkostyukov

vkostyukov Nov 21, 2016

Member

Thanks again @rpless! Merging this.

Member

vkostyukov commented Nov 21, 2016

Thanks again @rpless! Merging this.

@vkostyukov vkostyukov merged commit 80f0523 into finagle:master Nov 21, 2016

3 checks passed

codecov/patch 76.00% of diff hit (target 74.66%)
Details
codecov/project Absolute coverage decreased by -0.32% but relative coverage increased by +1.33% compared to 2d479db
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details

@rpless rpless deleted the rpless:remove-json-string-conversions branch Nov 21, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment