Protobuf: fixed a couple of bugs in binary serialization format and size estimation

This commit is contained in:
dsavvinov
2016-08-12 17:34:46 +03:00
parent 3e31ee9c1b
commit a21b818b85
3 changed files with 38 additions and 29 deletions
@@ -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
@@ -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
+10 -10
View File
@@ -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 {