Skip to content

Commit b80c126

Browse files
committed
Close #81 add specialization for codecs to minimize boxing of primitives
1 parent a7b8c30 commit b80c126

File tree

7 files changed

+104
-109
lines changed

7 files changed

+104
-109
lines changed

core/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonCodec.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
11
package com.github.plokhotnyuk.jsoniter_scala.core
22

3-
trait JsonCodec[A] extends JsonValueCodec[A] with JsonKeyCodec[A]
3+
import scala.{specialized => sp}
44

5-
trait JsonValueCodec[A] {
5+
trait JsonCodec[@sp A] extends JsonValueCodec[A] with JsonKeyCodec[A]
6+
7+
trait JsonValueCodec[@sp A] {
68
def decodeValue(in: JsonReader, default: A): A
79

810
def encodeValue(x: A, out: JsonWriter): Unit
911

1012
def nullValue: A
1113
}
1214

13-
trait JsonKeyCodec[A] {
15+
trait JsonKeyCodec[@sp A] {
1416
def decodeKey(in: JsonReader): A
1517

1618
def encodeKey(x: A, out: JsonWriter): Unit

core/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonReader.scala

Lines changed: 43 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import java.util.UUID
99
import com.github.plokhotnyuk.jsoniter_scala.core.JsonReader._
1010

1111
import scala.annotation.{switch, tailrec}
12+
import scala.{specialized => sp}
1213
import scala.util.control.NonFatal
1314

1415
class JsonParseException(msg: String, cause: Throwable, withStackTrace: Boolean)
@@ -478,12 +479,12 @@ final class JsonReader private[jsoniter_scala](
478479
x
479480
}
480481

481-
def readNullOrError[A](default: A, msg: String): A =
482-
if (default == null) throw new IllegalArgumentException("'default' should not be null")
482+
def readNullOrError[@sp A](default: A, msg: String): A =
483+
if (default == null) decodeError(msg)
483484
else if (isCurrentToken('n', head)) parseNullOrError(default, msg, head)
484485
else decodeError(msg)
485486

486-
def readNullOrTokenError[A](default: A, b: Byte): A =
487+
def readNullOrTokenError[@sp A](default: A, b: Byte): A =
487488
if (default == null) tokenError(b)
488489
else if (isCurrentToken('n', head)) parseNullOrTokenError(default, b, head)
489490
else tokenOrNullError(b)
@@ -529,62 +530,62 @@ final class JsonReader private[jsoniter_scala](
529530

530531
def decodeError(msg: String): Nothing = decodeError(msg, head - 1)
531532

532-
private[jsoniter_scala] def read[A](codec: JsonValueCodec[A], buf: Array[Byte], from: Int, to: Int, config: ReaderConfig): A = {
533+
private[jsoniter_scala] def read[@sp A](codec: JsonValueCodec[A], buf: Array[Byte], from: Int, to: Int, config: ReaderConfig): A = {
533534
val currBuf = this.buf
534-
this.config = config
535-
this.buf = buf
536-
head = from
537-
tail = to
538-
mark = 2147483647
539-
totalRead = 0
540-
try codec.decodeValue(this, codec.nullValue)
541-
finally {
535+
try {
536+
this.buf = buf
537+
this.config = config
538+
head = from
539+
tail = to
540+
totalRead = 0
541+
mark = 2147483647
542+
codec.decodeValue(this, codec.nullValue)
543+
} finally {
542544
this.buf = currBuf
543545
freeTooLongCharBuf()
544546
}
545547
}
546548

547-
private[jsoniter_scala] def read[A](codec: JsonValueCodec[A], in: InputStream, config: ReaderConfig): A = {
548-
this.config = config
549-
this.in = in
550-
head = 0
551-
tail = 0
552-
mark = 2147483647
553-
totalRead = 0
554-
try codec.decodeValue(this, codec.nullValue)
555-
finally {
549+
private[jsoniter_scala] def read[@sp A](codec: JsonValueCodec[A], in: InputStream, config: ReaderConfig): A =
550+
try {
551+
this.config = config
552+
this.in = in
553+
head = 0
554+
tail = 0
555+
totalRead = 0
556+
mark = 2147483647
557+
codec.decodeValue(this, codec.nullValue)
558+
} finally {
556559
this.in = null // to help GC, and to avoid modifying of supplied for parsing Array[Byte]
557560
freeTooLongBuf()
558561
freeTooLongCharBuf()
559562
}
560-
}
561563

562-
private[jsoniter_scala] def scanValueStream[A](codec: JsonValueCodec[A], in: InputStream, config: ReaderConfig)
563-
(f: A => Boolean): Unit = {
564-
this.config = config
565-
this.in = in
566-
head = 0
567-
tail = 0
568-
mark = 2147483647
569-
totalRead = 0
564+
private[jsoniter_scala] def scanValueStream[@sp A](codec: JsonValueCodec[A], in: InputStream, config: ReaderConfig)
565+
(f: A => Boolean): Unit =
570566
try {
567+
this.config = config
568+
this.in = in
569+
head = 0
570+
tail = 0
571+
totalRead = 0
572+
mark = 2147483647
571573
while (f(codec.decodeValue(this, codec.nullValue)) && skipWhitespaces()) ()
572574
} finally {
573575
this.in = null // to help GC, and to avoid modifying of supplied for parsing Array[Byte]
574576
freeTooLongBuf()
575577
freeTooLongCharBuf()
576578
}
577-
}
578579

579-
private[jsoniter_scala] def scanArray[A](codec: JsonValueCodec[A], in: InputStream, config: ReaderConfig)
580-
(f: A => Boolean): Unit = {
581-
this.config = config
582-
this.in = in
583-
head = 0
584-
tail = 0
585-
mark = 2147483647
586-
totalRead = 0
580+
private[jsoniter_scala] def scanArray[@sp A](codec: JsonValueCodec[A], in: InputStream, config: ReaderConfig)
581+
(f: A => Boolean): Unit =
587582
try {
583+
this.config = config
584+
this.in = in
585+
head = 0
586+
tail = 0
587+
totalRead = 0
588+
mark = 2147483647
588589
if (isNextToken('[')) {
589590
if (!isNextToken(']')) {
590591
rollbackToken()
@@ -600,7 +601,6 @@ final class JsonReader private[jsoniter_scala](
600601
freeTooLongBuf()
601602
freeTooLongCharBuf()
602603
}
603-
}
604604

605605
private def skipWhitespaces(): Boolean = {
606606
var pos = head
@@ -696,7 +696,7 @@ final class JsonReader private[jsoniter_scala](
696696
else buf(pos - 1) == b
697697

698698
@tailrec
699-
private def parseNullOrError[A](default: A, error: String, pos: Int): A =
699+
private def parseNullOrError[@sp A](default: A, error: String, pos: Int): A =
700700
if (pos + 2 < tail) {
701701
if (buf(pos) != 'u') decodeError(error, pos)
702702
if (buf(pos + 1) != 'l') decodeError(error, pos + 1)
@@ -706,7 +706,7 @@ final class JsonReader private[jsoniter_scala](
706706
} else parseNullOrError(default, error, loadMoreOrError(pos))
707707

708708
@tailrec
709-
private def parseNullOrTokenError[A](default: A, b: Byte, pos: Int): A =
709+
private def parseNullOrTokenError[@sp A](default: A, b: Byte, pos: Int): A =
710710
if (pos + 2 < tail) {
711711
if (buf(pos) != 'u') tokenOrNullError(b, pos)
712712
if (buf(pos + 1) != 'l') tokenOrNullError(b, pos + 1)
@@ -1260,7 +1260,7 @@ final class JsonReader private[jsoniter_scala](
12601260
case ex: NumberFormatException => decodeError("illegal number", head - 1, ex)
12611261
}
12621262

1263-
private def readNullOrNumberError[A](default: A, pos: Int): A =
1263+
private def readNullOrNumberError[@sp A](default: A, pos: Int): A =
12641264
if (default == null) numberError(pos - 1)
12651265
else parseNullOrError(default, "expected number or null", pos)
12661266

core/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/JsonWriter.scala

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter.{escapedChars, _}
99
import scala.annotation.{switch, tailrec}
1010
import scala.collection.breakOut
1111
import scala.collection.JavaConverters._
12+
import scala.{specialized => sp}
1213

1314
/**
1415
* Configuration for [[com.github.plokhotnyuk.jsoniter_scala.core.JsonWriter]] that contains params for formatting of
@@ -323,46 +324,43 @@ final class JsonWriter private[jsoniter_scala](
323324

324325
def writeObjectEnd(): Unit = writeNestedEnd('}')
325326

326-
private[jsoniter_scala] def write[A](codec: JsonValueCodec[A], x: A, out: OutputStream, config: WriterConfig): Unit = {
327-
this.config = config
328-
this.out = out
329-
count = 0
330-
indention = 0
327+
private[jsoniter_scala] def write[@sp A](codec: JsonValueCodec[A], x: A, out: OutputStream, config: WriterConfig): Unit =
331328
try {
329+
this.out = out
330+
this.config = config
331+
count = 0
332+
indention = 0
333+
isBufGrowingAllowed = true
332334
codec.encodeValue(x, this)
333335
flushBuffer() // do not flush buffer in case of exception during encoding to avoid hiding it by possible new one
334336
} finally {
335337
this.out = null // do not close output stream, just help GC instead
336338
freeTooLongBuf()
337339
}
338-
}
339340

340-
private[jsoniter_scala] def write[A](codec: JsonValueCodec[A], x: A, config: WriterConfig): Array[Byte] = {
341-
this.config = config
342-
this.count = 0
343-
this.indention = 0
341+
private[jsoniter_scala] def write[@sp A](codec: JsonValueCodec[A], x: A, config: WriterConfig): Array[Byte] =
344342
try {
343+
this.config = config
344+
this.count = 0
345+
this.indention = 0
346+
isBufGrowingAllowed = true
345347
codec.encodeValue(x, this)
346348
val arr = new Array[Byte](count)
347349
System.arraycopy(buf, 0, arr, 0, arr.length)
348350
arr
349351
} finally freeTooLongBuf()
350-
}
351352

352-
private[jsoniter_scala] def write[A](codec: JsonValueCodec[A], x: A, buf: Array[Byte], from: Int, config: WriterConfig): Int = {
353+
private[jsoniter_scala] def write[@sp A](codec: JsonValueCodec[A], x: A, buf: Array[Byte], from: Int, config: WriterConfig): Int = {
353354
val currBuf = this.buf
354-
this.config = config
355-
this.buf = buf
356-
count = from
357-
indention = 0
358-
isBufGrowingAllowed = false
359355
try {
356+
this.buf = buf
357+
this.config = config
358+
count = from
359+
indention = 0
360+
isBufGrowingAllowed = false
360361
codec.encodeValue(x, this)
361362
count
362-
} finally {
363-
this.buf = currBuf
364-
isBufGrowingAllowed = true
365-
}
363+
} finally this.buf = currBuf
366364
}
367365

368366
private def writeNestedStart(b: Byte): Unit = {

core/src/main/scala/com/github/plokhotnyuk/jsoniter_scala/core/package.scala

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.github.plokhotnyuk.jsoniter_scala
22

33
import java.io.{InputStream, OutputStream}
4+
import scala.{specialized => sp}
45

56
package object core {
67
private[this] final val readerConfig = new ReaderConfig
@@ -29,7 +30,7 @@ package object core {
2930
* while some input bytes are expected
3031
* @throws NullPointerException if the `codec` or `in` is null
3132
*/
32-
final def read[A](in: InputStream)(implicit codec: JsonValueCodec[A]): A = {
33+
final def read[@sp A](in: InputStream)(implicit codec: JsonValueCodec[A]): A = {
3334
if (in eq null) throw new NullPointerException
3435
readerPool.get.read(codec, in, readerConfig)
3536
}
@@ -48,7 +49,7 @@ package object core {
4849
* while some input bytes are expected
4950
* @throws NullPointerException if the `codec`, `in` or `config` is null
5051
*/
51-
final def read[A](in: InputStream, config: ReaderConfig)(implicit codec: JsonValueCodec[A]): A = {
52+
final def read[@sp A](in: InputStream, config: ReaderConfig)(implicit codec: JsonValueCodec[A]): A = {
5253
if (in eq null) throw new NullPointerException
5354
readerPool.get.read(codec, in, config)
5455
}
@@ -71,8 +72,8 @@ package object core {
7172
* @throws NullPointerException if the `codec`, `in` or `config` is null
7273
* @throws Throwable if some error was thrown by f() call
7374
*/
74-
final def scanValueStream[A](in: InputStream, config: ReaderConfig = readerConfig)(f: A => Boolean)
75-
(implicit codec: JsonValueCodec[A]): Unit = {
75+
final def scanValueStream[@sp A](in: InputStream, config: ReaderConfig = readerConfig)(f: A => Boolean)
76+
(implicit codec: JsonValueCodec[A]): Unit = {
7677
if ((in eq null) || (f eq null)) throw new NullPointerException
7778
readerPool.get.scanValueStream(codec, in, config)(f)
7879
}
@@ -95,8 +96,8 @@ package object core {
9596
* @throws NullPointerException if the `codec`, `in` or `config` is null
9697
* @throws Throwable if some error was thrown by f() call
9798
*/
98-
final def scanArray[A](in: InputStream, config: ReaderConfig = readerConfig)(f: A => Boolean)
99-
(implicit codec: JsonValueCodec[A]): Unit = {
99+
final def scanArray[@sp A](in: InputStream, config: ReaderConfig = readerConfig)(f: A => Boolean)
100+
(implicit codec: JsonValueCodec[A]): Unit = {
100101
if ((in eq null) || (f eq null)) throw new NullPointerException
101102
readerPool.get.scanArray(codec, in, config)(f)
102103
}
@@ -117,7 +118,7 @@ package object core {
117118
* also in case if end of input is detected while some input bytes are expected
118119
* @throws NullPointerException If the `codec` or `buf` is null.
119120
*/
120-
final def read[A](buf: Array[Byte])(implicit codec: JsonValueCodec[A]): A =
121+
final def read[@sp A](buf: Array[Byte])(implicit codec: JsonValueCodec[A]): A =
121122
readerPool.get.read(codec, buf, 0, buf.length, readerConfig)
122123

123124
/**
@@ -134,7 +135,7 @@ package object core {
134135
* also in case if end of input is detected while some input bytes are expected
135136
* @throws NullPointerException if the `codec`, `buf` or `config` is null
136137
*/
137-
final def read[A](buf: Array[Byte], config: ReaderConfig)(implicit codec: JsonValueCodec[A]): A =
138+
final def read[@sp A](buf: Array[Byte], config: ReaderConfig)(implicit codec: JsonValueCodec[A]): A =
138139
readerPool.get.read(codec, buf, 0, buf.length, config)
139140

140141
/**
@@ -155,8 +156,8 @@ package object core {
155156
* @throws ArrayIndexOutOfBoundsException if the `to` is greater than `buf` length or negative,
156157
* or `from` is greater than `to` or negative
157158
*/
158-
final def read[A](buf: Array[Byte], from: Int, to: Int, config: ReaderConfig = readerConfig)
159-
(implicit codec: JsonValueCodec[A]): A = {
159+
final def read[@sp A](buf: Array[Byte], from: Int, to: Int, config: ReaderConfig = readerConfig)
160+
(implicit codec: JsonValueCodec[A]): A = {
160161
if (to > buf.length || to < 0)
161162
throw new ArrayIndexOutOfBoundsException("`to` should be positive and not greater than `buf` length")
162163
if (from > to || from < 0)
@@ -174,7 +175,7 @@ package object core {
174175
* @param codec a codec for the given value
175176
* @throws NullPointerException if the `codec` or `config` is null
176177
*/
177-
final def write[A](x: A, out: OutputStream)(implicit codec: JsonValueCodec[A]): Unit = {
178+
final def write[@sp A](x: A, out: OutputStream)(implicit codec: JsonValueCodec[A]): Unit = {
178179
if (out eq null) throw new NullPointerException
179180
writerPool.get.write(codec, x, out, writerConfig)
180181
}
@@ -190,7 +191,7 @@ package object core {
190191
* @param codec a codec for the given value
191192
* @throws NullPointerException if the `codec`, `out` or `config` is null
192193
*/
193-
final def write[A](x: A, out: OutputStream, config: WriterConfig)(implicit codec: JsonValueCodec[A]): Unit = {
194+
final def write[@sp A](x: A, out: OutputStream, config: WriterConfig)(implicit codec: JsonValueCodec[A]): Unit = {
194195
if (out eq null) throw new NullPointerException
195196
writerPool.get.write(codec, x, out, config)
196197
}
@@ -205,7 +206,8 @@ package object core {
205206
* @return a byte array with `x` serialized to JSON
206207
* @throws NullPointerException if the `codec` is null
207208
*/
208-
final def write[A](x: A)(implicit codec: JsonValueCodec[A]): Array[Byte] = writerPool.get.write(codec, x, writerConfig)
209+
final def write[@sp A](x: A)(implicit codec: JsonValueCodec[A]): Array[Byte] =
210+
writerPool.get.write(codec, x, writerConfig)
209211

210212
/**
211213
* Serialize the `x` argument to a new allocated instance of byte array in UTF-8 encoding of JSON format,
@@ -218,7 +220,7 @@ package object core {
218220
* @return a byte array with `x` serialized to JSON
219221
* @throws NullPointerException if the `codec` or `config` is null
220222
*/
221-
final def write[A](x: A, config: WriterConfig)(implicit codec: JsonValueCodec[A]): Array[Byte] =
223+
final def write[@sp A](x: A, config: WriterConfig)(implicit codec: JsonValueCodec[A]): Array[Byte] =
222224
writerPool.get.write(codec, x, config)
223225

224226
/**
@@ -236,8 +238,8 @@ package object core {
236238
* @throws ArrayIndexOutOfBoundsException if the `from` is greater than `buf` length or negative,
237239
* or `buf` length was exceeded during serialization
238240
*/
239-
final def write[A](x: A, buf: Array[Byte], from: Int, config: WriterConfig = writerConfig)
240-
(implicit codec: JsonValueCodec[A]): Int = {
241+
final def write[@sp A](x: A, buf: Array[Byte], from: Int, config: WriterConfig = writerConfig)
242+
(implicit codec: JsonValueCodec[A]): Int = {
241243
if (from > buf.length || from < 0) // also checks that `buf` is not null before any serialization
242244
throw new ArrayIndexOutOfBoundsException("`from` should be positive and not greater than `buf` length")
243245
writerPool.get.write(codec, x, buf, from, config)

0 commit comments

Comments
 (0)