Files
EonaCat.Logger/EonaCat.Logger/Extensions/OffsetStream.cs
2022-09-19 09:07:51 +02:00

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