| File: Utilities\SubsetStream.cs | Web Access |
| Project: src\src\Microsoft.ML.Core\Microsoft.ML.Core.csproj (Microsoft.ML.Core) |
// 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; using System.IO; using Microsoft.ML.Runtime; namespace Microsoft.ML.Internal.Utilities { /// <summary> /// Returns a "view" stream, which appears to be a possibly truncated /// version of the stream. Reads on this containing stream will also /// advance the wrapped stream. If truncated, reads will not progress /// beyond the indicated length, and writes will fail beyond the /// indicated length. This stream supports seeking (and associated /// operations) if the underlying stream supports seeking, where it is /// supposed that the returned <c>SubsetStream</c> instance has position /// 0 during creation, corresponding to whatever the position of the /// enclosed stream was during creation, so this stream will act as an /// "offset" version of the enclosed stream. As this is intended to /// operate over a subset of a stream, during closing or disposal of the /// subset stream, the underlying stream will always remain open and /// undisposed. /// </summary> [BestFriend] internal sealed class SubsetStream : Stream { private readonly Stream _stream; // The position of the stream. private readonly long _offset; // Negative value if unbounded. private long _remaining; /// <summary> /// Construct the view stream. /// </summary> /// <param name="stream">The underlying stream</param> /// <param name="length">The maximum length this containing /// stream should appear to have, or null if unbounded</param> public SubsetStream(Stream stream, long? length = null) { Contracts.AssertValue(stream); Contracts.Assert(!length.HasValue || length >= 0); _stream = stream; _remaining = length.GetValueOrDefault(-1); try { _offset = stream.Position; } catch (NotSupportedException) { // Doesn't matter in this case anyway. Any operations depending // on _offset should fail, assuming this is a "proper" implementation // of stream. _offset = -1; } } public override int Read(byte[] buffer, int offset, int count) { if (_remaining < 0) return _stream.Read(buffer, offset, count); if (count > _remaining) count = (int)_remaining; int retval = _stream.Read(buffer, offset, count); _remaining -= retval; return retval; } public override void SetLength(long value) { _stream.SetLength(value + _offset); } public override long Seek(long offset, SeekOrigin origin) { long oldPosition = Position; long newPosition; if (origin == SeekOrigin.End) newPosition = _stream.Seek(offset + Length + _offset, SeekOrigin.Begin) - _offset; else newPosition = _stream.Seek(offset + _offset, origin) - _offset; if (_remaining >= 0) _remaining -= newPosition - oldPosition; return newPosition; } public override bool CanRead { get { return _stream.CanRead; } } public override bool CanWrite { get { return _stream.CanWrite; } } public override bool CanTimeout { get { return _stream.CanTimeout; } } public override bool CanSeek { get { return _stream.CanSeek; } } public override long Position { get { return _stream.Position - _offset; } set { Seek(value, SeekOrigin.Begin); } } public override long Length { get { // This may fail with a not supported operation, due to // the underlying stream not supporting these operations. // But that is fine as this is the expected behavior for // this code as well. if (_remaining < 0) return _stream.Length - _offset; return Position + _remaining; } } public override void Flush() { _stream.Flush(); } public override void Write(byte[] buffer, int offset, int count) { if (_remaining >= 0 && count > _remaining) throw Contracts.Except("cannot write {0} bytes to stream bounded at {1} bytes", count, _remaining); _stream.Write(buffer, offset, count); if (_remaining >= 0) _remaining -= count; } } }