Skip to content

Commit

Permalink
Merge pull request #4 from Arcus92/master
Browse files Browse the repository at this point in the history
Fixes incomplete stream reads with .NET 6
  • Loading branch information
ForeverZer0 committed Jun 20, 2022
2 parents f27bebf + a091788 commit 44e723f
Showing 1 changed file with 52 additions and 11 deletions.
63 changes: 52 additions & 11 deletions SharpNBT/TagReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public ShortTag ReadShort(bool named = true)
else
{
Span<byte> buffer = stackalloc byte[sizeof(short)];
BaseStream.Read(buffer);
ReadToFixSizedBuffer(buffer);
value = BitConverter.ToInt16(buffer);
if (SwapEndian)
value = value.SwapEndian();
Expand Down Expand Up @@ -109,7 +109,7 @@ public LongTag ReadLong(bool named = true)
else
{
Span<byte> buffer = stackalloc byte[sizeof(long)];
BaseStream.Read(buffer);
ReadToFixSizedBuffer(buffer);
value = BitConverter.ToInt64(buffer);
if (SwapEndian)
value = value.SwapEndian();
Expand All @@ -129,7 +129,7 @@ public FloatTag ReadFloat(bool named = true)
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);

Expand All @@ -146,7 +146,7 @@ public DoubleTag ReadDouble(bool named = true)
{
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);

Expand Down Expand Up @@ -177,7 +177,7 @@ public ByteArrayTag ReadByteArray(bool named = true)
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);
}

Expand All @@ -203,7 +203,7 @@ public IntArrayTag ReadIntArray(bool named = true)
}

var buffer = new byte[count * INT_SIZE];
BaseStream.Read(buffer, 0, count * INT_SIZE);
ReadToFixSizedBuffer(buffer, 0, count * INT_SIZE);

Span<int> values = MemoryMarshal.Cast<byte, int>(buffer);
if (SwapEndian)
Expand Down Expand Up @@ -236,7 +236,7 @@ public LongArrayTag ReadLongArray(bool named = true)
}

var buffer = new byte[count * LONG_SIZE];
BaseStream.Read(buffer, 0, count * LONG_SIZE);
ReadToFixSizedBuffer(buffer, 0, count * LONG_SIZE);

Span<long> values = MemoryMarshal.Cast<byte, long>(buffer);
if (SwapEndian)
Expand Down Expand Up @@ -397,7 +397,7 @@ protected string ReadUTF8String()
else
{
Span<byte> buffer = stackalloc byte[sizeof(ushort)];
BaseStream.Read(buffer);
ReadToFixSizedBuffer(buffer);
var uint16 = BitConverter.ToUInt16(buffer);
length = SwapEndian ? uint16.SwapEndian() : uint16;
}
Expand All @@ -406,7 +406,7 @@ protected string ReadUTF8String()
return null;

var utf8 = new byte[length];
BaseStream.Read(utf8, 0, length);
ReadToFixSizedBuffer(utf8, 0, length);
return Encoding.UTF8.GetString(utf8);
}

Expand All @@ -419,10 +419,52 @@ protected string ReadUTF8String()
private int ReadInt32()
{
Span<byte> buffer = stackalloc byte[sizeof(int)];
BaseStream.Read(buffer);
ReadToFixSizedBuffer(buffer);
var value = BitConverter.ToInt32(buffer);
return SwapEndian ? value.SwapEndian() : value;
}

/// <summary>
/// Reads bytes from the streams and stores them into the <paramref name="buffer"/>.
/// The number of read bytes is dictated by the size of the buffer.
/// This method ensures that all requested bytes are read.
/// </summary>
/// <remarks>
/// Use this instead of <c>BaseStream.Read(buffer)</c>.
/// There was a breaking change in .NET 6 where the <see cref="Stream.Read(byte[],int,int)"/> 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
/// </remarks>
/// <param name="buffer">The buffer where the read bytes are written to. the buffer size defines the number of bytes to read.</param>
/// <exception cref="EndOfStreamException">Throws if no more bytes could be read from the stream, but the buffer wasn't completely filled yet.</exception>
protected void ReadToFixSizedBuffer(Span<byte> buffer)
{
var totalBytes = 0;
while (totalBytes < buffer.Length)
{
var readBytes = BaseStream.Read(buffer.Slice(totalBytes));
if (readBytes == 0)
throw new EndOfStreamException();
totalBytes += readBytes;
}
}

/// <summary>
/// Reads bytes from the streams and stores them into the <paramref name="buffer"/>.
/// This method ensures that all requested bytes are read.
/// </summary>
/// <remarks>
/// Use this instead of <c>BaseStream.Read(buffer, offset, count)</c>.
/// There was a breaking change in .NET 6 where the <see cref="Stream.Read(byte[],int,int)"/> 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
/// </remarks>
/// <param name="buffer">The buffer where the read bytes are written to. The data will be stored starting at <paramref name="offset"/> to <paramref name="offset"/> + <paramref name="count"/> - 1.</param>
/// <param name="offset">The offset in <paramref name="buffer"/> where the read data is stored.</param>
/// <param name="count">The number of bytes to read. Must be positive.</param>
/// <exception cref="EndOfStreamException">Throws if no more bytes could be read from the stream, but the buffer wasn't completely filled yet.</exception>
protected void ReadToFixSizedBuffer(byte[] buffer, int offset, int count)
{
ReadToFixSizedBuffer(new Span<byte>(buffer, offset, count));
}

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
Expand Down Expand Up @@ -465,6 +507,5 @@ protected virtual Tag OnTagEncountered(TagType type, bool named)
TagEncountered.Invoke(this, args);
return args.Handled ? args.Result : null;
}

}
}

0 comments on commit 44e723f

Please sign in to comment.