/* * Copyright (c) 2024 ETH Zürich, IT Services * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ using System; using System.IO; namespace SafeExamBrowser.Configuration { /// /// A read-only wrapper for a subsection of another, larger stream. /// internal class SubStream : Stream { private long length; private long offset; private Stream original; public override bool CanRead => original.CanRead; public override bool CanSeek => original.CanSeek; public override bool CanWrite => false; public override long Length => length; public override long Position { get; set; } /// /// Creates a new wrapper for the specified subsection of the given stream. /// /// /// /// Below an example of a subsection within a stream: /// /// +==============+==============================================================+==============================+ /// | ... |####################### subsection ###########################| ... | /// +==============+==============================================================+==============================+ /// ^ ^ ^ ^ /// | | | | /// | + offset + length | /// | | /// + start of original end of original + /// /// /// In case the original stream does not support . /// In case the original stream does not support . /// In case the specified subsection is outside the bounds of the original stream. public SubStream(Stream original, long offset, long length) { this.original = original; this.offset = offset; this.length = length; if (!original.CanRead) { throw new ArgumentException("The original stream must support reading!", nameof(original)); } if (!original.CanSeek) { throw new ArgumentException("The original stream must support seeking!", nameof(original)); } if (original.Length < offset + length || offset < 0 || length < 1) { throw new ArgumentOutOfRangeException($"Specified subsection is outside the bounds of the original stream!"); } } public override void Flush() { throw new NotSupportedException(); } public override int Read(byte[] buffer, int offset, int count) { var originalPosition = original.Position; if (Position < 0 || Position >= Length) { return 0; } if (Position + count >= Length) { count = Convert.ToInt32(Length - Position); } original.Seek(this.offset + Position, SeekOrigin.Begin); var bytesRead = original.Read(buffer, offset, count); Position += bytesRead; original.Seek(originalPosition, SeekOrigin.Begin); return bytesRead; } public override int ReadByte() { if (Position < 0 || Position >= Length) { return -1; } return base.ReadByte(); } public override long Seek(long offset, SeekOrigin origin) { switch (origin) { case SeekOrigin.Begin: Position = offset; break; case SeekOrigin.Current: Position += offset; break; case SeekOrigin.End: Position = length + offset; break; default: throw new NotImplementedException($"Seeking from position '{origin}' is not implemented!"); } return Position; } public override void SetLength(long value) { throw new NotSupportedException(); } public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } } }