Skip to content

Commit

Permalink
Merge pull request #816 from dotnet/fix797
Browse files Browse the repository at this point in the history
Fix `Stream.Seek` implementations to reliably shift position as required
  • Loading branch information
AArnott committed Sep 20, 2022
2 parents 4b38f20 + 292694b commit 4a63c66
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 16 deletions.
Expand Up @@ -135,11 +135,7 @@ public override long Seek(long offset, SeekOrigin origin)
if (origin == SeekOrigin.Begin && offset > this.position)
{
// We can optimise this by skipping over instructions rather than executing them
int length = (int)(offset - this.position);

byte[] buffer = ArrayPool<byte>.Shared.Rent(length);
this.Read(buffer, 0, length);
ArrayPool<byte>.Shared.Return(buffer);
this.ReadExactly(checked((int)(offset - this.position)));
return this.position;
}
else
Expand Down
Expand Up @@ -73,13 +73,9 @@ public override long Seek(long offset, SeekOrigin origin)

if (offset > this.cacheStream.Length)
{
var toRead = (int)(offset - this.cacheStream.Length);
byte[] buffer = ArrayPool<byte>.Shared.Rent(toRead);
int read = this.stream.Read(buffer, 0, toRead);
this.cacheStream.Seek(0, SeekOrigin.End);
this.cacheStream.Write(buffer, 0, read);
ArrayPool<byte>.Shared.Return(buffer);

int toRead = (int)(offset - this.cacheStream.Length);
this.stream.ReadExactly(toRead, this.cacheStream);
this.DisposeStreamIfRead();
return this.cacheStream.Position;
}
Expand Down
27 changes: 27 additions & 0 deletions src/NerdBank.GitVersioning/ManagedGit/StreamExtensions.cs
Expand Up @@ -150,5 +150,32 @@ public static void Write(this Stream stream, Span<byte> span)
return true;
}
#endif

/// <summary>
/// Reads the specified number of bytes from a stream, or until the end of the stream.
/// </summary>
/// <param name="readFrom">The stream to read from.</param>
/// <param name="length">The number of bytes to be read.</param>
/// <param name="copyTo">The stream to copy the read bytes to, if required.</param>
/// <returns>The number of bytes actually read. This will be less than <paramref name="length"/> only if the end of <paramref name="readFrom"/> is reached.</returns>
internal static int ReadExactly(this Stream readFrom, int length, Stream? copyTo = null)
{
int bytesRemaining = length;
byte[] buffer = ArrayPool<byte>.Shared.Rent(Math.Min(50 * 1024, bytesRemaining));
while (bytesRemaining > 0)
{
int read = readFrom.Read(buffer, 0, Math.Min(buffer.Length, bytesRemaining));
if (read == 0)
{
break;
}

copyTo?.Write(buffer, 0, read);
bytesRemaining -= read;
}

ArrayPool<byte>.Shared.Return(buffer);
return length - bytesRemaining;
}
}
}
6 changes: 1 addition & 5 deletions src/NerdBank.GitVersioning/ManagedGit/ZLibStream.cs
Expand Up @@ -151,11 +151,7 @@ public override long Seek(long offset, SeekOrigin origin)
if (origin == SeekOrigin.Begin && offset > this.position)
{
// We may be able to optimize this by skipping over the compressed data
int length = (int)(offset - this.position);

byte[] buffer = ArrayPool<byte>.Shared.Rent(length);
this.Read(buffer, 0, length);
ArrayPool<byte>.Shared.Return(buffer);
this.ReadExactly(checked((int)(offset - this.position)));
return this.position;
}
else
Expand Down

0 comments on commit 4a63c66

Please sign in to comment.