|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.IO;
using System.ComponentModel;
namespace System.Windows.Baml2006
{
/// <summary>
/// Reference counted stream
/// Allows multiple callers independently coordinate "current position"
/// for reads and writes on a shared stream.
/// Methods on this class are not thread safe.
/// </summary>
internal class SharedStream : Stream
{
private Stream _baseStream;
private long _offset;
private long _length;
private long _position;
private RefCount _refCount;
public SharedStream(Stream baseStream)
{
ArgumentNullException.ThrowIfNull(baseStream);
Initialize(baseStream, 0, baseStream.Length);
}
/// <summary>
/// Constructor that limits the bytes that can be written to or read form
/// </summary>
/// <param name="baseStream"></param>
/// <param name="offset"></param>
/// <param name="length"></param>
public SharedStream(Stream baseStream, long offset, long length)
{
ArgumentNullException.ThrowIfNull(baseStream);
Initialize(baseStream, offset, length);
}
private void Initialize(Stream baseStream, long offset, long length)
{
if (baseStream.CanSeek == false)
{
throw new ArgumentException("can\u2019t seek on baseStream");
}
ArgumentOutOfRangeException.ThrowIfNegative(offset);
ArgumentOutOfRangeException.ThrowIfNegative(length);
SharedStream subStream = baseStream as SharedStream;
if (subStream != null)
{
_baseStream = subStream.BaseStream;
_offset = offset + subStream._offset;
_length = length;
_refCount = subStream._refCount;
_refCount.Value++;
}
else
{
_baseStream = baseStream;
_offset = offset;
_length = length;
_refCount = new RefCount();
_refCount.Value++;
}
}
/// <summary>
/// Number of clients sharing the base stream
/// </summary>
public virtual int SharedCount
{
get { return _refCount.Value; }
}
public override bool CanRead
{
get
{
CheckDisposed();
return _baseStream.CanRead;
}
}
public override bool CanSeek
{
get
{
return true;
}
}
public override bool CanWrite
{
get
{
#if true
return false;
#else
CheckDisposed();
return _baseStream.CanWrite;
#endif
}
}
public override void Flush()
{
CheckDisposed();
_baseStream.Flush();
}
public override long Length
{
get
{
return _length;
}
}
public override long Position
{
get
{
return _position;
}
set
{
if (value < 0 || value >= _length)
{
throw new ArgumentOutOfRangeException(nameof(value), value, string.Empty);
}
_position = value;
}
}
public virtual bool IsDisposed { get { return _baseStream == null; } }
public override int ReadByte()
{
CheckDisposed();
long end = Math.Min(_position + 1, _length);
int b;
if (Sync())
{
b = _baseStream.ReadByte();
_position = _baseStream.Position - _offset;
}
else
{
b = -1;
}
return b;
}
public override int Read(byte[] buffer, int offset, int count)
{
ArgumentNullException.ThrowIfNull(buffer);
ArgumentOutOfRangeException.ThrowIfNegative(offset);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, buffer.Length);
ArgumentOutOfRangeException.ThrowIfGreaterThan(offset, buffer.Length - count);
CheckDisposed();
long end = Math.Min(_position + count, _length);
int bytesRead = 0;
if (Sync())
{
bytesRead = _baseStream.Read(buffer, offset, (int)(end - _position));
_position = _baseStream.Position - _offset;
}
return bytesRead;
}
public override void Write(byte[] buffer, int offset, int count)
{
#if true
throw new NotSupportedException();
#else
if(buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if(offset < 0 || offset >= buffer.Length) {
throw new ArgumentOutOfRangeException(nameof(offset));
}
if((offset + count) > buffer.Length) {
throw new ArgumentOutOfRangeException(nameof(count));
}
CheckDisposed();
long end = Math.Min(_position + count, _length);
if (Sync())
{
_baseStream.Write(buffer, offset, (int)(end - _position));
_position = _baseStream.Position - _offset;
}
#endif
}
public override long Seek(long offset, SeekOrigin origin)
{
long newPosition;
switch (origin)
{
case SeekOrigin.Begin:
newPosition = offset;
break;
case SeekOrigin.Current:
newPosition = _position + offset;
break;
case SeekOrigin.End:
newPosition = _length + offset;
break;
default:
throw new InvalidEnumArgumentException(nameof(origin), (int)origin, typeof(SeekOrigin));
}
if (newPosition < 0 || newPosition >= _length)
{
throw new ArgumentOutOfRangeException(nameof(offset), offset, string.Empty);
}
CheckDisposed();
_position = newPosition;
return _position;
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_baseStream != null)
{
_refCount.Value--;
if (_refCount.Value < 1)
{
_baseStream.Close();
}
_refCount = null;
_baseStream = null;
}
}
base.Dispose(disposing);
}
public Stream BaseStream
{
get { return _baseStream; }
}
private void CheckDisposed()
{
if (IsDisposed)
{
throw new ObjectDisposedException("BaseStream");
}
}
private bool Sync()
{
System.Diagnostics.Debug.Assert(_baseStream != null, "Stream has already been disposed");
if (_position >= 0 && _position < _length)
{
if (_position + _offset != _baseStream.Position)
_baseStream.Seek(_offset + _position, SeekOrigin.Begin);
return true;
}
return false;
}
class RefCount
{
public int Value;
}
}
}
|