Delimited messages read/write support #2745
-
I work on an Android project that has several protobuf models generated by Google Protobuf plugin. Right now we're almost done with our migration to Wire. Unfortunately, we stumbled upon migration from AFAICS, a long time ago someone had the same problem (#633), but it's hard to understand whether that issue was disregarded or Wire maintainers implemented such functionality. Also, I see an initiative (protocolbuffers/protobuf#10229) to align different libraries on different language to unify the approach to delimited messages and there is no mentions about Wire either. Could you shed a light on this topic and suggest what can we do to complete our migration? Thanks in advance! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 2 replies
This comment has been hidden.
This comment has been hidden.
-
Okay so the code to do this today is a bit annoying, but it's doable. Let's go! First, I haven't thought about length-delimited protos since that comment in 2016 so my memory on how they work is a little hazy. I believe they're quite simply varint32 length-prefixed messages. This is basically the encoding of a nested message on the wire (sans the tag byte). If they're not that, just replace that varint call with whatever the actual type of the length prefix is. Encoding in this format is trivial with our APIs: Buffer buffer = new Buffer();
ProtoWriter writer = new ProtoWriter(buffer);
OneField firstWrite = new OneField.Builder().opt_int32(2).build();
writer.writeVarint32(OneField.ADAPTER.encodedSize(firstWrite));
// System.out.println(buffer.copy().readByteString().hex());
OneField.ADAPTER.encode(writer, firstWrite);
// System.out.println(buffer.copy().readByteString().hex());
OneField secondWrite = new OneField.Builder().opt_int32(Integer.MAX_VALUE).build();
writer.writeVarint32(OneField.ADAPTER.encodedSize(secondWrite));
// System.out.println(buffer.copy().readByteString().hex());
OneField.ADAPTER.encode(writer, secondWrite);
// System.out.println(buffer.copy().readByteString().hex()); Write the size, write the message, repeat. If you are wrapped around a Reading the format can be done today by doing the inverse operation: read the varint, read the message. However, by default, proto message reading will basically loop forever looking for tags (a message is a sequence of tag+value pairs). The only way we know when to stop (such as in a nested message) is when a limit is pushed. The problem is that We have some options on how to fix this... Option 1, which I did not write, is to use a special Option 2, which I did write, is to use a special int firstLength = new ProtoReader(buffer).readVarint32();
// System.out.println(buffer.copy().readByteString().hex());
BufferedSource firstSource = Okio.buffer(new FixedLengthSource(buffer, firstLength));
OneField firstRead = OneField.ADAPTER.decode(firstSource);
// System.out.println(buffer.copy().readByteString().hex());
assertEquals(firstWrite, firstRead);
int secondLength = new ProtoReader(buffer).readVarint32();
// System.out.println(buffer.copy().readByteString().hex());
BufferedSource secondSource = Okio.buffer(new FixedLengthSource(buffer, secondLength));
OneField secondRead = OneField.ADAPTER.decode(secondSource);
// System.out.println(buffer.copy().readByteString().hex());
assertEquals(secondWrite, secondRead); There's two annoying things about this technique:
Option 3, which I sorta wrote, involves exposing an API on ProtoReader reader = new ProtoReader(buffer);
reader.nextDelimited(); // returns length, just because it can.
OneField firstRead = OneField.ADAPTER.decode(reader);
// System.out.println(buffer.copy().readByteString().hex());
assertEquals(firstWrite, firstRead);
reader.nextDelimited();
OneField secondRead = OneField.ADAPTER.decode(reader);
// System.out.println(buffer.copy().readByteString().hex());
assertEquals(secondWrite, secondRead); So no matter what we should probably do some work to make this easier. And we should expose fixed-length sources in Okio because I'm sick of copying the implementation around (you can find one in OkHttp if you need one today). I'm going to re-open the original issue. It shouldn't have been closed as there's very clearly action to take here! Feel free to keep discussing here, or in that issue. |
Beta Was this translation helpful? Give feedback.
Okay so the code to do this today is a bit annoying, but it's doable. Let's go!
First, I haven't thought about length-delimited protos since that comment in 2016 so my memory on how they work is a little hazy. I believe they're quite simply varint32 length-prefixed messages. This is basically the encoding of a nested message on the wire (sans the tag byte). If they're not that, just replace that varint call with whatever the actual type of the length prefix is.
Encoding in this format is trivial with our APIs: