Skip to content

Commit 14f0db3

Browse files
committed
grpc-native: Implement encoding/decoding of bytes
Signed-off-by: Johannes Zottele <[email protected]>
1 parent 276b093 commit 14f0db3

File tree

7 files changed

+70
-6
lines changed

7 files changed

+70
-6
lines changed

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/internal/WireDecoder.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ internal interface WireDecoder: AutoCloseable {
4343
fun readSFixed64(): Long?
4444
fun readEnum(): Int?
4545
fun readString(): String?
46+
fun readBytes(): ByteArray?
4647
}
4748

4849
/**

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/internal/WireEncoder.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ internal interface WireEncoder {
2929
fun writeEnum(fieldNr: Int, value: Int): Boolean
3030
fun writeString(fieldNr: Int, value: String): Boolean
3131
fun flush()
32+
fun writeBytes(fieldNr: Int, value: ByteArray): Boolean
3233
}
3334

3435

grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/WireDecoder.native.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ internal class WireDecoderNative(private val source: Buffer): WireDecoder {
152152
return null
153153
}
154154

155+
// TODO: Is it possible to avoid copying the c_str, by directly allocating a K/N String (as in readBytes)?
155156
override fun readString(): String? = memScoped {
156157
val str = alloc<CPointerVar<pw_string_t>>()
157158
val ok = pw_decoder_read_string(raw, str.ptr)
@@ -162,6 +163,16 @@ internal class WireDecoderNative(private val source: Buffer): WireDecoder {
162163
pw_string_delete(str.value)
163164
}
164165
}
166+
167+
override fun readBytes(): ByteArray? {
168+
val length = readInt32() ?: return null
169+
if (length == 0) return ByteArray(0)
170+
val bytes = ByteArray(length)
171+
bytes.usePinned {
172+
pw_decoder_read_raw_bytes(raw, it.addressOf(0), length)
173+
}
174+
return bytes
175+
}
165176
}
166177

167178
internal actual fun WireDecoder(source: Buffer): WireDecoder = WireDecoderNative(source)

grpc/grpc-core/src/nativeMain/kotlin/kotlinx/rpc/grpc/internal/WireEncoder.native.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import kotlinx.io.Sink
99
import libprotowire.*
1010
import kotlin.experimental.ExperimentalNativeApi
1111
import kotlin.native.ref.createCleaner
12+
import kotlin.text.isEmpty
1213

1314

1415
// TODO: Evaluate if we should implement a ZeroCopyOutputSink (similar to the ZeroCopyInputSource)
@@ -104,6 +105,15 @@ internal class WireEncoderNative(private val sink: Sink): WireEncoder {
104105
}
105106
}
106107

108+
override fun writeBytes(fieldNr: Int, value: ByteArray): Boolean {
109+
if (value.isEmpty()) {
110+
return pw_encoder_write_bytes(raw, fieldNr, null, 0)
111+
}
112+
return value.usePinned {
113+
pw_encoder_write_bytes(raw, fieldNr, it.addressOf(0), value.size)
114+
}
115+
}
116+
107117
override fun flush() {
108118
pw_encoder_flush(raw)
109119
}

grpc/grpc-core/src/nativeTest/kotlin/kotlinx/rpc/grpc/internal/WireCodecTest.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -573,4 +573,41 @@ class WireCodecTest {
573573
assertNotNull(str)
574574
assertEquals("", str)
575575
}
576+
577+
@Test
578+
fun testEmptyByteArray() {
579+
val buffer = Buffer()
580+
581+
val encoder = WireEncoder(buffer)
582+
assertTrue(encoder.writeBytes(1, ByteArray(0)))
583+
encoder.flush()
584+
585+
val decoder = WireDecoder(buffer)
586+
587+
val tag = decoder.readTag()
588+
assertNotNull(tag)
589+
assertEquals(1, tag.fieldNr)
590+
assertEquals(WireType.LENGTH_DELIMITED, tag.wireType)
591+
592+
val bytes = decoder.readBytes()
593+
assertNotNull(bytes)
594+
assertEquals(0, bytes.size)
595+
}
596+
597+
@Test
598+
fun testBytesEncodeDecode() {
599+
val buffer = Buffer()
600+
val encoder = WireEncoder(buffer)
601+
602+
val bytes = ByteArray(1000000) { it.toByte() }
603+
604+
assertTrue(encoder.writeBytes(1, bytes))
605+
encoder.flush()
606+
607+
val decoder = WireDecoder(buffer)
608+
val tag = decoder.readTag()
609+
assertNotNull(tag)
610+
assertEquals(1, tag.fieldNr)
611+
assertEquals(WireType.LENGTH_DELIMITED, tag.wireType)
612+
}
576613
}

grpc/grpcpp-c/include/protowire.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ extern "C" {
4949
bool pw_encoder_write_double(pw_encoder_t *self, int field_no, double value);
5050
bool pw_encoder_write_enum(pw_encoder_t *self, int field_no, int value);
5151
bool pw_encoder_write_string(pw_encoder_t *self, int field_no, const char *data, int size);
52-
bool pw_encoder_write_bytes(pw_encoder_t *self, int field_no, pw_string_t *value);
52+
bool pw_encoder_write_bytes(pw_encoder_t *self, int field_no, const void *data, int size);
5353

5454

5555
//// WIRE DECODER ////
@@ -95,6 +95,8 @@ extern "C" {
9595
bool pw_decoder_read_double(pw_decoder_t *self, double *value);
9696
bool pw_decoder_read_enum(pw_decoder_t *self, int *value);
9797
bool pw_decoder_read_string(pw_decoder_t *self, pw_string_t **opaque_string);
98+
// To read an actual bytes field, you must combine read_int32 and this function
99+
bool pw_decoder_read_raw_bytes(pw_decoder_t *self, void* buffer, int size);
98100

99101
#ifdef __cplusplus
100102
}

grpc/grpcpp-c/src/protowire.cpp

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,14 @@ extern "C" {
169169
WRITE_FIELD_FUNC( enum, Enum, int)
170170

171171
bool pw_encoder_write_string(pw_encoder_t *self, int field_no, const char *data, int size) {
172+
return pw_encoder_write_bytes(self, field_no, data, size);
173+
}
174+
bool pw_encoder_write_bytes(pw_encoder_t *self, int field_no, const void *data, int size) {
172175
WireFormatLite::WriteTag(field_no, WireFormatLite::WIRETYPE_LENGTH_DELIMITED, &self->cos);
173176
self->cos.WriteVarint32(size);
174177
self->cos.WriteRawMaybeAliased(data, size);
175178
return check(self);
176179
}
177-
bool pw_encoder_write_bytes(pw_encoder_t *self, int field_no, pw_string_t *value) {
178-
WireFormatLite::WriteBytes(field_no, value->str, &self->cos);
179-
return check(self);
180-
}
181-
182180

183181
pw_decoder_t *pw_decoder_new(pw_zero_copy_input_t zero_copy_input) {
184182
return new pw_decoder_t(zero_copy_input);
@@ -214,4 +212,8 @@ extern "C" {
214212
*string_ref = new pw_string_t;
215213
return WireFormatLite::ReadString(&self->cis, &(*string_ref)->str);
216214
}
215+
216+
bool pw_decoder_read_raw_bytes(pw_decoder_t *self, void* buffer, int size) {
217+
return self->cis.ReadRaw(buffer, size);
218+
}
217219
}

0 commit comments

Comments
 (0)