Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions bson-multiplatform/src/commonMain/kotlin/BsonContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import opensavvy.ktmongo.bson.Bson
import opensavvy.ktmongo.bson.BsonArray
import opensavvy.ktmongo.bson.BsonContext
import opensavvy.ktmongo.bson.types.ObjectIdGenerator
import opensavvy.ktmongo.dsl.DangerousMongoApi
import opensavvy.ktmongo.dsl.LowLevelApi
import kotlin.concurrent.atomics.ExperimentalAtomicApi
import kotlin.time.ExperimentalTime
Expand Down Expand Up @@ -77,4 +78,57 @@ class BsonContext @OptIn(ExperimentalAtomicApi::class) constructor(
override fun readArray(bytes: ByteArray): BsonArray =
BsonArray(Bytes(bytes.copyOf()))

@LowLevelApi
@DangerousMongoApi
override fun openDocument(): CompletableBsonFieldWriter<Bson> {
val buffer = Buffer()
val bsonWriter = RawBsonWriter(buffer)
val fieldWriter = MultiplatformBsonFieldWriter(bsonWriter)

bsonWriter.writeInt32(0) // Document size. 0 for now, will be overwritten later.
return object: CompletableBsonFieldWriter<Bson>, BsonFieldWriter by fieldWriter {
override fun complete(): Bson {
bsonWriter.writeUnsignedByte(0u)

check(buffer.size <= Int.MAX_VALUE) { "A BSON document cannot be larger than 16MiB. Found ${buffer.size} bytes." }
val size = buffer.size.toInt()
val bytes = ByteArray(size)
buffer.readTo(bytes)
// 'buffer' is now empty

// Overwrite the size at the very start of the document
bsonWriter.writeInt32(size)
buffer.readTo(bytes, 0, 4)

return Bson(Bytes(bytes))
}
}
}

@LowLevelApi
@DangerousMongoApi
override fun openArray(): CompletableBsonValueWriter<BsonArray> {
val buffer = Buffer()
val bsonWriter = RawBsonWriter(buffer)
val fieldWriter = MultiplatformBsonArrayFieldWriter(MultiplatformBsonFieldWriter(bsonWriter))

bsonWriter.writeInt32(0) // Document size. 0 for now, will be overwritten later.
return object: CompletableBsonValueWriter<BsonArray>, BsonValueWriter by fieldWriter {
override fun complete(): BsonArray {
bsonWriter.writeUnsignedByte(0u)

check(buffer.size <= Int.MAX_VALUE) { "A BSON document cannot be larger than 16MiB. Found ${buffer.size} bytes." }
val size = buffer.size.toInt()
val bytes = ByteArray(size)
buffer.readTo(bytes)
// 'buffer' is now empty

// Overwrite the size at the very start of the document
bsonWriter.writeInt32(size)
buffer.readTo(bytes, 0, 4)

return BsonArray(Bytes(bytes))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ package opensavvy.ktmongo.bson.multiplatform

import opensavvy.ktmongo.bson.BsonFieldWriter
import opensavvy.ktmongo.bson.BsonValueWriter
import opensavvy.ktmongo.bson.CompletableBsonFieldWriter
import opensavvy.ktmongo.bson.CompletableBsonValueWriter
import opensavvy.ktmongo.bson.DEPRECATED_IN_BSON_SPEC
import opensavvy.ktmongo.bson.types.Timestamp
import opensavvy.ktmongo.dsl.DangerousMongoApi
import opensavvy.ktmongo.dsl.LowLevelApi

@LowLevelApi
Expand Down Expand Up @@ -143,6 +146,18 @@ internal class MultiplatformBsonArrayFieldWriter(
writer.writeArray(nextIndex(), block)
}

@LowLevelApi
@DangerousMongoApi
override fun openDocument(): CompletableBsonFieldWriter<Unit> {
return writer.openDocument(nextIndex())
}

@LowLevelApi
@DangerousMongoApi
override fun openArray(): CompletableBsonValueWriter<Unit> {
return writer.openArray(nextIndex())
}

@LowLevelApi
override fun <T> writeObjectSafe(obj: T) {
writer.writeObjectSafe(nextIndex(), obj)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import kotlinx.io.Buffer
import opensavvy.ktmongo.bson.BsonFieldWriter
import opensavvy.ktmongo.bson.BsonType
import opensavvy.ktmongo.bson.BsonValueWriter
import opensavvy.ktmongo.bson.CompletableBsonFieldWriter
import opensavvy.ktmongo.bson.CompletableBsonValueWriter
import opensavvy.ktmongo.bson.DEPRECATED_IN_BSON_SPEC
import opensavvy.ktmongo.bson.types.ObjectId
import opensavvy.ktmongo.bson.types.Timestamp
Expand All @@ -42,6 +44,13 @@ internal class MultiplatformBsonFieldWriter(
writer.writeCString(name)
}

@DangerousMongoApi
override fun open(name: String): CompletableBsonValueWriter<Unit> {
return object: CompletableBsonValueWriter<Unit>, BsonValueWriter by MultiplatformBsonSingleFieldWriter(this, name) {
override fun complete() {}
}
}

@LowLevelApi
override fun write(name: String, block: BsonValueWriter.() -> Unit) {
MultiplatformBsonSingleFieldWriter(this, name).block()
Expand Down Expand Up @@ -228,6 +237,54 @@ internal class MultiplatformBsonFieldWriter(
}
}

@LowLevelApi
@DangerousMongoApi
override fun openDocument(name: String): CompletableBsonFieldWriter<Unit> {
writeType(BsonType.Document)
writeName(name)

// We create the entire document in a child buffer so we can measure the size.
// Once we know the size, we can write it entirely to the real buffer.
val childBuffer = Buffer()
val childWriter = RawBsonWriter(childBuffer)
val childFieldWriter = MultiplatformBsonFieldWriter(childWriter)

return object: CompletableBsonFieldWriter<Unit>, BsonFieldWriter by childFieldWriter {
override fun complete() {
childWriter.writeUnsignedByte(0u)

// We now have an intermediate buffer, we can measure the size then transfer it the real writer
check(childBuffer.size <= Int.MAX_VALUE) { "A BSON document cannot be larger than 16MiB. Found ${childBuffer.size} bytes." }
this@MultiplatformBsonFieldWriter.writer.writeInt32(childBuffer.size.toInt() + 4)
this@MultiplatformBsonFieldWriter.writer.sink.write(childBuffer, childBuffer.size)
}
}
}

@LowLevelApi
@DangerousMongoApi
override fun openArray(name: String): CompletableBsonValueWriter<Unit> {
writeType(BsonType.Array)
writeName(name)

// We create the entire document in a child buffer so we can measure the size.
// Once we know the size, we can write it entirely to the real buffer.
val childBuffer = Buffer()
val childWriter = RawBsonWriter(childBuffer)
val childFieldWriter = MultiplatformBsonArrayFieldWriter(MultiplatformBsonFieldWriter(childWriter))

return object: CompletableBsonValueWriter<Unit>, BsonValueWriter by childFieldWriter {
override fun complete() {
childWriter.writeUnsignedByte(0u)

// We now have an intermediate buffer, we can measure the size then transfer it the real writer
check(childBuffer.size <= Int.MAX_VALUE) { "A BSON document cannot be larger than 16MiB. Found ${childBuffer.size} bytes." }
this@MultiplatformBsonFieldWriter.writer.writeInt32(childBuffer.size.toInt() + 4)
this@MultiplatformBsonFieldWriter.writer.sink.write(childBuffer, childBuffer.size)
}
}
}

@LowLevelApi
override fun <T> writeObjectSafe(name: String, obj: T) {
TODO()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package opensavvy.ktmongo.bson.multiplatform
import opensavvy.ktmongo.bson.BsonFieldWriter
import opensavvy.ktmongo.bson.BsonValueReader
import opensavvy.ktmongo.bson.BsonValueWriter
import opensavvy.ktmongo.bson.CompletableBsonFieldWriter
import opensavvy.ktmongo.bson.CompletableBsonValueWriter
import opensavvy.ktmongo.dsl.DangerousMongoApi
import opensavvy.ktmongo.bson.types.Timestamp
import opensavvy.ktmongo.dsl.LowLevelApi
Expand Down Expand Up @@ -124,6 +126,18 @@ internal class MultiplatformBsonSingleFieldWriter(
writer.writeArray(name, block)
}

@LowLevelApi
@DangerousMongoApi
override fun openDocument(): CompletableBsonFieldWriter<Unit> {
return writer.openDocument(name)
}

@LowLevelApi
@DangerousMongoApi
override fun openArray(): CompletableBsonValueWriter<Unit> {
return writer.openArray(name)
}

@LowLevelApi
override fun writeMinKey() {
writer.writeMinKey(name)
Expand Down
10 changes: 10 additions & 0 deletions bson-official/src/commonMain/kotlin/BsonContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package opensavvy.ktmongo.bson.official

import opensavvy.ktmongo.bson.*
import opensavvy.ktmongo.bson.BsonContext
import opensavvy.ktmongo.dsl.DangerousMongoApi
import opensavvy.ktmongo.dsl.LowLevelApi

/**
Expand All @@ -38,4 +39,13 @@ interface BsonContext : BsonContext {
@LowLevelApi
override fun buildArray(instance: BsonValueWriteable): BsonArray =
buildArray { instance.writeTo(this) }


@LowLevelApi
@DangerousMongoApi
override fun openDocument(): CompletableBsonFieldWriter<Bson>

@LowLevelApi
@DangerousMongoApi
override fun openArray(): CompletableBsonValueWriter<BsonArray>
}
103 changes: 103 additions & 0 deletions bson-official/src/jvmMain/kotlin/BsonContext.jvm.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ package opensavvy.ktmongo.bson.official

import opensavvy.ktmongo.bson.BsonFieldWriter
import opensavvy.ktmongo.bson.BsonValueWriter
import opensavvy.ktmongo.bson.CompletableBsonFieldWriter
import opensavvy.ktmongo.bson.CompletableBsonValueWriter
import opensavvy.ktmongo.bson.DEPRECATED_IN_BSON_SPEC
import opensavvy.ktmongo.bson.official.types.Jvm
import opensavvy.ktmongo.bson.official.types.KotlinObjectIdCodec
import opensavvy.ktmongo.bson.official.types.toOfficial
import opensavvy.ktmongo.bson.types.ObjectIdGenerator
import opensavvy.ktmongo.bson.types.Timestamp
import opensavvy.ktmongo.dsl.DangerousMongoApi
import opensavvy.ktmongo.dsl.LowLevelApi
import org.bson.*
import org.bson.BsonArray
Expand Down Expand Up @@ -98,13 +101,49 @@ class JvmBsonContext(
)
return BsonArray(document, this)
}

@LowLevelApi
@DangerousMongoApi
override fun openArray(): CompletableBsonValueWriter<opensavvy.ktmongo.bson.official.BsonArray> {
val nativeArray = BsonArray()
val writer = JavaRootArrayWriter(this, nativeArray)
return object: CompletableBsonValueWriter<opensavvy.ktmongo.bson.official.BsonArray>, BsonValueWriter by writer {
override fun complete(): opensavvy.ktmongo.bson.official.BsonArray {
return BsonArray(nativeArray, this@JvmBsonContext)
}
}
}

@LowLevelApi
@DangerousMongoApi
override fun openDocument(): CompletableBsonFieldWriter<opensavvy.ktmongo.bson.official.Bson> {
val document = BsonDocument()
val officialWriter = BsonDocumentWriter(document)
val writer = JavaBsonWriter(this, officialWriter)

return object: CompletableBsonFieldWriter<opensavvy.ktmongo.bson.official.Bson>, BsonFieldWriter by writer {
override fun complete(): opensavvy.ktmongo.bson.official.Bson {
officialWriter.close()
return Bson(document, this@JvmBsonContext)
}
}
}
}

@OptIn(LowLevelApi::class)
private class JavaBsonWriter(
private val context: JvmBsonContext,
private val writer: BsonWriter
) : BsonFieldWriter, BsonValueWriter {

@DangerousMongoApi
override fun open(name: String): CompletableBsonValueWriter<Unit> {
writer.writeName(name)
return object: CompletableBsonValueWriter<Unit>, BsonValueWriter by this {
override fun complete() {}
}
}

override fun write(name: String, block: BsonValueWriter.() -> Unit) {
writer.writeName(name)
block()
Expand Down Expand Up @@ -181,6 +220,26 @@ private class JavaBsonWriter(
writer.writeEndArray()
}

@DangerousMongoApi
override fun openDocument(name: String): CompletableBsonFieldWriter<Unit> {
writer.writeStartDocument(name)
return object: CompletableBsonFieldWriter<Unit>, BsonFieldWriter by this {
override fun complete() {
this@JavaBsonWriter.writer.writeEndDocument()
}
}
}

@DangerousMongoApi
override fun openArray(name: String): CompletableBsonValueWriter<Unit> {
writer.writeStartArray(name)
return object: CompletableBsonValueWriter<Unit>, BsonValueWriter by this {
override fun complete() {
this@JavaBsonWriter.writer.writeEndArray()
}
}
}

override fun <T> writeObjectSafe(name: String, obj: T) {
writer.writeName(name)
writeObjectSafe(obj)
Expand Down Expand Up @@ -283,6 +342,28 @@ private class JavaBsonWriter(
writer.writeEndArray()
}

@LowLevelApi
@DangerousMongoApi
override fun openDocument(): CompletableBsonFieldWriter<Unit> {
writer.writeStartDocument()
return object: CompletableBsonFieldWriter<Unit>, BsonFieldWriter by this {
override fun complete() {
this@JavaBsonWriter.writer.writeEndDocument()
}
}
}

@LowLevelApi
@DangerousMongoApi
override fun openArray(): CompletableBsonValueWriter<Unit> {
writer.writeStartArray()
return object: CompletableBsonValueWriter<Unit>, BsonValueWriter by this {
override fun complete() {
this@JavaBsonWriter.writer.writeEndArray()
}
}
}

override fun <T> writeObjectSafe(obj: T) {
if (obj == null) {
writer.writeNull()
Expand Down Expand Up @@ -425,6 +506,28 @@ private class JavaRootArrayWriter(
array.add(context.buildArray(block).raw)
}

@LowLevelApi
@DangerousMongoApi
override fun openArray(): CompletableBsonValueWriter<Unit> {
val root = context.openArray()
return object: CompletableBsonValueWriter<Unit>, BsonValueWriter by root {
override fun complete(): Unit {
this@JavaRootArrayWriter.array.add(root.complete().raw)
}
}
}

@LowLevelApi
@DangerousMongoApi
override fun openDocument(): CompletableBsonFieldWriter<Unit> {
val root = context.openDocument()
return object: CompletableBsonFieldWriter<Unit>, BsonFieldWriter by root {
override fun complete(): Unit {
this@JavaRootArrayWriter.array.add(root.complete().raw)
}
}
}

@LowLevelApi
override fun <T> writeObjectSafe(obj: T) {
val document = BsonDocument()
Expand Down
Loading