从技术上讲,protobuf 字段可能是无序的,但在大多数情况下(包括您展示的那个),我们可以合理地假设这些字段是有序的(在这里让它们无序的唯一方法是单独序列化两个半类并连接结果,这在 protobuf 规范中在技术上是有效的)。
所以; 我们将拥有的是:
- 一个 varint 表示:字段 1,字符串 - 始终为十进制
10
- 表示“a”的 varint,标头的长度
- "a" 字节,UTF-8 编码的标头
- 一个 varint 表示:字段 2,字符串 - 始终为十进制
18
- 一个 varint 表示“b”,主体的长度
- “b”字节,正文
我们大概可以假设“a”是>= 0
and < int.MaxValue
- 这意味着它最多需要 5 个字节来编码;因此,如果您缓冲至少6 个字节,您将有足够的信息来了解标头的大小。当然,从技术上讲,它也可以包含身体的一部分,所以你需要抓住它!但是,如果您有一个 sync-over-async ,您可以通过以下方式仅Stream
读取流的那一部分:
int protoHeader = ProtoReader.DirectReadVarintInt32(stream); // 10
int headerLength = ProtoReader.DirectReadVarintInt32(stream);
string header = ProtoReader.DirectReadString(stream, headerLength);
或者如果“异步同步”很棘手,明确阅读:
static byte[] ReadAtLeast6()
{
return new byte[] { 0x0A, 0x0B, 0x68, 0x65, 0x6C, 0x6C, 0x6F };
}
static byte[] ReadMore(int bytes)
{
return new byte[] { 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64 };
}
static void Main()
{
// pretend we read 7 bytes async
var data = ReadAtLeast6();
using (var ms = new MemoryStream())
{
ms.Write(data, 0, data.Length);
ms.Position = 0;
int protoHeader = ProtoReader.DirectReadVarintInt32(ms); // 10
int headerLength = ProtoReader.DirectReadVarintInt32(ms); // 11
int needed = (headerLength + (int)ms.Position) - data.Length; // 6 more
var pos = ms.Position;
ms.Seek(0, SeekOrigin.End);
data = ReadMore(needed);
ms.Write(data, 0, needed);
ms.Position = pos;
string header = ProtoReader.DirectReadString(ms, headerLength);
}
}