aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/Jellyfin.Extensions/StreamExtensions.cs32
1 files changed, 20 insertions, 12 deletions
diff --git a/src/Jellyfin.Extensions/StreamExtensions.cs b/src/Jellyfin.Extensions/StreamExtensions.cs
index ed3f6e665d..56a66b885a 100644
--- a/src/Jellyfin.Extensions/StreamExtensions.cs
+++ b/src/Jellyfin.Extensions/StreamExtensions.cs
@@ -73,6 +73,7 @@ namespace Jellyfin.Extensions
/// <param name="path">The file path to compare against.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>True if the stream and file are identical; otherwise false.</returns>
+ /// <exception cref="ArgumentException"><paramref name="stream"/> does not support seeking.</exception>
public static async Task<bool> IsFileIdenticalAsync(this Stream stream, string path, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(stream);
@@ -80,7 +81,7 @@ namespace Jellyfin.Extensions
if (!stream.CanSeek)
{
- return false;
+ throw new ArgumentException("Stream must support seeking.", nameof(stream));
}
var originalPosition = stream.Position;
@@ -113,30 +114,39 @@ namespace Jellyfin.Extensions
/// <param name="b">The second stream to compare.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>True if the streams are identical; otherwise false.</returns>
+ /// <exception cref="ArgumentException"><paramref name="a"/> or <paramref name="b"/> does not support seeking.</exception>
public static async Task<bool> IsStreamIdenticalAsync(this Stream a, Stream b, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(a);
ArgumentNullException.ThrowIfNull(b);
+ if (!a.CanSeek)
+ {
+ throw new ArgumentException("Stream must support seeking.", nameof(a));
+ }
+
+ if (!b.CanSeek)
+ {
+ throw new ArgumentException("Stream must support seeking.", nameof(b));
+ }
+
if (b.Length != a.Length)
{
return false;
}
- // If b is MemoryStream but a is not, swap them to use fast path B
+ // If b is MemoryStream but a is not, swap them to enable fast path B
if (b is MemoryStream && a is not MemoryStream)
{
(a, b) = (b, a);
}
- if (a is MemoryStream ms_a)
+ if (a is MemoryStream streamA && streamA.TryGetBuffer(out var segmentA))
{
- var bufferA = ms_a.GetBuffer();
-
// Fast path A: if both streams are MemoryStreams, compare directly against each other
- if (b is MemoryStream ms_b)
+ if (b is MemoryStream streamB && streamB.TryGetBuffer(out var segmentB))
{
- return bufferA.AsSpan(0, (int)ms_a.Length).SequenceEqual(ms_b.GetBuffer().AsSpan(0, (int)ms_b.Length));
+ return segmentA.AsSpan().SequenceEqual(segmentB.AsSpan());
}
// Fast path B: only first stream is a MemoryStream, compare against second stream chunk-by-chunk
@@ -148,9 +158,7 @@ namespace Jellyfin.Extensions
int bytesRead;
while ((bytesRead = await b.ReadAsync(memoryB, cancellationToken).ConfigureAwait(false)) > 0)
{
- cancellationToken.ThrowIfCancellationRequested();
-
- if (!bufferA.AsSpan(offset, bytesRead).SequenceEqual(bufferB.AsSpan(0, bytesRead)))
+ if (!segmentA.AsSpan(offset, bytesRead).SequenceEqual(memoryB.Span[..bytesRead]))
{
return false;
}
@@ -158,7 +166,7 @@ namespace Jellyfin.Extensions
offset += bytesRead;
}
- return offset == ms_a.Length;
+ return offset == segmentA.Count;
}
finally
{
@@ -190,7 +198,7 @@ namespace Jellyfin.Extensions
return true;
}
- if (!bufferA.AsSpan(0, bytesReadA).SequenceEqual(bufferB.AsSpan(0, bytesReadB)))
+ if (!memoryA.Span[..bytesReadA].SequenceEqual(memoryB.Span[..bytesReadB]))
{
return false;
}