Skip to content

grpc: Support sub-messages #430

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Aug 7, 2025
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,41 @@ package kotlinx.rpc.grpc.pb

import kotlinx.rpc.grpc.utils.BitSet
import kotlinx.rpc.internal.utils.InternalRpcApi
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty

@InternalRpcApi
public abstract class InternalMessage(fieldsWithPresence: Int) {
public val presenceMask: BitSet = BitSet(fieldsWithPresence)

@Suppress("PropertyName")
public abstract val _size: Int
}

@InternalRpcApi
public class MsgFieldDelegate<T : Any>(
private val presenceIdx: Int? = null,
private val defaultProvider: (() -> T)? = null
) : ReadWriteProperty<InternalMessage, T> {

private var valueSet = false
private var value: T? = null

override operator fun getValue(thisRef: InternalMessage, property: KProperty<*>): T {
if (!valueSet) {
if (defaultProvider != null) {
value = defaultProvider.invoke()
valueSet = true
} else {
error("Property ${property.name} not initialized")
}
}
return value as T
}

override operator fun setValue(thisRef: InternalMessage, property: KProperty<*>, value: T) {
presenceIdx?.let { thisRef.presenceMask[it] = true }
this@MsgFieldDelegate.value = value
valueSet = true
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
package kotlinx.rpc.grpc.pb

import kotlinx.io.Buffer
import kotlinx.rpc.grpc.internal.popLimit
import kotlinx.rpc.grpc.internal.pushLimit
import kotlinx.rpc.internal.utils.InternalRpcApi

// TODO: Evaluate if this buffer size is suitable for all targets (KRPC-186)
Expand Down Expand Up @@ -76,6 +78,16 @@ public interface WireDecoder : AutoCloseable {
public fun readPackedFloat(): List<Float>
public fun readPackedDouble(): List<Double>
public fun readPackedEnum(): List<Int>

// TODO: Throw error instead of just returning
public fun <T : InternalMessage> readMessage(msg: T, decoder: (T, WireDecoder) -> Unit) {
val len = readInt32()
if (hadError()) return
if (len <= 0) return
val limit = pushLimit(len)
decoder(msg, this)
popLimit(limit)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@ public interface WireEncoder {
public fun writePackedDouble(fieldNr: Int, value: List<Double>): Boolean
public fun writePackedEnum(fieldNr: Int, value: List<Int>, fieldSize: Int): Boolean =
writePackedInt32(fieldNr, value, fieldSize)

public fun <T : InternalMessage> writeMessage(
fieldNr: Int,
value: T,
encode: T.(WireEncoder) -> Unit
)

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@

package kotlinx.rpc.grpc.pb

import kotlinx.io.bytestring.encodeToByteString
import kotlinx.rpc.internal.utils.InternalRpcApi

@InternalRpcApi
public object WireSize

public fun WireSize.tag(fieldNumber: Int, wireType: WireType): Int =
uInt32(KTag(fieldNumber, wireType).toRawKTag())

@InternalRpcApi
public expect fun WireSize.int32(value: Int): Int

Expand All @@ -27,12 +31,36 @@ public expect fun WireSize.sInt32(value: Int): Int
@InternalRpcApi
public expect fun WireSize.sInt64(value: Long): Int

@InternalRpcApi
public fun WireSize.float(value: Float): Int = 32

@InternalRpcApi
public fun WireSize.double(value: Double): Int = 64

@InternalRpcApi
public fun WireSize.fixed32(value: UInt): Int = 32

@InternalRpcApi
public fun WireSize.fixed64(value: ULong): Int = 64

@InternalRpcApi
public fun WireSize.sFixed32(value: Int): Int = 32

@InternalRpcApi
public fun WireSize.sFixed64(value: Long): Int = 64

@InternalRpcApi
public fun WireSize.bool(value: Boolean): Int = int32(if (value) 1 else 0)

@InternalRpcApi
public fun WireSize.enum(value: Int): Int = int32(value)

@InternalRpcApi
public fun WireSize.bytes(value: ByteArray): Int = value.size

@InternalRpcApi
public fun WireSize.string(value: String): Int = value.encodeToByteString().size

@InternalRpcApi
public fun WireSize.packedInt32(value: List<Int>): Int = value.sumOf { int32(it) }

Expand All @@ -53,3 +81,21 @@ public fun WireSize.packedSInt64(value: List<Long>): Int = value.sumOf { sInt64(

@InternalRpcApi
public fun WireSize.packedEnum(value: List<Int>): Int = value.sumOf { enum(it) }

@InternalRpcApi
public fun WireSize.packedFloat(value: List<Float>): Int = value.size * 32

@InternalRpcApi
public fun WireSize.packedDouble(value: List<Double>): Int = value.size * 64

@InternalRpcApi
public fun WireSize.packedFixed32(value: List<UInt>): Int = value.size * 32

@InternalRpcApi
public fun WireSize.packedFixed64(value: List<ULong>): Int = value.size * 64

@InternalRpcApi
public fun WireSize.packedSFixed32(value: List<Int>): Int = value.size * 32

@InternalRpcApi
public fun WireSize.packedSFixed64(value: List<Long>): Int = value.size * 64
Loading
Loading