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(); } } }