diff --git a/proto/runtime/src/main/kotlin/CodedInputStream.kt b/proto/runtime/src/main/kotlin/CodedInputStream.kt index bbf2e58de41..1ba96e47970 100644 --- a/proto/runtime/src/main/kotlin/CodedInputStream.kt +++ b/proto/runtime/src/main/kotlin/CodedInputStream.kt @@ -192,13 +192,13 @@ class CodedInputStream(val buffer: ByteArray) { // reads zig-zag encoded integer not larger than 32-bit long fun readZigZag32NoTag(): Int { val value = readInt32NoTag() - return (value shr 1) xor (-(value and 1)) // bit magic for decoding zig-zag number + return (value ushr 1) xor (-(value and 1)) // bit magic for decoding zig-zag number } // reads zig-zag encoded integer not larger than 64-bit long fun readZigZag64NoTag(): Long { val value = readInt64NoTag() - return (value shr 1) xor (-(value and 1L)) // bit magic for decoding zig-zag number + return (value ushr 1) xor (-(value and 1L)) // bit magic for decoding zig-zag number } // checks if at least one more byte can be read from underlying input stream diff --git a/proto/runtime/src/main/kotlin/CodedOutputStream.kt b/proto/runtime/src/main/kotlin/CodedOutputStream.kt index 69b9efff821..7470239a2ba 100644 --- a/proto/runtime/src/main/kotlin/CodedOutputStream.kt +++ b/proto/runtime/src/main/kotlin/CodedOutputStream.kt @@ -11,7 +11,7 @@ class CodedOutputStream(val buffer: ByteArray) { fun writeTag(fieldNumber: Int, type: WireType) { val tag = (fieldNumber shl 3) or type.id - writeInt32NoTag(tag) + writeRawVarint32(tag) } fun writeInt32(fieldNumber: Int, value: Int) { @@ -19,15 +19,24 @@ class CodedOutputStream(val buffer: ByteArray) { writeInt32NoTag(value) } + fun writeInt32NoTag(value: Int) { + if (value < 0) { // sign-extend negative values + writeRawVarint64(value.toLong()) + return + } + writeRawVarint32(value) + } + // Note that unsigned integer types are stored as their signed counterparts with top bit // simply stored in the sign bit - similar to Java's protobuf implementation. Hence, all // methods, writing unsigned ints simply redirect call to corresponding signed-writing method fun writeUInt32(fieldNumber: Int, value: Int) { - writeInt32(fieldNumber, value) + writeTag(fieldNumber, WireType.VARINT) + writeUInt32NoTag(value) } fun writeUInt32NoTag(value: Int) { - writeInt32NoTag(value) + writeRawVarint32(value) } fun writeInt64(fieldNumber: Int, value: Long) { @@ -35,13 +44,18 @@ class CodedOutputStream(val buffer: ByteArray) { writeInt64NoTag(value) } + fun writeInt64NoTag(value: Long) { + writeRawVarint64(value) + } + // See notes on unsigned integers implementation above fun writeUInt64(fieldNumber: Int, value: Long) { - writeInt64(fieldNumber, value) + writeTag(fieldNumber, WireType.VARINT) + writeUInt64NoTag(value) } fun writeUInt64NoTag(value: Long) { - writeInt64NoTag(value) + writeRawVarint64(value) } fun writeBool(fieldNumber: Int, value: Boolean) { @@ -50,7 +64,7 @@ class CodedOutputStream(val buffer: ByteArray) { } fun writeBoolNoTag(value: Boolean) { - writeInt32NoTag(if (value) 1 else 0) + writeRawVarint32(if (value) 1 else 0) } // Writing enums is like writing one int32 number. Caller is responsible for converting enum-object to ordinal @@ -60,7 +74,7 @@ class CodedOutputStream(val buffer: ByteArray) { } fun writeEnumNoTag(value: Int) { - writeInt32NoTag(value) + writeRawVarint32(value) } fun writeSInt32(fieldNumber: Int, value: Int) { @@ -69,7 +83,7 @@ class CodedOutputStream(val buffer: ByteArray) { } fun writeSInt32NoTag(value: Int) { - writeInt32NoTag((value shl 1) xor (value shr 31)) + writeUInt32NoTag((value shl 1) xor (value shr 31)) } fun writeSInt64(fieldNumber: Int, value: Long) { @@ -78,7 +92,7 @@ class CodedOutputStream(val buffer: ByteArray) { } fun writeSInt64NoTag(value: Long) { - writeInt64NoTag((value shl 1) xor (value shr 63)) + writeUInt64NoTag((value shl 1) xor (value shr 63)) } fun writeBytes(fieldNumber: Int, value: ByteArray) { @@ -90,7 +104,7 @@ class CodedOutputStream(val buffer: ByteArray) { } fun writeBytesNoTag(value: ByteArray) { - writeInt32NoTag(value.size) + writeRawVarint32(value.size) output.write(value) } @@ -99,12 +113,7 @@ class CodedOutputStream(val buffer: ByteArray) { * Then she/he can re-use low-level methods for operating with raw values, that are not annotated with Protobuf tags. */ - fun writeInt32NoTag(value: Int) { - if (value < 0) { - writeInt64NoTag(value.toLong()) - return - } - + fun writeRawVarint32(value: Int) { var curValue: Int = value // we have at most 32 information bits. With overhead of 1 bit per 7 bits we need at most 5 bytes for encoding @@ -129,7 +138,7 @@ class CodedOutputStream(val buffer: ByteArray) { output.write(res, 0, resSize) } - fun writeInt64NoTag(value: Long) { + fun writeRawVarint64(value: Long) { var curValue: Long = value // we have at most 64 information bits. With overhead of 1 bit per 7 bits we need at most 10 bytes for encoding diff --git a/proto/runtime/src/main/kotlin/WireFormat.kt b/proto/runtime/src/main/kotlin/WireFormat.kt index ff1ba920f68..d43c6e37003 100644 --- a/proto/runtime/src/main/kotlin/WireFormat.kt +++ b/proto/runtime/src/main/kotlin/WireFormat.kt @@ -23,25 +23,22 @@ object WireFormat { } fun getVarint32Size(value: Int): Int { - if (value < 0) { - return getVarint64Size(value.toLong()) - } var curValue = value var size = 0 - while (curValue != 0) { + do { size += 1 curValue = curValue ushr VARINT_INFO_BITS_COUNT - } + } while (curValue != 0) return size } fun getVarint64Size(value: Long): Int { var curValue = value var size = 0 - while (curValue != 0L) { + do { size += 1 curValue = curValue ushr VARINT_INFO_BITS_COUNT - } + }while (curValue != 0L) return size } @@ -54,15 +51,18 @@ object WireFormat { } fun getInt32Size(fieldNumber: Int, value: Int): Int { - return getTagSize(fieldNumber, WireType.VARINT) + getVarint32Size(value) + return getTagSize(fieldNumber, WireType.VARINT) + getInt32SizeNoTag(value) } fun getInt32SizeNoTag(value: Int): Int { + if (value < 0) { + return getVarint64Size(value.toLong()) + } return getVarint32Size(value) } fun getUInt32Size(fieldNumber: Int, value: Int): Int { - return getInt32Size(fieldNumber, value) + return getTagSize(fieldNumber, WireType.VARINT) + getUInt32SizeNoTag(value) } fun getUInt32SizeNoTag(value: Int): Int { @@ -70,7 +70,7 @@ object WireFormat { } fun getInt64Size(fieldNumber: Int, value: Long): Int { - return getTagSize(fieldNumber, WireType.VARINT) + getVarint64Size(value) + return getTagSize(fieldNumber, WireType.VARINT) + getUInt64SizeNoTag(value) } fun getInt64SizeNoTag(value: Long): Int {