| File: System\Windows\Markup\Baml2006\SharedStream.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. 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) { 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; } private class RefCount { public int Value; } } }