Encoding a uint32
requires two varints, each of which is at least one byte long. The first byte contains the encoding ("wire") type and the tag number, and will be one byte long if the tag is at most 15. The second varint is the integer itself, which will be one byte long if the integer is at most 127. An omitted ("defaulted") optional field does not occupy any bytes at all; omitted fields are not encoded in any way on the wire; their absence must be deduced.
Consequently, If an optional uint32
field is omitted, the wire encoding will be at least two bytes shorter than the same protobuf with the optional field included.
You don't indicate how you are computing "the length of 'out'". The only correct way is to call data__get_packed_size
prior to packing the data
object. I'm guessing, however, that you are using strlen(out)
, which will not produce the correct result. strlen
can only be used on strings; more specifically, it can only be used on NUL-terminated strings
, because it stops counting when it hits a NUL
(0) byte, and produces undefined behaviour if it does not see a NUL
byte. You must not use strlen
on arbitrary binary data.
In the protobuf encoding, an explicit uint32
with a tag of 3 and a value of 0 will be encoded as 0x18 0x00
, while an explicit uint32
with a tag of 3 and a value of 42 will be encoded as 0x18 0x2A
. If there were no NULs earlier in the data encoding, then the second byte of the encoding of 0
will terminate strlen
's count. Under certain ideal circumstances (say, the buffer is more than long enough, and is cleared to all NUL
s prior to packing the message into it), strlen
will report the length of the protobuf with 0
as one "character" shorter than the protobuf with 42
, because the NUL
in the encoding of the 0
occurs at the end of the message, causing strlen
to stop counting exactly one byte early.
That's an explanation, not a workaround. DO NOT use strlen
on things which are not NUL-terminated strings.