Skip to content

Commit 62ceef8

Browse files
authored
Merge pull request #69 from AVSystem/bigintdec
BigInt/BigDecimal native support in Input/Output
2 parents a106713 + 26046bb commit 62ceef8

File tree

16 files changed

+206
-95
lines changed

16 files changed

+206
-95
lines changed

commons-benchmark/jvm/src/main/scala/com/avsystem/commons/ser/GenCodecBenchmarks.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,7 @@ object DummyInput extends Input {
3939
def readList() = ignored
4040
def readBoolean() = ignored
4141
def readDouble() = ignored
42+
def readBigInt() = ignored
43+
def readBigDecimal() = ignored
4244
def skip() = ()
4345
}

commons-benchmark/src/main/scala/com/avsystem/commons/ser/CirceJsonInputOutput.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ class CirceJsonOutput(consumer: Json => Any) extends Output {
2222
def writeInt(int: Int): Unit = consumer(Json.fromInt(int))
2323
def writeLong(long: Long): Unit = consumer(Json.fromLong(long))
2424
def writeDouble(double: Double): Unit = consumer(Json.fromDoubleOrString(double))
25-
def writeBinary(binary: Array[Byte]): Unit = ???
25+
def writeBigInt(bigInt: BigInt): Unit = consumer(Json.fromBigInt(bigInt))
26+
def writeBigDecimal(bigDecimal: BigDecimal): Unit = consumer(Json.fromBigDecimal(bigDecimal))
27+
def writeBinary(binary: Array[Byte]): Unit = consumer(Json.fromValues(binary.map(Json.fromInt(_))))
2628
def writeList(): ListOutput = new CirceJsonListOutput(consumer)
2729
def writeObject(): ObjectOutput = new CirceJsonObjectOutput(consumer)
2830
override def writeFloat(float: Float): Unit = consumer(Json.fromFloatOrString(float))
@@ -67,7 +69,10 @@ class CirceJsonInput(json: Json) extends Input {
6769
def readInt(): Int = asNumber.toInt.getOrElse(failNot("int"))
6870
def readLong(): Long = asNumber.toLong.getOrElse(failNot("long"))
6971
def readDouble(): Double = asNumber.toDouble
70-
def readBinary(): Array[Byte] = ???
72+
def readBigInt(): BigInt = asNumber.toBigInt.getOrElse(failNot("bigInteger"))
73+
def readBigDecimal(): BigDecimal = asNumber.toBigDecimal.getOrElse(failNot("bigDecimal"))
74+
def readBinary(): Array[Byte] = json.asArray.getOrElse(failNot("array")).iterator
75+
.map(_.asNumber.flatMap(_.toByte).getOrElse(failNot("byte"))).toArray
7176
def readList(): ListInput = new CirceJsonListInput(json.asArray.getOrElse(failNot("array")))
7277
def readObject(): ObjectInput = new CirceJsonObjectInput(json.asObject.getOrElse(failNot("object")))
7378
def skip(): Unit = ()

commons-core/src/main/scala/com/avsystem/commons/serialization/GenCodec.scala

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,8 +287,8 @@ object GenCodec extends RecursiveAutoCodecs with TupleGenCodecs {
287287
implicit lazy val LongCodec: GenCodec[Long] = create(_.readLong(), _ writeLong _)
288288
implicit lazy val FloatCodec: GenCodec[Float] = create(_.readFloat(), _ writeFloat _)
289289
implicit lazy val DoubleCodec: GenCodec[Double] = create(_.readDouble(), _ writeDouble _)
290-
implicit lazy val BigIntCodec: GenCodec[BigInt] = createNullable(i => BigInt(i.readString()), (o, v) => o.writeString(v.toString))
291-
implicit lazy val BigDecimalCodec: GenCodec[BigDecimal] = createNullable(i => BigDecimal(i.readString()), (o, v) => o.writeString(v.toString))
290+
implicit lazy val BigIntCodec: GenCodec[BigInt] = createNullable(_.readBigInt(), _ writeBigInt _)
291+
implicit lazy val BigDecimalCodec: GenCodec[BigDecimal] = createNullable(_.readBigDecimal(), _ writeBigDecimal _)
292292

293293
implicit lazy val JBooleanCodec: GenCodec[JBoolean] = createNullable(_.readBoolean(), _ writeBoolean _)
294294
implicit lazy val JCharacterCodec: GenCodec[JCharacter] = createNullable(_.readChar(), _ writeChar _)
@@ -298,8 +298,10 @@ object GenCodec extends RecursiveAutoCodecs with TupleGenCodecs {
298298
implicit lazy val JLongCodec: GenCodec[JLong] = createNullable(_.readLong(), _ writeLong _)
299299
implicit lazy val JFloatCodec: GenCodec[JFloat] = createNullable(_.readFloat(), _ writeFloat _)
300300
implicit lazy val JDoubleCodec: GenCodec[JDouble] = createNullable(_.readDouble(), _ writeDouble _)
301-
implicit lazy val JBigIntegerCodec: GenCodec[JBigInteger] = createNullable(i => new JBigInteger(i.readString()), (o, v) => o.writeString(v.toString))
302-
implicit lazy val JBigDecimalCodec: GenCodec[JBigDecimal] = createNullable(i => new JBigDecimal(i.readString()), (o, v) => o.writeString(v.toString))
301+
implicit lazy val JBigIntegerCodec: GenCodec[JBigInteger] =
302+
createNullable(_.readBigInt().bigInteger, (o, v) => o.writeBigInt(BigInt(v)))
303+
implicit lazy val JBigDecimalCodec: GenCodec[JBigDecimal] =
304+
createNullable(_.readBigDecimal().bigDecimal, (o, v) => o.writeBigDecimal(BigDecimal(v)))
303305

304306
implicit lazy val JDateCodec: GenCodec[JDate] = createNullable(i => new JDate(i.readTimestamp()), (o, d) => o.writeTimestamp(d.getTime))
305307
implicit lazy val StringCodec: GenCodec[String] = createNullable(_.readString(), _ writeString _)

commons-core/src/main/scala/com/avsystem/commons/serialization/InputOutput.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ trait Output extends Any {
2222
def writeTimestamp(millis: Long): Unit = writeLong(millis)
2323
def writeFloat(float: Float): Unit = writeDouble(float)
2424
def writeDouble(double: Double): Unit
25+
def writeBigInt(bigInt: BigInt): Unit
26+
def writeBigDecimal(bigDecimal: BigDecimal): Unit
2527
def writeBinary(binary: Array[Byte]): Unit
2628
def writeList(): ListOutput
2729
def writeObject(): ObjectOutput
@@ -139,6 +141,8 @@ trait Input extends Any {
139141
def readTimestamp(): Long = readLong()
140142
def readFloat(): Float = readDouble().toFloat
141143
def readDouble(): Double
144+
def readBigInt(): BigInt
145+
def readBigDecimal(): BigDecimal
142146
def readBinary(): Array[Byte]
143147
def readList(): ListInput
144148
def readObject(): ObjectInput

commons-core/src/main/scala/com/avsystem/commons/serialization/SimpleValueInputOutput.scala

Lines changed: 35 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ object SimpleValueOutput {
2828
* - `Int`
2929
* - `Long`
3030
* - `Double`
31+
* - `BigInt`
32+
* - `BigDecimal`
3133
* - `Boolean`
3234
* - `String`
3335
* - `Array[Byte]`
@@ -49,27 +51,27 @@ class SimpleValueOutput(
4951
def this(consumer: Any => Unit) =
5052
this(consumer, new MHashMap[String, Any], new ListBuffer[Any])
5153

52-
def writeBinary(binary: Array[Byte]) = consumer(binary)
53-
def writeString(str: String) = consumer(str)
54-
def writeDouble(double: Double) = consumer(double)
55-
def writeInt(int: Int) = consumer(int)
56-
57-
def writeList() = new ListOutput {
54+
def writeNull(): Unit = consumer(null)
55+
def writeBoolean(boolean: Boolean): Unit = consumer(boolean)
56+
def writeString(str: String): Unit = consumer(str)
57+
def writeInt(int: Int): Unit = consumer(int)
58+
def writeLong(long: Long): Unit = consumer(long)
59+
def writeDouble(double: Double): Unit = consumer(double)
60+
def writeBigInt(bigInt: BigInt): Unit = consumer(bigInt)
61+
def writeBigDecimal(bigDecimal: BigDecimal): Unit = consumer(bigDecimal)
62+
def writeBinary(binary: Array[Byte]): Unit = consumer(binary)
63+
64+
def writeList(): ListOutput = new ListOutput {
5865
private val buffer = newListRepr
5966
def writeElement() = new SimpleValueOutput(buffer += _, newObjectRepr, newListRepr)
60-
def finish() = consumer(buffer.result())
67+
def finish(): Unit = consumer(buffer.result())
6168
}
6269

63-
def writeBoolean(boolean: Boolean) = consumer(boolean)
64-
65-
def writeObject() = new ObjectOutput {
70+
def writeObject(): ObjectOutput = new ObjectOutput {
6671
private val result = newObjectRepr
6772
def writeField(key: String) = new SimpleValueOutput(v => result += ((key, v)), newObjectRepr, newListRepr)
68-
def finish() = consumer(result)
73+
def finish(): Unit = consumer(result)
6974
}
70-
71-
def writeLong(long: Long) = consumer(long)
72-
def writeNull() = consumer(null)
7375
}
7476

7577
object SimpleValueInput {
@@ -91,41 +93,42 @@ class SimpleValueInput(value: Any) extends Input {
9193
case _ => throw new ReadFailure(s"Expected ${classTag[B].runtimeClass} but got ${value.getClass}")
9294
}
9395

94-
def inputType = value match {
96+
def inputType: InputType = value match {
9597
case null => InputType.Null
9698
case _: BSeq[Any] => InputType.List
9799
case _: BMap[_, Any] => InputType.Object
98100
case _ => InputType.Simple
99101
}
100102

101-
def readBinary() = doRead[Array[Byte]]
102-
def readLong() = doReadUnboxed[Long, JLong]
103-
def readNull() = if (value == null) null else throw new ReadFailure("not null")
104-
def readObject() =
103+
def readNull(): Null = if (value == null) null else throw new ReadFailure("not null")
104+
def readBoolean(): Boolean = doReadUnboxed[Boolean, JBoolean]
105+
def readString(): String = doRead[String]
106+
def readInt(): Int = doReadUnboxed[Int, JInteger]
107+
def readLong(): Long = doReadUnboxed[Long, JLong]
108+
def readDouble(): Double = doReadUnboxed[Double, JDouble]
109+
def readBigInt(): BigInt = doRead[JBigInteger]
110+
def readBigDecimal(): BigDecimal = doRead[JBigDecimal]
111+
def readBinary(): Array[Byte] = doRead[Array[Byte]]
112+
113+
def readObject(): ObjectInput =
105114
new ObjectInput {
106115
private val map = doRead[BMap[String, Any]]
107116
private val it = map.iterator.map {
108117
case (k, v) => new SimpleValueFieldInput(k, v)
109118
}
110-
def nextField() = it.next()
111-
override def peekField(name: String) = map.getOpt(name).map(new SimpleValueFieldInput(name, _))
112-
def hasNext = it.hasNext
119+
def nextField(): SimpleValueFieldInput = it.next()
120+
override def peekField(name: String): Opt[SimpleValueFieldInput] = map.getOpt(name).map(new SimpleValueFieldInput(name, _))
121+
def hasNext: Boolean = it.hasNext
113122
}
114123

115-
def readInt() = doReadUnboxed[Int, JInteger]
116-
def readString() = doRead[String]
117-
118-
def readList() =
124+
def readList(): ListInput =
119125
new ListInput {
120126
private val it = doRead[BSeq[Any]].iterator.map(new SimpleValueInput(_))
121-
def nextElement() = it.next()
122-
def hasNext = it.hasNext
127+
def nextElement(): SimpleValueInput = it.next()
128+
def hasNext: Boolean = it.hasNext
123129
}
124130

125-
def readBoolean() = doReadUnboxed[Boolean, JBoolean]
126-
def readDouble() = doReadUnboxed[Double, JDouble]
127-
128-
def skip() = ()
131+
def skip(): Unit = ()
129132
}
130133

131134
class SimpleValueFieldInput(val fieldName: String, value: Any)

commons-core/src/main/scala/com/avsystem/commons/serialization/StreamInputOutput.scala

Lines changed: 84 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ private object FormatConstants {
2929
final val ObjectStartMarker: Byte = 11
3030
final val ListEndMarker: Byte = 12
3131
final val ObjectEndMarker: Byte = 13
32+
final val BitIntMarker: Byte = 14
33+
final val BigDecimalMarker: Byte = 15
3234
}
3335

3436
import com.avsystem.commons.serialization.FormatConstants._
@@ -47,53 +49,68 @@ class StreamInput(is: DataInputStream) extends Input {
4749
InputType.Simple
4850
}
4951

50-
def readNull(): Null = if (markerByte == NullMarker)
51-
null
52-
else
53-
throw new ReadFailure(s"Expected null, but $markerByte found")
54-
55-
def readString(): String = if (markerByte == StringMarker)
56-
is.readUTF()
57-
else
58-
throw new ReadFailure(s"Expected string, but $markerByte found")
59-
60-
def readBoolean(): Boolean = if (markerByte == BooleanMarker)
61-
is.readBoolean()
62-
else
63-
throw new ReadFailure(s"Expected boolean, but $markerByte found")
64-
65-
def readInt(): Int = if (markerByte == IntMarker)
66-
is.readInt()
67-
else
68-
throw new ReadFailure(s"Expected int, but $markerByte found")
69-
70-
def readLong(): Long = if (markerByte == LongMarker)
71-
is.readLong()
72-
else
73-
throw new ReadFailure(s"Expected long, but $markerByte found")
74-
75-
def readDouble(): Double = if (markerByte == DoubleMarker)
76-
is.readDouble()
77-
else
78-
throw new ReadFailure(s"Expected double, but $markerByte found")
79-
80-
def readBinary(): Array[Byte] = if (markerByte == ByteArrayMarker) {
81-
val binary = Array.ofDim[Byte](is.readInt())
82-
is.readFully(binary)
83-
binary
84-
} else {
85-
throw new ReadFailure(s"Expected binary array, but $markerByte found")
86-
}
52+
def readNull(): Null =
53+
if (markerByte == NullMarker) null
54+
else throw new ReadFailure(s"Expected null but $markerByte found")
55+
56+
def readString(): String =
57+
if (markerByte == StringMarker) is.readUTF()
58+
else throw new ReadFailure(s"Expected string but $markerByte found")
59+
60+
def readBoolean(): Boolean =
61+
if (markerByte == BooleanMarker) is.readBoolean()
62+
else throw new ReadFailure(s"Expected boolean but $markerByte found")
63+
64+
def readInt(): Int =
65+
if (markerByte == IntMarker) is.readInt()
66+
else throw new ReadFailure(s"Expected int but $markerByte found")
67+
68+
def readLong(): Long =
69+
if (markerByte == LongMarker) is.readLong()
70+
else throw new ReadFailure(s"Expected long but $markerByte found")
71+
72+
def readDouble(): Double =
73+
if (markerByte == DoubleMarker) is.readDouble()
74+
else throw new ReadFailure(s"Expected double but $markerByte found")
75+
76+
def readBigInt(): BigInt =
77+
if (markerByte == BitIntMarker) {
78+
val len = is.readInt()
79+
val bytes = new Array[Byte](len)
80+
is.read(bytes)
81+
BigInt(bytes)
82+
} else {
83+
throw new ReadFailure(s"Expected big integer but $markerByte found")
84+
}
8785

88-
def readList(): ListInput = if (markerByte == ListStartMarker)
89-
new StreamListInput(is)
90-
else
91-
throw new ReadFailure(s"Expected list, but $markerByte found")
86+
def readBigDecimal(): BigDecimal =
87+
if (markerByte == BigDecimalMarker) {
88+
val len = is.readInt()
89+
val bytes = new Array[Byte](len)
90+
is.read(bytes)
91+
val unscaled = BigInt(bytes)
92+
val scale = is.readInt()
93+
BigDecimal(unscaled, scale)
94+
} else {
95+
throw new ReadFailure(s"Expected big decimal but $markerByte found")
96+
}
9297

93-
def readObject(): ObjectInput = if (markerByte == ObjectStartMarker)
94-
new StreamObjectInput(is)
95-
else
96-
throw new ReadFailure(s"Expected object, but $markerByte found")
98+
def readBinary(): Array[Byte] =
99+
if (markerByte == ByteArrayMarker) {
100+
val binary = Array.ofDim[Byte](is.readInt())
101+
is.readFully(binary)
102+
binary
103+
} else {
104+
throw new ReadFailure(s"Expected binary array but $markerByte found")
105+
}
106+
107+
def readList(): ListInput =
108+
if (markerByte == ListStartMarker) new StreamListInput(is)
109+
else throw new ReadFailure(s"Expected list but $markerByte found")
110+
111+
def readObject(): ObjectInput =
112+
if (markerByte == ObjectStartMarker) new StreamObjectInput(is)
113+
else throw new ReadFailure(s"Expected object but $markerByte found")
97114

98115
def skip(): Unit = markerByte match {
99116
case NullMarker =>
@@ -121,6 +138,10 @@ class StreamInput(is: DataInputStream) extends Input {
121138
new StreamListInput(is).skipRemaining()
122139
case ObjectStartMarker =>
123140
new StreamObjectInput(is).skipRemaining()
141+
case BitIntMarker =>
142+
is.skipBytes(is.readInt())
143+
case BigDecimalMarker =>
144+
is.skipBytes(is.readInt() + Integer.BYTES)
124145
case unexpected =>
125146
throw new ReadFailure(s"Unexpected marker byte: $unexpected")
126147
}
@@ -182,14 +203,16 @@ private object StreamObjectInput {
182203
case class EmptyFieldInput(name: String) extends FieldInput {
183204
private def nope: Nothing = throw new ReadFailure(s"Something went horribly wrong ($name)")
184205

185-
def fieldName: String = nope
186206
def inputType: InputType = nope
207+
def fieldName: String = nope
187208
def readNull(): Null = nope
188209
def readString(): String = nope
189210
def readBoolean(): Boolean = nope
190211
def readInt(): Int = nope
191212
def readLong(): Long = nope
192213
def readDouble(): Double = nope
214+
def readBigInt(): BigInt = nope
215+
def readBigDecimal(): BigDecimal = nope
193216
def readBinary(): Array[Byte] = nope
194217
def readList(): ListInput = nope
195218
def readObject(): ObjectInput = nope
@@ -232,6 +255,21 @@ class StreamOutput(os: DataOutputStream) extends Output {
232255
os.writeDouble(double)
233256
}
234257

258+
def writeBigInt(bigInt: BigInt): Unit = {
259+
os.writeByte(BitIntMarker)
260+
val bytes = bigInt.toByteArray
261+
os.writeInt(bytes.length)
262+
os.write(bytes)
263+
}
264+
265+
def writeBigDecimal(bigDecimal: BigDecimal): Unit = {
266+
os.writeByte(BigDecimalMarker)
267+
val bytes = bigDecimal.bigDecimal.unscaledValue.toByteArray
268+
os.writeInt(bytes.length)
269+
os.write(bytes)
270+
os.writeInt(bigDecimal.scale)
271+
}
272+
235273
def writeBinary(binary: Array[Byte]): Unit = {
236274
os.writeByte(ByteArrayMarker)
237275
os.writeInt(binary.length)

commons-core/src/main/scala/com/avsystem/commons/serialization/json/JsonStringInput.scala

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class JsonStringInput(reader: JsonReader, callback: AfterElement = AfterElementN
3333
case _ => afterElement()
3434
}
3535

36-
private def expectedError(tpe: JsonType) = throw new ReadFailure(s"Expected $tpe but got ${reader.jsonType}: ${reader.currentValue}")
36+
private def expectedError(tpe: JsonType) =
37+
throw new ReadFailure(s"Expected $tpe but got ${reader.jsonType}: ${reader.currentValue}")
3738

3839
private def checkedValue[T](jsonType: JsonType): T = {
3940
if (reader.jsonType != jsonType) expectedError(jsonType)
@@ -64,6 +65,8 @@ class JsonStringInput(reader: JsonReader, callback: AfterElement = AfterElementN
6465
def readInt(): Int = matchNumericString(_.toInt)
6566
def readLong(): Long = matchNumericString(_.toLong)
6667
def readDouble(): Double = matchNumericString(_.toDouble)
68+
def readBigInt(): BigInt = matchNumericString(BigInt(_))
69+
def readBigDecimal(): BigDecimal = matchNumericString(BigDecimal(_))
6770
def readBinary(): Array[Byte] = {
6871
val hex = checkedValue[String](JsonType.string)
6972
val result = new Array[Byte](hex.length / 2)
@@ -213,7 +216,7 @@ final class JsonReader(val json: String) {
213216
private def readHex(): Int =
214217
fromHex(read())
215218

216-
private def parseNumber(): Any = {
219+
private def parseNumber(): String = {
217220
val start = i
218221

219222
if (isNext('-')) {

commons-core/src/main/scala/com/avsystem/commons/serialization/json/JsonStringOutput.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ final class JsonStringOutput(builder: JStringBuilder) extends BaseJsonOutput wit
5050
writeString(double.toString)
5151
else builder.append(double.toString)
5252
}
53+
def writeBigInt(bigInt: BigInt): Unit = builder.append(bigInt.toString)
54+
def writeBigDecimal(bigDecimal: BigDecimal): Unit = builder.append(bigDecimal.toString)
5355
def writeBinary(binary: Array[Byte]): Unit = {
5456
builder.append('"')
5557
var i = 0

0 commit comments

Comments
 (0)