I'm trying to write binary message using PipeWriter
that is prefixed with byte length.
Byte length is unknown to me until whole message is finished.
Can this be done at all with PipeWriter
?
Basically I'm trying to implement those three methods:
void StartMessage(PipeWriter writer)
{
// leave 4 empty bytes for length written at later time
this.header_ = writer.GetMemory(4);
writer.Advance(4);
}
void Write(PipeWriter writer, ReadOnlySpan<byte> span)
{
var buffer = writer.GetSpan(span.Length);
span.CopyTo(buffer);
writer.Advance(span.Length);
this.totalLength_ += span.Length;
}
ValueTask EndMessage(PipeWriter writer)
{
// should prepend this.totalLength_ to the message
BinaryPrimities.WriteUInt32BingEndian(this.header_.Span, this.totalLength_);
// signal end of message
return writer.FlushAsync();
}
This is giving me expected results, but documentation of PipeWrite.Advance
states:
You must request a new buffer after calling System.IO.Pipelines.PipeWriter.Advance(System.Int32) to continue writing more data; you cannot write to a previously acquired buffer.
Reading such message can be easily done using PipeReader
and AdvanceTo(consumed,examined)
method, where we only consume input when whole length of message is available, but I've been unable to come up with PipeWriter
usage that is correct according to documentation.
The pattern you are showing will lead to an use after free. And you will overwrite your output buffer on random parts.
You need to not send the message until all the bytes is present, it mean that you need all the message being in memory before being able to send it.
You can easily achieve this by writing to a MemoryStream then pass the underlying buffer to the PipeWriter later.
Also, mind that PipeWriter.GetMemory can, and will return more bytes than what you asked for.