技術的には protobuf フィールドは順不同になる可能性がありますが、ほとんどの場合 (表示されているものを含む)、フィールドが順不同であると合理的に想定できます (ここでそれらを順不同にする唯一の方法は、個別にシリアル化することです)これは protobuf 仕様で技術的に有効です)。
そう; 私たちが持つものは次のとおりです。
- varint を示す: フィールド 1、文字列 - 常に 10 進数
10
- ヘッダーの長さ「a」を表す varint
- 「a」バイト、UTF-8 でエンコードされたヘッダー
- varint を示す: フィールド 2、文字列 - 常に 10 進数
18
- 本体の長さ「b」を表す varint
- "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);
}
}