ZoneBuilder/Source/Core/IO/ClippedStream.cs

271 lines
7.2 KiB
C#

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.IO;
#endregion
namespace CodeImp.DoomBuilder.IO
{
internal class ClippedStream : Stream
{
#region ================== Variables
// Base stream
private Stream basestream;
// Data limit
private int offset;
private int length;
private long position;
// Disposing
private bool isdisposed;
#endregion
#region ================== Properties
public override long Length { get { return length; } }
public override long Position { get { return position; } set { this.Seek(value, SeekOrigin.Begin); } }
public override bool CanRead { get { return basestream.CanRead; } }
public override bool CanSeek { get { return basestream.CanSeek; } }
public override bool CanWrite { get { return basestream.CanWrite; } }
public bool IsDisposed { get { return isdisposed; } }
#endregion
#region ================== Constructor / Disposer
// Constructor
public ClippedStream(Stream basestream, int offset, int length)
{
// Can only create from a stream that can seek
if(!basestream.CanSeek) throw new ArgumentException("ClippedStream can only be created with a Stream that allows Seeking.");
// Initialize
this.basestream = basestream;
this.position = 0;
this.offset = offset;
this.length = length;
// We have no destructor
GC.SuppressFinalize(this);
}
// Disposer
public new void Dispose()
{
// Not already disposed?
if(!isdisposed)
{
// Already set isdisposed to prevent recursion
isdisposed = true;
// Clean up
basestream = null;
// Dispose base
base.Dispose();
}
}
#endregion
#region ================== Methods
// This flushes the written changes
public override void Flush()
{
// Flush base stream
basestream.Flush();
}
// This reads from the stream
public override int Read(byte[] buffer, int offset, int count)
{
// Check if this exceeds limits
if((this.position + count) > (this.length + 1))
{
// Read only within limits
count = this.length - (int)this.position;
}
// Anything to read?
if(count > 0)
{
// Seek if needed
if(basestream.Position != (this.offset + this.position))
basestream.Seek(this.offset + this.position, SeekOrigin.Begin);
// Read from base stream
position += count;
return basestream.Read(buffer, offset, count);
}
else
{
return 0;
}
}
// This writes to the stream
public override void Write(byte[] buffer, int offset, int count)
{
// Check if this exceeds limits
if((this.position + count) > (this.length + 1))
throw new ArgumentException("Attempted to write outside the range of the stream.");
// Seek if needed
if(basestream.Position != (this.offset + this.position))
basestream.Seek(this.offset + this.position, SeekOrigin.Begin);
// Read from base stream
position += count;
basestream.Write(buffer, offset, count);
}
// Seek within clipped buffer
public override long Seek(long offset, SeekOrigin origin)
{
// Seeking from beginning
if(origin == SeekOrigin.Begin)
{
// Check if this exceeds limits
if((offset > this.length) || (offset < 0))
throw new ArgumentException("Attempted to seek outside the range of the stream.");
// Seek
position = basestream.Seek(this.offset + offset, SeekOrigin.Begin) - this.offset;
}
// Seeking from current position
else if(origin == SeekOrigin.Current)
{
// Check if this exceeds limits
if((this.position + offset > this.length) || (this.position + offset < 0))
throw new ArgumentException("Attempted to seek outside the range of the stream.");
// Seek
position = basestream.Seek(this.offset + this.position + offset, SeekOrigin.Begin) - this.offset;
}
// Seeking from end
else
{
// Check if this exceeds limits
if((offset > 0) || (this.length + offset < 0))
throw new ArgumentException("Attempted to seek outside the range of the stream.");
// Seek
position = basestream.Seek(this.offset + this.length + offset, SeekOrigin.Begin) - this.offset;
}
// Return new position
return position;
}
// Change the length of the steam
public override void SetLength(long value)
{
// Not supported
throw new NotSupportedException("This operation is not supported.");
}
// Asynchronous read from stream
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
// Check if this exceeds limits
if((this.position + count) > (this.length + 1))
throw new ArgumentException("Attempted to read outside the range of the stream.");
// Seek if needed
if(basestream.Position != (this.offset + this.position))
basestream.Seek(this.offset + this.position, SeekOrigin.Begin);
// Read
position += count;
return base.BeginRead(buffer, offset, count, callback, state);
}
// Asynchronous write to stream
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
{
// Check if this exceeds limits
if((this.position + count) > (this.length + 1))
throw new ArgumentException("Attempted to write outside the range of the stream.");
// Seek if needed
if(basestream.Position != (this.offset + this.position))
basestream.Seek(this.offset + this.position, SeekOrigin.Begin);
// Write
position += count;
return base.BeginWrite(buffer, offset, count, callback, state);
}
// This closes the stream
public override void Close()
{
basestream = null;
base.Close();
this.Dispose();
}
// This reads a single byte from the stream
public override int ReadByte()
{
// Check if this exceeds limits
if((this.position + 1) > (this.length + 1))
throw new ArgumentException("Attempted to read outside the range of the stream.");
// Seek if needed
if(basestream.Position != (this.offset + this.position))
basestream.Seek(this.offset + this.position, SeekOrigin.Begin);
// Read from base stream
position++;
return basestream.ReadByte();
}
// This writes a single byte to the stream
public override void WriteByte(byte value)
{
// Check if this exceeds limits
if((this.position + 1) > (this.length + 1))
throw new ArgumentException("Attempted to write outside the range of the stream.");
// Seek if needed
if(basestream.Position != (this.offset + this.position))
basestream.Seek(this.offset + this.position, SeekOrigin.Begin);
// Read from base stream
position++;
basestream.WriteByte(value);
}
// This returns all the bytes in the stream
public byte[] ReadAllBytes()
{
byte[] bytes = new byte[length];
Seek(0, SeekOrigin.Begin);
Read(bytes, 0, length);
return bytes;
}
#endregion
}
}