Skip to content

Commit 596de22

Browse files
author
Roman Janusz
authored
Merge pull request #412 from AVSystem/optimized-bson
Changed `BsonInput`/`BsonOutput` implementations to use optimized value representations
2 parents 6ff59ca + 651809f commit 596de22

13 files changed

+573
-236
lines changed
Lines changed: 34 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
package com.avsystem.commons
22
package mongo
33

4-
import java.nio.ByteBuffer
5-
import com.avsystem.commons.serialization.GenCodec.{ReadFailure, WriteFailure}
6-
import com.avsystem.commons.serialization.{GenCodec, GenKeyCodec, TransparentWrapping}
7-
import org.bson.codecs.{BsonValueCodec, DecoderContext, EncoderContext}
4+
import com.avsystem.commons.serialization.GenCodec.ReadFailure
5+
import com.avsystem.commons.serialization._
6+
import org.bson._
87
import org.bson.io.BasicOutputBuffer
98
import org.bson.types.{Decimal128, ObjectId}
10-
import org.bson.{BsonArray, BsonBinary, BsonBinaryReader, BsonBinaryWriter, BsonBoolean, BsonDateTime, BsonDecimal128, BsonDocument, BsonDouble, BsonElement, BsonInt32, BsonInt64, BsonNull, BsonObjectId, BsonString, BsonType, BsonValue}
9+
10+
import java.nio.ByteBuffer
1111

1212
trait BsonGenCodecs {
1313
implicit def objectIdCodec: GenCodec[ObjectId] = BsonGenCodecs.objectIdCodec
1414
implicit def objectIdKeyCodec: GenKeyCodec[ObjectId] = BsonGenCodecs.objectIdKeyCodec
15-
1615
implicit def decimal128Codec: GenCodec[Decimal128] = BsonGenCodecs.decimal128Codec
1716

1817
implicit def bsonArrayCodec: GenCodec[BsonArray] = BsonGenCodecs.bsonArrayCodec
@@ -33,13 +32,13 @@ trait BsonGenCodecs {
3332
object BsonGenCodecs {
3433
// needed so that ObjectId can be used as ID type in AutoIdMongoEntity
3534
// (TransparentWrapping is used in EntityIdMode)
36-
implicit val objectIdIdentityWrapping: TransparentWrapping[ObjectId, ObjectId] =
37-
TransparentWrapping.identity
35+
implicit val objectIdIdentityWrapping: TransparentWrapping[ObjectId, ObjectId] = TransparentWrapping.identity
3836

3937
implicit val objectIdCodec: GenCodec[ObjectId] = GenCodec.nullable(
4038
i => i.readCustom(ObjectIdMarker).getOrElse(new ObjectId(i.readSimple().readString())),
4139
(o, v) => if (!o.writeCustom(ObjectIdMarker, v)) o.writeSimple().writeString(v.toHexString)
4240
)
41+
4342
implicit val objectIdKeyCodec: GenKeyCodec[ObjectId] =
4443
GenKeyCodec.create(new ObjectId(_), _.toHexString)
4544

@@ -48,104 +47,43 @@ object BsonGenCodecs {
4847
(o, v) => if (!o.writeCustom(Decimal128Marker, v)) o.writeSimple().writeBigDecimal(v.bigDecimalValue())
4948
)
5049

51-
implicit val bsonArrayCodec: GenCodec[BsonArray] = GenCodec.nullableList(
52-
li => new BsonArray(li.iterator(bsonValueCodec.read).to(JList)),
53-
(lo, ba) => ba.asScala.foreach(bsonValueCodec.write(lo.writeElement(), _))
54-
)
55-
56-
implicit val bsonBinaryCodec: GenCodec[BsonBinary] =
57-
GenCodec.ByteArrayCodec.transform[BsonBinary](_.getData, new BsonBinary(_))
58-
59-
implicit val bsonBooleanCodec: GenCodec[BsonBoolean] =
60-
GenCodec.BooleanCodec.transform[BsonBoolean](_.getValue, new BsonBoolean(_))
61-
62-
implicit val bsonDateTimeCodec: GenCodec[BsonDateTime] = GenCodec.nullableSimple(
63-
i => new BsonDateTime(i.readTimestamp()),
64-
(o, v) => o.writeTimestamp(v.getValue)
65-
)
66-
67-
implicit val bsonDocumentCodec: GenCodec[BsonDocument] = GenCodec.nullableObject(
68-
oi => new BsonDocument(oi.iterator(bsonValueCodec.read).map {
69-
case (k, v) => new BsonElement(k, v)
70-
}.to(JList)),
71-
(oo, bd) => bd.asScala.foreach { case (key, value) =>
72-
bsonValueCodec.write(oo.writeField(key), value)
50+
implicit val bsonValueCodec: GenCodec[BsonValue] = GenCodec.create(
51+
i => i.readCustom(BsonValueMarker).getOrElse {
52+
val reader = new BsonBinaryReader(ByteBuffer.wrap(i.readSimple().readBinary()))
53+
BsonValueUtils.decode(reader)
54+
},
55+
(o, bv) => if (!o.writeCustom(BsonValueMarker, bv)) {
56+
val buffer = new BasicOutputBuffer()
57+
val writer = new BsonBinaryWriter(buffer)
58+
BsonValueUtils.encode(writer, bv)
59+
writer.flush()
60+
writer.close()
61+
o.writeSimple().writeBinary(buffer.toByteArray)
7362
}
7463
)
7564

76-
implicit val bsonDecimal128Codec: GenCodec[BsonDecimal128] =
77-
decimal128Codec.transform[BsonDecimal128](_.getValue, new BsonDecimal128(_))
78-
79-
implicit val bsonDoubleCodec: GenCodec[BsonDouble] =
80-
GenCodec.DoubleCodec.transform(_.getValue, new BsonDouble(_))
81-
82-
implicit val bsonInt32Codec: GenCodec[BsonInt32] =
83-
GenCodec.IntCodec.transform(_.getValue, new BsonInt32(_))
65+
private def bsonValueSubCodec[T <: BsonValue](fromBsonValue: BsonValue => T): GenCodec[T] =
66+
bsonValueCodec.transform(identity, fromBsonValue)
8467

85-
implicit val bsonInt64Codec: GenCodec[BsonInt64] =
86-
GenCodec.LongCodec.transform(_.getValue, new BsonInt64(_))
68+
implicit val bsonArrayCodec: GenCodec[BsonArray] = bsonValueSubCodec(_.asArray())
69+
implicit val bsonBinaryCodec: GenCodec[BsonBinary] = bsonValueSubCodec(_.asBinary())
70+
implicit val bsonBooleanCodec: GenCodec[BsonBoolean] = bsonValueSubCodec(_.asBoolean())
71+
implicit val bsonDateTimeCodec: GenCodec[BsonDateTime] = bsonValueSubCodec(_.asDateTime())
72+
implicit val bsonDocumentCodec: GenCodec[BsonDocument] = bsonValueSubCodec(_.asDocument())
73+
implicit val bsonDecimal128Codec: GenCodec[BsonDecimal128] = bsonValueSubCodec(_.asDecimal128())
74+
implicit val bsonDoubleCodec: GenCodec[BsonDouble] = bsonValueSubCodec(_.asDouble())
75+
implicit val bsonInt32Codec: GenCodec[BsonInt32] = bsonValueSubCodec(_.asInt32())
76+
implicit val bsonInt64Codec: GenCodec[BsonInt64] = bsonValueSubCodec(_.asInt64())
8777

8878
implicit val bsonNullCodec: GenCodec[BsonNull] =
89-
GenCodec.create(i => {
90-
if (!i.readNull()) throw new ReadFailure("Input did not contain expected null value")
91-
BsonNull.VALUE
92-
}, (o, _) => o.writeNull())
79+
bsonValueSubCodec { bv =>
80+
if (bv.isNull) BsonNull.VALUE
81+
else throw new ReadFailure("Input did not contain expected null value")
82+
}
9383

9484
implicit val bsonObjectIdCodec: GenCodec[BsonObjectId] =
9585
objectIdCodec.transform(_.getValue, new BsonObjectId(_))
9686

9787
implicit val bsonStringCodec: GenCodec[BsonString] =
9888
GenCodec.StringCodec.transform(_.getValue, new BsonString(_))
99-
100-
private val mongoBsonValueCodec: BsonValueCodec = new BsonValueCodec()
101-
private val mongoDecoderContext: DecoderContext = DecoderContext.builder().build()
102-
private val mongoEncoderContext: EncoderContext = EncoderContext.builder().build()
103-
104-
implicit val bsonValueCodec: GenCodec[BsonValue] = GenCodec.create(
105-
i => {
106-
val bvOpt = i.readMetadata(BsonTypeMetadata) map {
107-
case BsonType.ARRAY => bsonArrayCodec.read(i)
108-
case BsonType.BINARY => bsonBinaryCodec.read(i)
109-
case BsonType.BOOLEAN => bsonBooleanCodec.read(i)
110-
case BsonType.DATE_TIME => bsonDateTimeCodec.read(i)
111-
case BsonType.DECIMAL128 => bsonDecimal128Codec.read(i)
112-
case BsonType.DOCUMENT => bsonDocumentCodec.read(i)
113-
case BsonType.DOUBLE => bsonDoubleCodec.read(i)
114-
case BsonType.INT32 => bsonInt32Codec.read(i)
115-
case BsonType.INT64 => bsonInt64Codec.read(i)
116-
case BsonType.NULL => bsonNullCodec.read(i)
117-
case BsonType.OBJECT_ID => bsonObjectIdCodec.read(i)
118-
case BsonType.STRING => bsonStringCodec.read(i)
119-
case other => throw new ReadFailure(s"Unsupported Bson type: $other")
120-
}
121-
bvOpt.getOrElse {
122-
val reader = new BsonBinaryReader(ByteBuffer.wrap(i.readSimple().readBinary()))
123-
mongoBsonValueCodec.decode(reader, mongoDecoderContext)
124-
}
125-
},
126-
(o, bv) => if (o.keepsMetadata(BsonTypeMetadata)) {
127-
bv match {
128-
case array: BsonArray => bsonArrayCodec.write(o, array)
129-
case binary: BsonBinary => bsonBinaryCodec.write(o, binary)
130-
case boolean: BsonBoolean => bsonBooleanCodec.write(o, boolean)
131-
case dateTime: BsonDateTime => bsonDateTimeCodec.write(o, dateTime)
132-
case decimal128: BsonDecimal128 => bsonDecimal128Codec.write(o, decimal128)
133-
case document: BsonDocument => bsonDocumentCodec.write(o, document)
134-
case double: BsonDouble => bsonDoubleCodec.write(o, double)
135-
case int32: BsonInt32 => bsonInt32Codec.write(o, int32)
136-
case int64: BsonInt64 => bsonInt64Codec.write(o, int64)
137-
case bNull: BsonNull => bsonNullCodec.write(o, bNull)
138-
case objectId: BsonObjectId => bsonObjectIdCodec.write(o, objectId)
139-
case string: BsonString => bsonStringCodec.write(o, string)
140-
case other => throw new WriteFailure(s"Unsupported value: $other")
141-
}
142-
} else {
143-
val buffer = new BasicOutputBuffer()
144-
val writer = new BsonBinaryWriter(buffer)
145-
mongoBsonValueCodec.encode(writer, bv, mongoEncoderContext)
146-
writer.flush()
147-
writer.close()
148-
o.writeSimple().writeBinary(buffer.toByteArray)
149-
}
150-
)
15189
}

commons-mongo/jvm/src/main/scala/com/avsystem/commons/mongo/BsonInputOutput.scala

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,68 @@
11
package com.avsystem.commons
22
package mongo
33

4-
import java.nio.ByteBuffer
5-
6-
import com.avsystem.commons.serialization.{InputAndSimpleInput, InputMetadata, OutputAndSimpleOutput, TypeMarker}
7-
import org.bson.BsonType
4+
import com.avsystem.commons.serialization.GenCodec.ReadFailure
5+
import com.avsystem.commons.serialization.{FieldInput, InputAndSimpleInput, InputMetadata, OutputAndSimpleOutput, TypeMarker}
86
import org.bson.types.{Decimal128, ObjectId}
7+
import org.bson.{BsonInvalidOperationException, BsonType, BsonValue}
8+
9+
import java.nio.ByteBuffer
910

1011
object ObjectIdMarker extends TypeMarker[ObjectId]
1112
object Decimal128Marker extends TypeMarker[Decimal128]
13+
object BsonValueMarker extends TypeMarker[BsonValue]
1214

1315
object BsonTypeMetadata extends InputMetadata[BsonType]
1416

1517
trait BsonInput extends Any with InputAndSimpleInput {
1618
def readObjectId(): ObjectId
1719
def readDecimal128(): Decimal128
20+
def readBsonValue(): BsonValue
1821

1922
protected def bsonType: BsonType
2023

24+
protected def handleFailures[T](expr: => T): T =
25+
try expr catch {
26+
case e: BsonInvalidOperationException => throw new ReadFailure(e.getMessage, e)
27+
}
28+
29+
protected def wrongType(expected: BsonType*): Nothing =
30+
throw new ReadFailure(s"Encountered BsonValue of type $bsonType, expected ${expected.mkString(" or ")}")
31+
32+
protected def expect[T](tpe: BsonType, value: => T): T =
33+
if (bsonType == tpe) handleFailures(value)
34+
else wrongType(tpe)
35+
36+
override def readBigInt(): BigInt = handleFailures {
37+
bsonType match {
38+
case BsonType.INT32 | BsonType.INT64 =>
39+
BigInt(readLong())
40+
case BsonType.DECIMAL128 =>
41+
val bigDec = BigDecimal(readDecimal128().bigDecimalValue())
42+
bigDec.toBigIntExact.getOrElse(throw new ReadFailure(s"whole number expected but got $bigDec"))
43+
case BsonType.BINARY =>
44+
BigInt(readBinary())
45+
case _ =>
46+
wrongType(BsonType.INT32, BsonType.INT64, BsonType.DECIMAL128, BsonType.BINARY)
47+
}
48+
}
49+
50+
override def readBigDecimal(): BigDecimal =
51+
handleFailures {
52+
bsonType match {
53+
case BsonType.INT32 | BsonType.INT64 =>
54+
BigDecimal(readLong())
55+
case BsonType.DOUBLE =>
56+
BigDecimal(readDouble())
57+
case BsonType.DECIMAL128 =>
58+
BigDecimal(readDecimal128().bigDecimalValue)
59+
case BsonType.BINARY =>
60+
BsonInput.bigDecimalFromBytes(readBinary())
61+
case _ =>
62+
wrongType(BsonType.INT32, BsonType.INT64, BsonType.DOUBLE, BsonType.DECIMAL128, BsonType.BINARY)
63+
}
64+
}
65+
2166
override def readMetadata[T](metadata: InputMetadata[T]): Opt[T] =
2267
metadata match {
2368
case BsonTypeMetadata => bsonType.opt
@@ -28,6 +73,7 @@ trait BsonInput extends Any with InputAndSimpleInput {
2873
typeMarker match {
2974
case ObjectIdMarker => readObjectId().opt
3075
case Decimal128Marker => readDecimal128().opt
76+
case BsonValueMarker => readBsonValue().opt
3177
case _ => Opt.Empty
3278
}
3379
}
@@ -43,9 +89,25 @@ object BsonInput {
4389
}
4490
}
4591

92+
trait BsonFieldInput extends BsonInput with FieldInput
93+
4694
trait BsonOutput extends Any with OutputAndSimpleOutput {
4795
def writeObjectId(objectId: ObjectId): Unit
4896
def writeDecimal128(decimal128: Decimal128): Unit
97+
def writeBsonValue(bsonValue: BsonValue): Unit
98+
99+
override def writeBigInt(bigInt: BigInt): Unit =
100+
if (bigInt.isValidLong) writeLong(bigInt.longValue)
101+
else Decimal128Utils.fromBigDecimal(BigDecimal(bigInt)) match {
102+
case Opt(dec128) => writeDecimal128(dec128)
103+
case Opt.Empty => writeBinary(bigInt.toByteArray)
104+
}
105+
106+
override def writeBigDecimal(bigDecimal: BigDecimal): Unit =
107+
Decimal128Utils.fromBigDecimal(bigDecimal) match {
108+
case Opt(dec128) => writeDecimal128(dec128)
109+
case Opt.Empty => writeBinary(BsonOutput.bigDecimalBytes(bigDecimal))
110+
}
49111

50112
override def keepsMetadata(metadata: InputMetadata[_]): Boolean =
51113
BsonTypeMetadata == metadata
@@ -54,13 +116,15 @@ trait BsonOutput extends Any with OutputAndSimpleOutput {
54116
typeMarker match {
55117
case ObjectIdMarker => writeObjectId(value); true
56118
case Decimal128Marker => writeDecimal128(value); true
119+
case BsonValueMarker => writeBsonValue(value); true
57120
case _ => false
58121
}
59122
}
60123

61124
object BsonOutput {
62125
def bigDecimalBytes(bigDecimal: BigDecimal): Array[Byte] = {
63126
val unscaledBytes = bigDecimal.bigDecimal.unscaledValue.toByteArray
64-
ByteBuffer.allocate(unscaledBytes.length + Integer.BYTES).put(unscaledBytes).putInt(bigDecimal.scale).array
127+
ByteBuffer.allocate(unscaledBytes.length + Integer.BYTES)
128+
.put(unscaledBytes).putInt(bigDecimal.scale).array
65129
}
66130
}

0 commit comments

Comments
 (0)