NBMsgPack036: Async converters should not reuse readers after returning them
Custom converters (classes that derive from MessagePackConverter<T>) that override the ReadAsync method should not reuse a MessagePackReader or MessagePackStreamingReader after returning it via ReturnReader.
Example violation
In the following example, the MessagePackStreamingReader is returned before the method is done using it.
public override async ValueTask<SomeCustomType?> ReadAsync(MessagePackAsyncReader reader, SerializationContext context)
{
MessagePackStreamingReader streamingReader = reader.CreateStreamingReader();
int count;
while (streamingReader.TryReadArrayHeader(out count).NeedsMoreBytes())
{
streamingReader = new(await streamingReader.FetchMoreBytesAsync());
}
if (count != 1)
{
throw new MessagePackSerializationException();
}
reader.ReturnReader(ref streamingReader); // streamingReader returned here
int seedCount;
while (streamingReader.TryRead(out seedCount).NeedsMoreBytes()) // OOPS: streamingReader reused here
{
streamingReader = new(await streamingReader.FetchMoreBytesAsync());
}
return new SomeCustomType(seedCount);
}
Resolution
The fix is simply to move the ReturnReader call to a point after the method is done using the reader.
public override async ValueTask<SomeCustomType?> ReadAsync(MessagePackAsyncReader reader, SerializationContext context)
{
MessagePackStreamingReader streamingReader = reader.CreateStreamingReader();
int count;
while (streamingReader.TryReadArrayHeader(out count).NeedsMoreBytes())
{
streamingReader = new(await streamingReader.FetchMoreBytesAsync());
}
if (count != 1)
{
throw new MessagePackSerializationException();
}
int seedCount;
while (streamingReader.TryRead(out seedCount).NeedsMoreBytes())
{
streamingReader = new(await streamingReader.FetchMoreBytesAsync());
}
reader.ReturnReader(ref streamingReader);
return new SomeCustomType(seedCount);
}