Skip to content

Commit 4a63c66

Browse files
authored
Merge pull request #816 from dotnet/fix797
Fix `Stream.Seek` implementations to reliably shift position as required
2 parents 4b38f20 + 292694b commit 4a63c66

File tree

4 files changed

+31
-16
lines changed

4 files changed

+31
-16
lines changed

src/NerdBank.GitVersioning/ManagedGit/GitPackDeltafiedStream.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,7 @@ public override long Seek(long offset, SeekOrigin origin)
135135
if (origin == SeekOrigin.Begin && offset > this.position)
136136
{
137137
// We can optimise this by skipping over instructions rather than executing them
138-
int length = (int)(offset - this.position);
139-
140-
byte[] buffer = ArrayPool<byte>.Shared.Rent(length);
141-
this.Read(buffer, 0, length);
142-
ArrayPool<byte>.Shared.Return(buffer);
138+
this.ReadExactly(checked((int)(offset - this.position)));
143139
return this.position;
144140
}
145141
else

src/NerdBank.GitVersioning/ManagedGit/GitPackMemoryCacheStream.cs

+2-6
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,9 @@ public override long Seek(long offset, SeekOrigin origin)
7373

7474
if (offset > this.cacheStream.Length)
7575
{
76-
var toRead = (int)(offset - this.cacheStream.Length);
77-
byte[] buffer = ArrayPool<byte>.Shared.Rent(toRead);
78-
int read = this.stream.Read(buffer, 0, toRead);
7976
this.cacheStream.Seek(0, SeekOrigin.End);
80-
this.cacheStream.Write(buffer, 0, read);
81-
ArrayPool<byte>.Shared.Return(buffer);
82-
77+
int toRead = (int)(offset - this.cacheStream.Length);
78+
this.stream.ReadExactly(toRead, this.cacheStream);
8379
this.DisposeStreamIfRead();
8480
return this.cacheStream.Position;
8581
}

src/NerdBank.GitVersioning/ManagedGit/StreamExtensions.cs

+27
Original file line numberDiff line numberDiff line change
@@ -150,5 +150,32 @@ internal static bool TryAdd<TKey, TValue>(this System.Collections.Generic.IDicti
150150
return true;
151151
}
152152
#endif
153+
154+
/// <summary>
155+
/// Reads the specified number of bytes from a stream, or until the end of the stream.
156+
/// </summary>
157+
/// <param name="readFrom">The stream to read from.</param>
158+
/// <param name="length">The number of bytes to be read.</param>
159+
/// <param name="copyTo">The stream to copy the read bytes to, if required.</param>
160+
/// <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>
161+
internal static int ReadExactly(this Stream readFrom, int length, Stream? copyTo = null)
162+
{
163+
int bytesRemaining = length;
164+
byte[] buffer = ArrayPool<byte>.Shared.Rent(Math.Min(50 * 1024, bytesRemaining));
165+
while (bytesRemaining > 0)
166+
{
167+
int read = readFrom.Read(buffer, 0, Math.Min(buffer.Length, bytesRemaining));
168+
if (read == 0)
169+
{
170+
break;
171+
}
172+
173+
copyTo?.Write(buffer, 0, read);
174+
bytesRemaining -= read;
175+
}
176+
177+
ArrayPool<byte>.Shared.Return(buffer);
178+
return length - bytesRemaining;
179+
}
153180
}
154181
}

src/NerdBank.GitVersioning/ManagedGit/ZLibStream.cs

+1-5
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,7 @@ public override long Seek(long offset, SeekOrigin origin)
151151
if (origin == SeekOrigin.Begin && offset > this.position)
152152
{
153153
// We may be able to optimize this by skipping over the compressed data
154-
int length = (int)(offset - this.position);
155-
156-
byte[] buffer = ArrayPool<byte>.Shared.Rent(length);
157-
this.Read(buffer, 0, length);
158-
ArrayPool<byte>.Shared.Return(buffer);
154+
this.ReadExactly(checked((int)(offset - this.position)));
159155
return this.position;
160156
}
161157
else

0 commit comments

Comments
 (0)