From a091788fa4b6bcb88fb6b10b2ba7c8b2832e2a7e Mon Sep 17 00:00:00 2001 From: David Schulte Date: Wed, 15 Jun 2022 15:45:01 +0200 Subject: [PATCH] Fixed an issue with .NET 6 where `TagReader` only partially reads the stream because the returned value of `Read` was ignored. --- SharpNBT/TagReader.cs | 63 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 11 deletions(-) diff --git a/SharpNBT/TagReader.cs b/SharpNBT/TagReader.cs index 04ce9fe..42366d2 100644 --- a/SharpNBT/TagReader.cs +++ b/SharpNBT/TagReader.cs @@ -70,7 +70,7 @@ namespace SharpNBT else { Span buffer = stackalloc byte[sizeof(short)]; - BaseStream.Read(buffer); + ReadToFixSizedBuffer(buffer); value = BitConverter.ToInt16(buffer); if (SwapEndian) value = value.SwapEndian(); @@ -109,7 +109,7 @@ namespace SharpNBT else { Span buffer = stackalloc byte[sizeof(long)]; - BaseStream.Read(buffer); + ReadToFixSizedBuffer(buffer); value = BitConverter.ToInt64(buffer); if (SwapEndian) value = value.SwapEndian(); @@ -129,7 +129,7 @@ namespace SharpNBT var name = named ? ReadUTF8String() : null; var buffer = new byte[sizeof(float)]; - BaseStream.Read(buffer, 0, sizeof(float)); + ReadToFixSizedBuffer(buffer, 0, sizeof(float)); if (SwapEndian) Array.Reverse(buffer); @@ -146,7 +146,7 @@ namespace SharpNBT { var name = named ? ReadUTF8String() : null; var buffer = new byte[sizeof(double)]; - BaseStream.Read(buffer, 0, buffer.Length); + ReadToFixSizedBuffer(buffer, 0, buffer.Length); if (SwapEndian) Array.Reverse(buffer); @@ -177,7 +177,7 @@ namespace SharpNBT var name = named ? ReadUTF8String() : null; var count = ReadCount(); var buffer = new byte[count]; - BaseStream.Read(buffer, 0, count); + ReadToFixSizedBuffer(buffer, 0, count); return new ByteArrayTag(name, buffer); } @@ -203,7 +203,7 @@ namespace SharpNBT } var buffer = new byte[count * INT_SIZE]; - BaseStream.Read(buffer, 0, count * INT_SIZE); + ReadToFixSizedBuffer(buffer, 0, count * INT_SIZE); Span values = MemoryMarshal.Cast(buffer); if (SwapEndian) @@ -236,7 +236,7 @@ namespace SharpNBT } var buffer = new byte[count * LONG_SIZE]; - BaseStream.Read(buffer, 0, count * LONG_SIZE); + ReadToFixSizedBuffer(buffer, 0, count * LONG_SIZE); Span values = MemoryMarshal.Cast(buffer); if (SwapEndian) @@ -397,7 +397,7 @@ namespace SharpNBT else { Span buffer = stackalloc byte[sizeof(ushort)]; - BaseStream.Read(buffer); + ReadToFixSizedBuffer(buffer); var uint16 = BitConverter.ToUInt16(buffer); length = SwapEndian ? uint16.SwapEndian() : uint16; } @@ -406,7 +406,7 @@ namespace SharpNBT return null; var utf8 = new byte[length]; - BaseStream.Read(utf8, 0, length); + ReadToFixSizedBuffer(utf8, 0, length); return Encoding.UTF8.GetString(utf8); } @@ -419,10 +419,52 @@ namespace SharpNBT private int ReadInt32() { Span buffer = stackalloc byte[sizeof(int)]; - BaseStream.Read(buffer); + ReadToFixSizedBuffer(buffer); var value = BitConverter.ToInt32(buffer); return SwapEndian ? value.SwapEndian() : value; } + + /// + /// Reads bytes from the streams and stores them into the . + /// The number of read bytes is dictated by the size of the buffer. + /// This method ensures that all requested bytes are read. + /// + /// + /// Use this instead of BaseStream.Read(buffer). + /// There was a breaking change in .NET 6 where the can read less bytes than requested for certain streams. + /// Read more here: https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams + /// + /// The buffer where the read bytes are written to. the buffer size defines the number of bytes to read. + /// Throws if no more bytes could be read from the stream, but the buffer wasn't completely filled yet. + protected void ReadToFixSizedBuffer(Span buffer) + { + var totalBytes = 0; + while (totalBytes < buffer.Length) + { + var readBytes = BaseStream.Read(buffer.Slice(totalBytes)); + if (readBytes == 0) + throw new EndOfStreamException(); + totalBytes += readBytes; + } + } + + /// + /// Reads bytes from the streams and stores them into the . + /// This method ensures that all requested bytes are read. + /// + /// + /// Use this instead of BaseStream.Read(buffer, offset, count). + /// There was a breaking change in .NET 6 where the can read less bytes than requested for certain streams. + /// Read more here: https://docs.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/partial-byte-reads-in-streams + /// + /// The buffer where the read bytes are written to. The data will be stored starting at to + - 1. + /// The offset in where the read data is stored. + /// The number of bytes to read. Must be positive. + /// Throws if no more bytes could be read from the stream, but the buffer wasn't completely filled yet. + protected void ReadToFixSizedBuffer(byte[] buffer, int offset, int count) + { + ReadToFixSizedBuffer(new Span(buffer, offset, count)); + } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. @@ -465,6 +507,5 @@ namespace SharpNBT TagEncountered.Invoke(this, args); return args.Handled ? args.Result : null; } - } } \ No newline at end of file