Andrew Shepherd
Andrew Shepherd

Reputation: 45272

Serializing/Deserializing a standalone integer using protocol buffers

Up to now, I've been using Protocol Buffers to serialize and deserialize objects using the code-generated classes.

Now I am attempting to serialize and deserialize a single 64-bit integer. The problem is, I'm getting different results in Java and C#.

Here's how I'm doing it in Java....

private static byte[] convertLongToByteArray(long value) throws IOException {
    int size = CodedOutputStream.computeInt64SizeNoTag(value);
    byte[] buffer = new byte[size];
    CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(buffer);
    codedOutputStream.writeInt64NoTag(value);
    codedOutputStream.flush();
    codedOutputStream.checkNoSpaceLeft();
    return buffer;
}

And here's how I'm doing this in C#, using protobuf.net:

public void SerializeLongValue()
{
    long n = 9876;
    byte[] memoryBuffer = null;
    using (MemoryStream destination = new MemoryStream())
    {
        ProtoBuf.Serializer.Serialize(destination, n);
        destination.Flush();
        memoryBuffer = destination.ToArray();
    }

    using (MemoryStream source = new MemoryStream(memoryBuffer))
    {
        long result = ProtoBuf.Serializer.Deserialize<long>(source);
        Assert.AreEqual(n, result);
    }
}

The java code converted the number 9876 to [0x94, 0x4D]

The C# code converted the number 9876 to [0x08, 0x94, 0x4D]

How do I do this so that both com.google.protobuf and protobuf.net produce identical outputs?

Upvotes: 3

Views: 1220

Answers (1)

Andrew Shepherd
Andrew Shepherd

Reputation: 45272

The protobuf.net method ProtoBuf.Serializer.Serialize forces a field header (field number=1) into the stream. That's the only way you can perform a serialization; this method invokes a number of internal methods which are not publicly available.

The solution I'm using is to change the Java code to also include a field header.

Here is my new Java code.

private static byte[] convertLongToByteArray(long value) throws IOException {
    int size = CodedOutputStream.computeTagSize(1) + CodedOutputStream.computeInt64SizeNoTag(value);
    byte[] buffer = new byte[size];
    CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(buffer);
    codedOutputStream.writeInt64(1, value);
    codedOutputStream.flush();
    codedOutputStream.checkNoSpaceLeft();
    return buffer;
}

public static long convertByteArrayToLong(byte[] byteArray) throws IOException {
    CodedInputStream codedInputStream = CodedInputStream.newInstance(byteArray);
    codedInputStream.readTag();
    return codedInputStream.readInt64();
}

The changes I've made are:

  • When computing the required buffer size, include a tag size
  • Instead of CodedOutputStream.WriteInt64NoTag, call CodedOutputStream.WriteInt64
  • When reading back, call CodedOutputStream.ReadTag before calling CodedOutputStream.ReadInt64

Upvotes: 2

Related Questions