diff --git a/Library/DiscUtils.Ntfs/Internals/WofStream.cs b/Library/DiscUtils.Ntfs/Internals/WofStream.cs index 41c43b4a6..28d3b6b8a 100644 --- a/Library/DiscUtils.Ntfs/Internals/WofStream.cs +++ b/Library/DiscUtils.Ntfs/Internals/WofStream.cs @@ -146,7 +146,7 @@ public override int Read(Span buffer) { if ((Position & (maxChunkSize - 1)) != 0) { - throw new ArgumentOutOfRangeException("Read not aligned to compression chunks"); + throw new InvalidOperationException("Read not aligned to compression chunks"); } var total = 0; @@ -175,7 +175,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation { if ((Position & (maxChunkSize - 1)) != 0) { - throw new ArgumentOutOfRangeException("Read not aligned to compression chunks"); + throw new InvalidOperationException("Read not aligned to compression chunks"); } var total = 0; @@ -204,7 +204,7 @@ public override int Read(byte[] buffer, int offset, int count) { if ((Position & (maxChunkSize - 1)) != 0) { - throw new ArgumentOutOfRangeException("Read not aligned to compression chunks"); + throw new InvalidOperationException("Read not aligned to compression chunks"); } var total = 0; diff --git a/Library/DiscUtils.Streams/Block/BlockCacheStream.cs b/Library/DiscUtils.Streams/Block/BlockCacheStream.cs index a05b9b667..bf6aa572e 100644 --- a/Library/DiscUtils.Streams/Block/BlockCacheStream.cs +++ b/Library/DiscUtils.Streams/Block/BlockCacheStream.cs @@ -822,10 +822,14 @@ protected override void Dispose(bool disposing) private void CheckDisposed() { +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(_wrappedStream is null, this); +#else if (_wrappedStream == null) { throw new ObjectDisposedException("BlockCacheStream"); } +#endif } private void InvalidateBlocks(long firstBlock, int numBlocks) diff --git a/Library/DiscUtils.Streams/ConcatStream.cs b/Library/DiscUtils.Streams/ConcatStream.cs index 57ddbdf1a..548721a42 100644 --- a/Library/DiscUtils.Streams/ConcatStream.cs +++ b/Library/DiscUtils.Streams/ConcatStream.cs @@ -82,25 +82,38 @@ public override bool CanWrite } } + public override long? GetPositionInBaseStream(Stream baseStream, long virtualPosition) + { + if (ReferenceEquals(baseStream, this)) + { + return virtualPosition; + } + + var activeStreamIndex = GetStream(virtualPosition, out var activeStreamStartPos); + + var activeStream = _streams[activeStreamIndex]; + + var basePosition = virtualPosition - activeStreamStartPos; + + return activeStream.GetPositionInBaseStream(baseStream, basePosition); + } + public override IEnumerable Extents { get { CheckDisposed(); - var extents = new List(); long pos = 0; for (var i = 0; i < _streams.Count; ++i) { foreach (var extent in _streams[i].Extents) { - extents.Add(new StreamExtent(extent.Start + pos, extent.Length)); + yield return new StreamExtent(extent.Start + pos, extent.Length); } pos += _streams[i].Length; } - - return extents; } } @@ -388,9 +401,13 @@ private int GetStream(long targetPos, out long streamStartPos) private void CheckDisposed() { +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(_streams is null, this); +#else if (_streams == null) { throw new ObjectDisposedException("ConcatStream"); } +#endif } } \ No newline at end of file diff --git a/Library/DiscUtils.Streams/MirrorStream.cs b/Library/DiscUtils.Streams/MirrorStream.cs index ffcd41870..6dc07425e 100644 --- a/Library/DiscUtils.Streams/MirrorStream.cs +++ b/Library/DiscUtils.Streams/MirrorStream.cs @@ -20,6 +20,7 @@ // DEALINGS IN THE SOFTWARE. // +using DiscUtils.Streams.Compatibility; using System; using System.Collections.Generic; using System.IO; @@ -63,6 +64,16 @@ public MirrorStream(Ownership ownsWrapped, IEnumerable wrapped) } } + public override long? GetPositionInBaseStream(Stream baseStream, long virtualPosition) + { + if (ReferenceEquals(baseStream, this)) + { + return virtualPosition; + } + + return _wrapped[0].GetPositionInBaseStream(baseStream, virtualPosition); + } + public override bool CanRead => _canRead; public override bool CanSeek => _canSeek; diff --git a/Library/DiscUtils.Streams/SnapshotStream.cs b/Library/DiscUtils.Streams/SnapshotStream.cs index 53e1285e4..57856104b 100644 --- a/Library/DiscUtils.Streams/SnapshotStream.cs +++ b/Library/DiscUtils.Streams/SnapshotStream.cs @@ -77,6 +77,17 @@ public SnapshotStream(Stream baseStream, Ownership owns) _diffExtents = []; } + public override long? GetPositionInBaseStream(Stream baseStream, long virtualPosition) + { + if (ReferenceEquals(baseStream, this) + || _baseStream is not CompatibilityStream baseCompatStream) + { + return virtualPosition; + } + + return baseCompatStream.GetPositionInBaseStream(baseStream, virtualPosition); + } + /// /// Gets an indication as to whether the stream can be read. /// diff --git a/Library/DiscUtils.Streams/SparseStream.cs b/Library/DiscUtils.Streams/SparseStream.cs index eb1f5cabf..389ad1063 100644 --- a/Library/DiscUtils.Streams/SparseStream.cs +++ b/Library/DiscUtils.Streams/SparseStream.cs @@ -178,6 +178,16 @@ public SparseReadOnlyWrapperStream(SparseStream wrapped, Ownership ownsWrapped) _ownsWrapped = ownsWrapped; } + public override long? GetPositionInBaseStream(Stream baseStream, long virtualPosition) + { + if (ReferenceEquals(baseStream, this)) + { + return virtualPosition; + } + + return _wrapped.GetPositionInBaseStream(baseStream, virtualPosition); + } + public override bool CanRead { get @@ -331,6 +341,16 @@ public SparseWrapperStream(Stream wrapped, Ownership ownsWrapped, IEnumerable _canRead; public override bool CanSeek => true; @@ -116,7 +136,7 @@ public override int Read(byte[] buffer, int offset, int count) var streamIdx = (int)(stripe % _wrapped.Count); var streamStripe = stripe / _wrapped.Count; - Stream targetStream = _wrapped[streamIdx]; + var targetStream = _wrapped[streamIdx]; targetStream.Position = streamStripe * _stripeSize + stripeOffset; var numRead = targetStream.Read(buffer, offset + totalRead, stripeToRead); @@ -146,7 +166,7 @@ public override async ValueTask ReadAsync(Memory buffer, Cancellation var streamIdx = (int)(stripe % _wrapped.Count); var streamStripe = stripe / _wrapped.Count; - Stream targetStream = _wrapped[streamIdx]; + var targetStream = _wrapped[streamIdx]; targetStream.Position = streamStripe * _stripeSize + stripeOffset; var numRead = await targetStream.ReadAsync(buffer.Slice(totalRead, stripeToRead), cancellationToken).ConfigureAwait(false); @@ -238,7 +258,7 @@ public override void Write(byte[] buffer, int offset, int count) var streamIdx = (int)(stripe % _wrapped.Count); var streamStripe = stripe / _wrapped.Count; - Stream targetStream = _wrapped[streamIdx]; + var targetStream = _wrapped[streamIdx]; targetStream.Position = streamStripe * _stripeSize + stripeOffset; targetStream.Write(buffer, offset + totalWritten, stripeToWrite); @@ -269,7 +289,7 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella var streamIdx = (int)(stripe % _wrapped.Count); var streamStripe = stripe / _wrapped.Count; - Stream targetStream = _wrapped[streamIdx]; + var targetStream = _wrapped[streamIdx]; targetStream.Position = streamStripe * _stripeSize + stripeOffset; await targetStream.WriteAsync(buffer.Slice(totalWritten, stripeToWrite), cancellationToken).ConfigureAwait(false); @@ -300,7 +320,7 @@ public override void Write(ReadOnlySpan buffer) var streamIdx = (int)(stripe % _wrapped.Count); var streamStripe = stripe / _wrapped.Count; - Stream targetStream = _wrapped[streamIdx]; + var targetStream = _wrapped[streamIdx]; targetStream.Position = streamStripe * _stripeSize + stripeOffset; targetStream.Write(buffer.Slice(totalWritten, stripeToWrite)); diff --git a/Library/DiscUtils.Streams/SubStream.cs b/Library/DiscUtils.Streams/SubStream.cs index 4b37691ac..ce4ca6c7f 100644 --- a/Library/DiscUtils.Streams/SubStream.cs +++ b/Library/DiscUtils.Streams/SubStream.cs @@ -85,6 +85,21 @@ public override IEnumerable Extents } } + public override long? GetPositionInBaseStream(Stream baseStream, long virtualPosition) + { + if (ReferenceEquals(baseStream, this)) + { + return virtualPosition; + } + + if (_parent is CompatibilityStream baseCompatStream) + { + return baseCompatStream.GetPositionInBaseStream(baseStream, _first + virtualPosition); + } + + return _first + virtualPosition; + } + public override long Length => _length; public override long Position diff --git a/Library/DiscUtils.Streams/ThreadSafeStream.cs b/Library/DiscUtils.Streams/ThreadSafeStream.cs index f3ec8d6e4..e6ad7924d 100644 --- a/Library/DiscUtils.Streams/ThreadSafeStream.cs +++ b/Library/DiscUtils.Streams/ThreadSafeStream.cs @@ -20,6 +20,7 @@ // DEALINGS IN THE SOFTWARE. // +using DiscUtils.Streams.Compatibility; using System; using System.Collections.Generic; using System.IO; @@ -87,10 +88,26 @@ public ThreadSafeStream(SparseStream toWrap, Ownership ownership) private ThreadSafeStream(ThreadSafeStream toClone) { _common = toClone._common; + +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(_common is null, toClone); +#else if (_common == null) { throw new ObjectDisposedException("toClone"); } +#endif + } + + public override long? GetPositionInBaseStream(Stream baseStream, long virtualPosition) + { + if (ReferenceEquals(baseStream, this) + || _common.WrappedStream is not CompatibilityStream baseCompatStream) + { + return virtualPosition; + } + + return baseCompatStream.GetPositionInBaseStream(baseStream, virtualPosition); } /// @@ -169,8 +186,12 @@ private SparseStream Wrapped { get { - var wrapped = _common.WrappedStream ?? throw new ObjectDisposedException("ThreadSafeStream"); - return wrapped; +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(_common.WrappedStream is null, this); + return _common.WrappedStream; +#else + return _common.WrappedStream ?? throw new ObjectDisposedException("ThreadSafeStream"); +#endif } } diff --git a/Library/DiscUtils.Streams/Util/CompatExtensions.cs b/Library/DiscUtils.Streams/Util/CompatExtensions.cs index 8b158f2ba..1925c5d72 100644 --- a/Library/DiscUtils.Streams/Util/CompatExtensions.cs +++ b/Library/DiscUtils.Streams/Util/CompatExtensions.cs @@ -13,6 +13,21 @@ namespace DiscUtils.Streams.Compatibility; public abstract class CompatibilityStream : Stream { + /// + /// In a derived class, get corresponding position in a base stream of this instance + /// to a position in this instance. + /// + public virtual long? GetPositionInBaseStream(Stream baseStream, long virtualPosition) + { + if (baseStream is null + || ReferenceEquals(baseStream, this)) + { + return virtualPosition; + } + + return null; + } + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { #if NET6_0_OR_GREATER @@ -241,5 +256,29 @@ public static void AppendData(this IncrementalHash hash, ReadOnlySpan data public static Task CopyToAsync(this Stream source, Stream target, CancellationToken cancellationToken) => source.CopyToAsync(target, bufferSize: 80 * 1024, cancellationToken); #endif + + /// + /// If stream is derived from CompatibilityStream, get corresponding position in a base stream of this instance + /// to a position in this instance. + /// + public static long? GetPositionInBaseStream(this Stream stream, Stream baseStream, long virtualPosition) + { + if (ReferenceEquals(stream, baseStream)) + { + return virtualPosition; + } + + if (stream is CompatibilityStream compatBaseStream) + { + return compatBaseStream.GetPositionInBaseStream(baseStream, virtualPosition); + } + + if (baseStream is null) + { + return virtualPosition; + } + + return null; + } } diff --git a/Library/DiscUtils.Streams/WrappingMappedStream.cs b/Library/DiscUtils.Streams/WrappingMappedStream.cs index f1f1ba7d0..9d4aff1ad 100644 --- a/Library/DiscUtils.Streams/WrappingMappedStream.cs +++ b/Library/DiscUtils.Streams/WrappingMappedStream.cs @@ -54,6 +54,17 @@ public WrappingMappedStream(T toWrap, Ownership ownership, IEnumerable WrappedStream.CanRead; public override bool CanSeek => WrappedStream.CanSeek; diff --git a/Library/DiscUtils.Streams/WrappingStream.cs b/Library/DiscUtils.Streams/WrappingStream.cs index 731000406..5efd25bde 100644 --- a/Library/DiscUtils.Streams/WrappingStream.cs +++ b/Library/DiscUtils.Streams/WrappingStream.cs @@ -46,6 +46,16 @@ public WrappingStream(SparseStream toWrap, Ownership ownership) _ownership = ownership; } + public override long? GetPositionInBaseStream(Stream baseStream, long virtualPosition) + { + if (ReferenceEquals(baseStream, this)) + { + return virtualPosition; + } + + return _wrapped.GetPositionInBaseStream(baseStream, virtualPosition); + } + public override bool CanRead => _wrapped.CanRead; public override bool CanSeek => _wrapped.CanSeek; diff --git a/Library/DiscUtils.Vmdk/CommonSparseExtentStream.cs b/Library/DiscUtils.Vmdk/CommonSparseExtentStream.cs index c4026e154..2ec8a80aa 100644 --- a/Library/DiscUtils.Vmdk/CommonSparseExtentStream.cs +++ b/Library/DiscUtils.Vmdk/CommonSparseExtentStream.cs @@ -589,10 +589,14 @@ protected async ValueTask LoadGrainTableAsync(int index, CancellationToken protected void CheckDisposed() { +#if NET7_0_OR_GREATER + ObjectDisposedException.ThrowIf(_fileStream is null, this); +#else if (_fileStream == null) { throw new ObjectDisposedException("CommonSparseExtentStream"); } +#endif } private IEnumerable LayerExtents(long start, long count)