Repository navigation
Write sint32 values as varints of at most 5 bytes - #42
Merged
Merged
Conversation
getSerializedSize() counts a sint32 as its zigzag value encoded as an unsigned 32-bit varint, at most 5 bytes, as protobuf does. But writeRawSignedVarInt() went through writeRawVarInt(), the int32 writer, which writes a negative int as a 10-byte varint. The zigzag value has its top bit set for every v >= 2^30 and every v < -2^30, so for each such value writeTo() wrote 5 bytes more than it had reserved, with no parsing involved: toByteArray() threw ArrayIndexOutOfBoundsException, and a pooled heap target got the extra bytes written into the neighbouring buffer of its chunk. Write the zigzag value with a new writeRawVarUInt() (byte[] and NIO sinks), which never writes more than 5 bytes. The output now matches protobuf-java for singular, repeated and packed sint32 fields.
This was referenced Oct 2, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
For a
sint32field,getSerializedSize()counts the zigzag-encoded value as an unsigned 32-bit varint, at most 5 bytes, as protobuf does. The writer, however, went throughwriteRawVarInt(), which is theint32writer: it encodes a negative int as a 10-byte varint.The zigzag value is negative for every v >= 2^30 and every v < -2^30. For each such value,
writeTo()wrote 5 bytes more than it had reserved. This happens on freshly built messages, with no parsing involved:toByteArray()throwsArrayIndexOutOfBoundsException;writeTo()into a pooled heap buffer writes the extra bytes into the next buffer of the same chunk, because pooled heap buffers share one backing array.Singular, repeated, packed and map
sint32fields are all affected.Example
The current writer:
Change
A new
writeRawVarUInt()writes an unsigned 32-bit varint of at most 5 bytes, for both the byte[] and the NIO sink.writeRawSignedVarInt()uses it:Writes now match
computeSignedVarIntSize(), and the output matches protobuf-java.Testing
NumbersTestandRepeatedNumbersTestcover the values whose zigzag value has its top bit set: 2^30, -2^30 - 1,Integer.MAX_VALUEandInteger.MIN_VALUE. For singular, repeated and packed fields, sizes and bytes match protobuf-java. These tests fail on master. Fullmvn verifypasses.