Created
March 10, 2023 18:12
-
-
Save AyrA/b709e5fcc6a4175063a2aea26e48f301 to your computer and use it in GitHub Desktop.
Stream that is a window of a bigger stream. Allows to only give parts of a stream to a component
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
namespace AyrA.IO | |
{ | |
public class WindowedStream : Stream | |
{ | |
public override bool CanRead => baseStream.CanRead; | |
public override bool CanSeek => baseStream.CanSeek; | |
public override bool CanWrite => false; | |
public override long Length => length; | |
public override long Position | |
{ | |
get => baseOffset; | |
set => Seek(value, SeekOrigin.Begin); | |
} | |
private readonly Stream baseStream; | |
private long length; | |
private readonly bool keepOpen; | |
private long baseOffset = 0; | |
public WindowedStream(Stream baseStream, long length, bool keepOpen = false) | |
{ | |
this.baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream)); | |
if (length < 0) | |
{ | |
throw new ArgumentOutOfRangeException(nameof(length), "Length cannot be negative"); | |
} | |
//Validate length if it's possible | |
try | |
{ | |
if (length > baseStream.Length - baseStream.Position) | |
{ | |
throw new ArgumentOutOfRangeException(nameof(length), "Length exceeds remaining data in base stream"); | |
} | |
} | |
catch | |
{ | |
//NOOP | |
} | |
this.length = length; | |
this.keepOpen = keepOpen; | |
} | |
public override void Flush() | |
{ | |
throw new NotSupportedException("Base stream is not writable"); | |
} | |
public override int Read(byte[] buffer, int offset, int count) | |
{ | |
var maxRead = (int)Math.Min(int.MaxValue, length - baseOffset); | |
int read = baseStream.Read(buffer, offset, Math.Min(maxRead, count)); | |
baseOffset += read; | |
return read; | |
} | |
public override long Seek(long offset, SeekOrigin origin) | |
{ | |
if (!CanSeek) | |
{ | |
throw new NotSupportedException("Base stream is not seekable"); | |
} | |
var pos = origin switch | |
{ | |
SeekOrigin.Begin => offset, | |
SeekOrigin.Current => baseOffset + offset, | |
SeekOrigin.End => length + offset, | |
_ => throw new ArgumentOutOfRangeException(nameof(origin)), | |
}; | |
//Pos now contains the new absolute position | |
//We check if this is still inside of the permitted window | |
if (pos < 0 || pos > length) | |
{ | |
throw new ArgumentOutOfRangeException(nameof(offset), "New position is outside of the possible range"); | |
} | |
baseStream.Seek(pos - baseOffset, SeekOrigin.Current); | |
return baseOffset = pos; | |
} | |
public override void SetLength(long value) | |
{ | |
if (baseOffset > value) | |
{ | |
throw new ArgumentOutOfRangeException(nameof(baseOffset), "Cannot reduce length beyond current position"); | |
} | |
length = value; | |
} | |
public override void Write(byte[] buffer, int offset, int count) | |
{ | |
throw new NotSupportedException("Base stream is not writable"); | |
} | |
protected override void Dispose(bool disposing) | |
{ | |
base.Dispose(disposing); | |
if (!keepOpen) | |
{ | |
baseStream.Dispose(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment