261 lines
6.3 KiB
C#
261 lines
6.3 KiB
C#
using System;
|
|
using System.IO;
|
|
|
|
namespace EonaCat.Extensions
|
|
{
|
|
// This file is part of the EonaCat project(s) which is released under the Apache License.
|
|
// See the LICENSE file or go to https://EonaCat.com/License for full license details.
|
|
|
|
public class OffsetStream : Stream
|
|
{
|
|
private const int BUFFERSIZE = 4096;
|
|
|
|
public OffsetStream(Stream stream, long offset = 0, long length = 0, bool readOnly = false, bool ownStream = false)
|
|
{
|
|
if (stream.CanSeek)
|
|
{
|
|
if (offset > stream.Length)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
|
|
BaseStreamOffset = offset;
|
|
|
|
if (length > stream.Length - offset)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
|
|
if (length == 0)
|
|
{
|
|
Length1 = stream.Length - offset;
|
|
}
|
|
else
|
|
{
|
|
Length1 = length;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BaseStreamOffset = 0;
|
|
Length1 = length;
|
|
}
|
|
|
|
BaseStream = stream;
|
|
ReadOnly = readOnly;
|
|
OwnStream = ownStream;
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
if (Disposed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (disposing)
|
|
{
|
|
if (OwnStream & (BaseStream != null))
|
|
{
|
|
BaseStream.Dispose();
|
|
}
|
|
}
|
|
|
|
Disposed = true;
|
|
|
|
base.Dispose(disposing);
|
|
}
|
|
|
|
public override bool CanRead => BaseStream.CanRead;
|
|
|
|
public override bool CanSeek => BaseStream.CanSeek;
|
|
|
|
public override bool CanWrite => BaseStream.CanWrite && !ReadOnly;
|
|
|
|
public override long Length => Length1;
|
|
|
|
public override long Position
|
|
{
|
|
get => Position1;
|
|
|
|
set
|
|
{
|
|
if (value > Length1)
|
|
{
|
|
throw new EndOfStreamException();
|
|
}
|
|
|
|
if (!BaseStream.CanSeek)
|
|
{
|
|
throw new NotSupportedException("Cannot seek stream.");
|
|
}
|
|
|
|
Position1 = value;
|
|
}
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
if (ReadOnly)
|
|
{
|
|
throw new IOException("OffsetStream is read only.");
|
|
}
|
|
|
|
BaseStream.Flush();
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
if (count < 1)
|
|
{
|
|
throw new ArgumentOutOfRangeException("Count cannot be less than 1.");
|
|
}
|
|
|
|
if (Position1 >= Length1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (count > Length1 - Position1)
|
|
{
|
|
count = Convert.ToInt32(Length1 - Position1);
|
|
}
|
|
|
|
if (BaseStream.CanSeek)
|
|
{
|
|
BaseStream.Position = BaseStreamOffset + Position1;
|
|
}
|
|
|
|
int bytesRead = BaseStream.Read(buffer, offset, count);
|
|
Position1 += bytesRead;
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
if (!BaseStream.CanSeek)
|
|
{
|
|
throw new IOException("Stream is not seekable.");
|
|
}
|
|
|
|
long pos;
|
|
|
|
switch (origin)
|
|
{
|
|
case SeekOrigin.Begin:
|
|
pos = offset;
|
|
break;
|
|
|
|
case SeekOrigin.Current:
|
|
pos = Position1 + offset;
|
|
break;
|
|
|
|
case SeekOrigin.End:
|
|
pos = Length1 + offset;
|
|
break;
|
|
|
|
default:
|
|
pos = 0;
|
|
break;
|
|
}
|
|
|
|
if (pos < 0 || pos >= Length1)
|
|
{
|
|
throw new EndOfStreamException("OffsetStream reached begining/end of stream.");
|
|
}
|
|
|
|
Position1 = pos;
|
|
|
|
return pos;
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
if (ReadOnly)
|
|
{
|
|
throw new IOException("OffsetStream is read only.");
|
|
}
|
|
|
|
BaseStream.SetLength(BaseStreamOffset + value);
|
|
Length1 = value;
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
if (ReadOnly)
|
|
{
|
|
throw new IOException("OffsetStream is read only.");
|
|
}
|
|
|
|
if (count < 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
long pos = Position1 + count;
|
|
|
|
if (pos > Length1)
|
|
{
|
|
throw new EndOfStreamException("OffsetStream reached end of stream.");
|
|
}
|
|
|
|
if (BaseStream.CanSeek)
|
|
{
|
|
BaseStream.Position = BaseStreamOffset + Position1;
|
|
}
|
|
|
|
BaseStream.Write(buffer, offset, count);
|
|
Position1 = pos;
|
|
}
|
|
|
|
public long BaseStreamOffset { get; private set; }
|
|
|
|
public Stream BaseStream { get; }
|
|
public long Length1 { get; set; }
|
|
public long Position1 { get; set; }
|
|
|
|
public bool ReadOnly { get; }
|
|
|
|
public bool Disposed { get; set; }
|
|
|
|
public bool OwnStream { get; }
|
|
|
|
public void Reset(long offset, long length, long position)
|
|
{
|
|
BaseStreamOffset = offset;
|
|
Length1 = length;
|
|
Position1 = position;
|
|
}
|
|
|
|
public void WriteTo(Stream stream)
|
|
{
|
|
WriteTo(stream, BUFFERSIZE);
|
|
}
|
|
|
|
public void WriteTo(Stream stream, int bufferSize)
|
|
{
|
|
if (!BaseStream.CanSeek)
|
|
{
|
|
throw new IOException("Stream is not seekable.");
|
|
}
|
|
|
|
if (Length1 < bufferSize)
|
|
{
|
|
bufferSize = Convert.ToInt32(Length1);
|
|
}
|
|
|
|
long previousPosition = Position1;
|
|
Position1 = 0;
|
|
|
|
try
|
|
{
|
|
CopyTo(stream, bufferSize);
|
|
}
|
|
finally
|
|
{
|
|
Position1 = previousPosition;
|
|
}
|
|
}
|
|
}
|
|
} |