ReFuel.Gltf/ReFuel.Gltf/IO/StreamWrapper.cs
2025-10-12 14:33:16 +03:00

99 lines
3.3 KiB
C#

namespace ReFuel.Gltf.IO
{
public class StreamWrapper : Stream
{
private readonly Stream _proxy;
public Stream BaseStream { get; }
public bool KeepOpen { get; }
public long Offset { get; private init; } = 0;
public long Limit { get; private init; } = -1;
public override bool CanRead => _proxy.CanRead;
public override bool CanSeek => _proxy.CanSeek;
public override bool CanWrite => _proxy.CanWrite;
public override long Length => Limit < 0 ? _proxy.Length : Math.Min(Limit, Length - Offset);
public override long Position
{
get => ToRelOffset(_proxy.Position);
set => _proxy.Position = ToAbsOffset(value);
}
public StreamWrapper(Stream baseStream, bool keepOpen = false)
{
BaseStream = baseStream;
KeepOpen = keepOpen;
_proxy = Synchronized(baseStream);
}
public override void Flush() => _proxy.Flush();
public override int Read(byte[] buffer, int offset, int count) => _proxy.Read(buffer, offset, count);
public override long Seek(long offset, SeekOrigin origin) => _proxy.Seek(offset, origin);
public override void SetLength(long value) => _proxy.SetLength(value);
public override void Write(byte[] buffer, int offset, int count) => _proxy.Write(buffer, offset, count);
public StreamWrapper Fork(long offset = 0, long limit = -1, bool keepOpen = false)
{
if (limit < 0)
limit = Limit;
if (offset < 0 || offset > Length)
throw new ArgumentOutOfRangeException(nameof(offset), "Offset out of range.");
if (limit >= 0 && offset + limit > Length)
throw new ArgumentOutOfRangeException(nameof(limit), "Limit too large for the given offset.");
return ForkAbs(ToAbsOffset(offset), limit, keepOpen);
}
public StreamWrapper ForkAbs(long offset = 0, long limit = -1, bool keepOpen = false)
{
if (offset < 0 || offset > BaseStream.Length)
throw new ArgumentOutOfRangeException(nameof(offset), "Offset out of range.");
if (limit >= 0 && offset + limit > BaseStream.Length)
throw new ArgumentOutOfRangeException(nameof(limit), "Limit too large for the given offset.");
return new StreamWrapper(BaseStream, keepOpen)
{
Offset = offset,
Limit = limit,
};
}
private long ToAbsOffset(long offset)
{
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), offset, "The relative offset cannot be less than zero.");
if (Limit >= 0 && offset > Limit)
throw new ArgumentOutOfRangeException(nameof(offset), offset, "The relative offset cannot be greater than the limit.");
return offset + Offset;
}
private long ToRelOffset(long offset)
{
return offset - Offset;
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_proxy.Dispose();
if (!KeepOpen)
BaseStream.Dispose();
}
}
}