140 lines
4.2 KiB
C#
140 lines
4.2 KiB
C#
/*
|
|
* 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
|
|
{
|
|
/// <summary>
|
|
/// A read-only wrapper for a subsection of another, larger stream.
|
|
/// </summary>
|
|
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; }
|
|
|
|
/// <summary>
|
|
/// Creates a new wrapper for the specified subsection of the given stream.
|
|
/// </summary>
|
|
/// <remarks>
|
|
///
|
|
/// Below an example of a subsection within a stream:
|
|
///
|
|
/// +==============+==============================================================+==============================+
|
|
/// | ... |####################### subsection ###########################| ... |
|
|
/// +==============+==============================================================+==============================+
|
|
/// ^ ^ ^ ^
|
|
/// | | | |
|
|
/// | + offset + length |
|
|
/// | |
|
|
/// + start of original end of original +
|
|
///
|
|
/// </remarks>
|
|
/// <exception cref="ArgumentException">In case the original stream does not support <see cref="Stream.CanRead"/>.</exception>
|
|
/// <exception cref="ArgumentException">In case the original stream does not support <see cref="Stream.CanSeek"/>.</exception>
|
|
/// <exception cref="ArgumentOutOfRangeException">In case the specified subsection is outside the bounds of the original stream.</exception>
|
|
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();
|
|
}
|
|
}
|
|
}
|