mirror of
https://github.com/aaru-dps/Aaru.Helpers.git
synced 2025-12-16 19:24:35 +00:00
[Aaru.Helpers] Move all stream extensions from around the application to this project.
This commit is contained in:
257
IO/ForcedSeekStream.cs
Normal file
257
IO/ForcedSeekStream.cs
Normal file
@@ -0,0 +1,257 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : ForcedSeekStream.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Filters.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Provides a seekable stream from a forward-readable stream.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2023 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Aaru.Helpers.IO;
|
||||
|
||||
/// <summary>
|
||||
/// ForcedSeekStream allows to seek a forward-readable stream (like System.IO.Compression streams) by doing the
|
||||
/// slow and known trick of rewinding and forward reading until arriving the desired position.
|
||||
/// </summary>
|
||||
/// <inheritdoc />
|
||||
public sealed class ForcedSeekStream<T> : Stream where T : Stream
|
||||
{
|
||||
const int BUFFER_LEN = 1048576;
|
||||
readonly string _backFile;
|
||||
readonly FileStream _backStream;
|
||||
readonly T _baseStream;
|
||||
long _streamLength;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="T:Aaru.Helpers.IO.ForcedSeekStream`1" /> class.</summary>
|
||||
/// <param name="length">The real (uncompressed) length of the stream.</param>
|
||||
/// <param name="args">Parameters that are used to create the base stream.</param>
|
||||
/// <inheritdoc />
|
||||
public ForcedSeekStream(long length, params object[] args)
|
||||
{
|
||||
_streamLength = length;
|
||||
_baseStream = (T)Activator.CreateInstance(typeof(T), args);
|
||||
_backFile = Path.GetTempFileName();
|
||||
_backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
|
||||
|
||||
if(length == 0)
|
||||
CalculateLength();
|
||||
}
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="T:Aaru.Helpers.IO.ForcedSeekStream`1" /> class.</summary>
|
||||
/// <param name="args">Parameters that are used to create the base stream.</param>
|
||||
/// <inheritdoc />
|
||||
public ForcedSeekStream(params object[] args)
|
||||
{
|
||||
_baseStream = (T)Activator.CreateInstance(typeof(T), args);
|
||||
_backFile = Path.GetTempFileName();
|
||||
_backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
|
||||
CalculateLength();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanRead => _baseStream.CanRead;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSeek => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanWrite => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Length => _streamLength;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Position
|
||||
{
|
||||
get => _backStream.Position;
|
||||
|
||||
set => SetPosition(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the real (uncompressed) length of the stream. It basically reads (uncompresses) the whole stream to
|
||||
/// memory discarding its contents, so it should be used as a last resort.
|
||||
/// </summary>
|
||||
/// <returns>The length.</returns>
|
||||
public void CalculateLength()
|
||||
{
|
||||
int read;
|
||||
|
||||
do
|
||||
{
|
||||
var buffer = new byte[BUFFER_LEN];
|
||||
read = _baseStream.EnsureRead(buffer, 0, BUFFER_LEN);
|
||||
_backStream.Write(buffer, 0, read);
|
||||
} while(read == BUFFER_LEN);
|
||||
|
||||
_streamLength = _backStream.Length;
|
||||
_backStream.Position = 0;
|
||||
}
|
||||
|
||||
void SetPosition(long position)
|
||||
{
|
||||
if(position == _backStream.Position)
|
||||
return;
|
||||
|
||||
if(position < _backStream.Length)
|
||||
{
|
||||
_backStream.Position = position;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(position > _streamLength)
|
||||
position = _streamLength;
|
||||
|
||||
_backStream.Position = _backStream.Length;
|
||||
long toPosition = position - _backStream.Position;
|
||||
var fullBufferReads = (int)(toPosition / BUFFER_LEN);
|
||||
var restToRead = (int)(toPosition % BUFFER_LEN);
|
||||
byte[] buffer;
|
||||
int bufPos;
|
||||
int left;
|
||||
|
||||
for(var i = 0; i < fullBufferReads; i++)
|
||||
{
|
||||
buffer = new byte[BUFFER_LEN];
|
||||
bufPos = 0;
|
||||
left = BUFFER_LEN;
|
||||
|
||||
while(left > 0)
|
||||
{
|
||||
int done = _baseStream.EnsureRead(buffer, bufPos, left);
|
||||
left -= done;
|
||||
bufPos += done;
|
||||
}
|
||||
|
||||
_backStream.Write(buffer, 0, BUFFER_LEN);
|
||||
}
|
||||
|
||||
buffer = new byte[restToRead];
|
||||
bufPos = 0;
|
||||
left = restToRead;
|
||||
|
||||
while(left > 0)
|
||||
{
|
||||
int done = _baseStream.EnsureRead(buffer, bufPos, left);
|
||||
left -= done;
|
||||
bufPos += done;
|
||||
}
|
||||
|
||||
_backStream.Write(buffer, 0, restToRead);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Flush()
|
||||
{
|
||||
_baseStream.Flush();
|
||||
_backStream.Flush();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if(_backStream.Position + count > _streamLength)
|
||||
count = (int)(_streamLength - _backStream.Position);
|
||||
|
||||
if(_backStream.Position + count <= _backStream.Length)
|
||||
return _backStream.EnsureRead(buffer, offset, count);
|
||||
|
||||
long oldPosition = _backStream.Position;
|
||||
SetPosition(_backStream.Position + count);
|
||||
SetPosition(oldPosition);
|
||||
|
||||
return _backStream.EnsureRead(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int ReadByte()
|
||||
{
|
||||
if(_backStream.Position + 1 > _streamLength)
|
||||
return -1;
|
||||
|
||||
if(_backStream.Position + 1 <= _backStream.Length)
|
||||
return _backStream.ReadByte();
|
||||
|
||||
SetPosition(_backStream.Position + 1);
|
||||
SetPosition(_backStream.Position - 1);
|
||||
|
||||
return _backStream.ReadByte();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
switch(origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
if(offset < 0)
|
||||
throw new IOException(Localization.Cannot_seek_before_stream_start);
|
||||
|
||||
SetPosition(offset);
|
||||
|
||||
break;
|
||||
case SeekOrigin.End:
|
||||
if(offset > 0)
|
||||
throw new IOException(Localization.Cannot_seek_after_stream_end);
|
||||
|
||||
if(_streamLength == 0)
|
||||
CalculateLength();
|
||||
|
||||
SetPosition(_streamLength + offset);
|
||||
|
||||
break;
|
||||
default:
|
||||
SetPosition(_backStream.Position + offset);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return _backStream.Position;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetLength(long value) => throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Close()
|
||||
{
|
||||
_backStream?.Close();
|
||||
File.Delete(_backFile);
|
||||
}
|
||||
|
||||
~ForcedSeekStream()
|
||||
{
|
||||
_backStream?.Close();
|
||||
File.Delete(_backFile);
|
||||
}
|
||||
}
|
||||
78
IO/NonClosableStream.cs
Normal file
78
IO/NonClosableStream.cs
Normal file
@@ -0,0 +1,78 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : NonClosableStream.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Compression.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Overrides MemoryStream to ignore standard close requests.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2023 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
|
||||
namespace Aaru.Helpers.IO;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Creates a MemoryStream that ignores close commands</summary>
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public sealed class NonClosableStream : Stream
|
||||
{
|
||||
readonly Stream _baseStream;
|
||||
|
||||
public NonClosableStream(byte[] buffer) => _baseStream = new MemoryStream(buffer);
|
||||
|
||||
public NonClosableStream() => _baseStream = new MemoryStream();
|
||||
|
||||
public NonClosableStream(Stream stream) => _baseStream = stream;
|
||||
|
||||
public override bool CanRead => _baseStream.CanRead;
|
||||
public override bool CanSeek => _baseStream.CanSeek;
|
||||
public override bool CanWrite => _baseStream.CanWrite;
|
||||
public override long Length => _baseStream.Length;
|
||||
|
||||
public override long Position
|
||||
{
|
||||
get => _baseStream.Position;
|
||||
set => _baseStream.Position = value;
|
||||
}
|
||||
|
||||
public override void Flush() => _baseStream.Flush();
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count) => _baseStream.EnsureRead(buffer, offset, count);
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin);
|
||||
|
||||
public override void SetLength(long value) => _baseStream.SetLength(value);
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count) => _baseStream.Write(buffer, offset, count);
|
||||
|
||||
public override void Close()
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
public void ReallyClose() => _baseStream.Close();
|
||||
}
|
||||
680
IO/OffsetStream.cs
Normal file
680
IO/OffsetStream.cs
Normal file
@@ -0,0 +1,680 @@
|
||||
// /***************************************************************************
|
||||
// Aaru Data Preservation Suite
|
||||
// ----------------------------------------------------------------------------
|
||||
//
|
||||
// Filename : OffsetStream.cs
|
||||
// Author(s) : Natalia Portillo <claunia@claunia.com>
|
||||
//
|
||||
// Component : Filters.
|
||||
//
|
||||
// --[ Description ] ----------------------------------------------------------
|
||||
//
|
||||
// Provides a stream that's a subset of another stream.
|
||||
//
|
||||
// --[ License ] --------------------------------------------------------------
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as
|
||||
// published by the Free Software Foundation; either version 2.1 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful, but
|
||||
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public
|
||||
// License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// Copyright © 2011-2023 Natalia Portillo
|
||||
// ****************************************************************************/
|
||||
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
#if !NETSTANDARD2_0
|
||||
|
||||
#endif
|
||||
|
||||
namespace Aaru.Helpers.IO;
|
||||
|
||||
/// <summary>Creates a stream that is a subset of another stream.</summary>
|
||||
/// <inheritdoc />
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public sealed class OffsetStream : Stream
|
||||
{
|
||||
readonly Stream _baseStream;
|
||||
readonly long _streamEnd;
|
||||
readonly long _streamStart;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified stream, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="stream">Base stream</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(Stream stream, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = stream;
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="T:System.IO.FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="share">
|
||||
/// A bitwise combination of the enumeration values that determines how the file will be shared by
|
||||
/// processes.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
/// <param name="options">A bitwise combination of the enumeration values that specifies additional file options.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,
|
||||
FileOptions options, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(path, mode, access, share, bufferSize, options);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="T:System.IO.FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(SafeFileHandle handle, FileAccess access, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(handle, access);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="T:System.IO.FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(SafeFileHandle handle, FileAccess access, int bufferSize, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(handle, access, bufferSize);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="T:System.IO.FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
/// <param name="isAsync">Specifies whether to use asynchronous I/O or synchronous I/O.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(handle, access, bufferSize, isAsync);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="T:System.IO.FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="share">
|
||||
/// A bitwise combination of the enumeration values that determines how the file will be shared by
|
||||
/// processes.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
/// <param name="useAsync">Specifies whether to use asynchronous I/O or synchronous I/O.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync,
|
||||
long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(path, mode, access, share, bufferSize, useAsync);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="T:System.IO.FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="share">
|
||||
/// A bitwise combination of the enumeration values that determines how the file will be shared by
|
||||
/// processes.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, long start,
|
||||
long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(path, mode, access, share, bufferSize);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="T:System.IO.FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="share">
|
||||
/// A bitwise combination of the enumeration values that determines how the file will be shared by
|
||||
/// processes.
|
||||
/// </param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(string path, FileMode mode, FileAccess access, FileShare share, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(path, mode, access, share);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="T:System.IO.FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(string path, FileMode mode, FileAccess access, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(path, mode, access);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified file, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(string path, FileMode mode, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new FileStream(path, mode);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified byte array, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
|
||||
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
|
||||
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
|
||||
/// <param name="publiclyVisible">Currently ignored.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new MemoryStream(buffer, index, count, writable, publiclyVisible);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified byte array, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
|
||||
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
|
||||
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(byte[] buffer, int index, int count, bool writable, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new MemoryStream(buffer, index, count, writable);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified byte array, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
|
||||
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(byte[] buffer, int index, int count, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new MemoryStream(buffer, index, count);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified byte array, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(byte[] buffer, bool writable, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new MemoryStream(buffer, writable);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Initializes a stream that only allows reading from <paramref name="start" /> to <paramref name="end" /> of the
|
||||
/// specified byte array, both inclusive.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="start">Start position</param>
|
||||
/// <param name="end">Last readable position</param>
|
||||
/// <exception cref="T:System.ArgumentOutOfRangeException">Invalid range</exception>
|
||||
public OffsetStream(byte[] buffer, long start, long end)
|
||||
{
|
||||
if(start < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(start), Localization.Start_cant_be_a_negative_number);
|
||||
|
||||
if(end < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_cant_be_a_negative_number);
|
||||
|
||||
_streamStart = start;
|
||||
_streamEnd = end;
|
||||
|
||||
_baseStream = new MemoryStream(buffer);
|
||||
|
||||
if(end > _baseStream.Length)
|
||||
throw new ArgumentOutOfRangeException(nameof(end), Localization.End_is_after_stream_end);
|
||||
|
||||
_baseStream.Position = start;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanRead => _baseStream.CanRead;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSeek => _baseStream.CanSeek;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanWrite => _baseStream.CanWrite;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Length => _streamEnd - _streamStart + 1;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Position
|
||||
{
|
||||
get => _baseStream.Position - _streamStart;
|
||||
|
||||
set
|
||||
{
|
||||
if(value + _streamStart > _streamEnd)
|
||||
throw new IOException(Localization.Cannot_set_position_past_stream_end);
|
||||
|
||||
_baseStream.Position = value + _streamStart;
|
||||
}
|
||||
}
|
||||
|
||||
~OffsetStream()
|
||||
{
|
||||
_baseStream.Close();
|
||||
_baseStream.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
||||
{
|
||||
if(_baseStream.Position + count > _streamEnd)
|
||||
throw new IOException(Localization.Cannot_read_past_stream_end);
|
||||
|
||||
return _baseStream.BeginRead(buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
|
||||
{
|
||||
if(_baseStream.Position + count > _streamEnd)
|
||||
throw new IOException(Localization.Cannot_write_past_stream_end);
|
||||
|
||||
return _baseStream.BeginWrite(buffer, offset, count, callback, state);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Close() => _baseStream.Close();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int EndRead(IAsyncResult asyncResult) => _baseStream.EndRead(asyncResult);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void EndWrite(IAsyncResult asyncResult) => _baseStream.EndWrite(asyncResult);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int ReadByte() => _baseStream.Position == _streamEnd + 1 ? -1 : _baseStream.ReadByte();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteByte(byte value)
|
||||
{
|
||||
if(_baseStream.Position + 1 > _streamEnd)
|
||||
throw new IOException(Localization.Cannot_write_past_stream_end);
|
||||
|
||||
_baseStream.WriteByte(value);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Flush() => _baseStream.Flush();
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if(_baseStream.Position + count > _streamEnd + 1)
|
||||
throw new IOException(Localization.Cannot_read_past_stream_end);
|
||||
|
||||
return _baseStream.EnsureRead(buffer, offset, count);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
switch(origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
if(offset + _streamStart > _streamEnd)
|
||||
throw new IOException(Localization.Cannot_seek_after_stream_end);
|
||||
|
||||
return _baseStream.Seek(offset + _streamStart, SeekOrigin.Begin) - _streamStart;
|
||||
case SeekOrigin.End:
|
||||
if(offset - (_baseStream.Length - _streamEnd) < _streamStart)
|
||||
throw new IOException(Localization.Cannot_seek_before_stream_start);
|
||||
|
||||
return _baseStream.Seek(offset - (_baseStream.Length - _streamEnd), SeekOrigin.End) - _streamStart;
|
||||
default:
|
||||
if(offset + _baseStream.Position > _streamEnd)
|
||||
throw new IOException(Localization.Cannot_seek_after_stream_end);
|
||||
|
||||
return _baseStream.Seek(offset, SeekOrigin.Current) - _streamStart;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetLength(long value) =>
|
||||
throw new NotSupportedException(Localization.Growing_OffsetStream_is_not_supported);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
if(_baseStream.Position + count > _streamEnd)
|
||||
throw new IOException(Localization.Cannot_write_past_stream_end);
|
||||
|
||||
_baseStream.Write(buffer, offset, count);
|
||||
}
|
||||
}
|
||||
372
IO/SplitJoinStream.cs
Normal file
372
IO/SplitJoinStream.cs
Normal file
@@ -0,0 +1,372 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32.SafeHandles;
|
||||
|
||||
namespace Aaru.Helpers.IO;
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>Implements a stream that joins two or more files (sequentially) as a single stream</summary>
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Global")]
|
||||
public class SplitJoinStream : Stream
|
||||
{
|
||||
readonly Dictionary<long, Stream> _baseStreams;
|
||||
long _position;
|
||||
long _streamLength;
|
||||
|
||||
/// <inheritdoc />
|
||||
public SplitJoinStream()
|
||||
{
|
||||
_baseStreams = new Dictionary<long, Stream>();
|
||||
_streamLength = 0;
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanRead => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanSeek => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanWrite => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Length => _streamLength;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Position
|
||||
{
|
||||
get => _position;
|
||||
|
||||
set
|
||||
{
|
||||
if(value >= _streamLength)
|
||||
throw new IOException(Localization.Cannot_set_position_past_stream_end);
|
||||
|
||||
_position = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Adds a stream at the end of the current stream</summary>
|
||||
/// <param name="stream">Stream to add</param>
|
||||
/// <exception cref="ArgumentException">The specified stream is non-readable or non-seekable</exception>
|
||||
public void Add(Stream stream)
|
||||
{
|
||||
if(!stream.CanSeek)
|
||||
throw new ArgumentException(Localization.Non_seekable_streams_are_not_supported);
|
||||
|
||||
if(!stream.CanRead)
|
||||
throw new ArgumentException(Localization.Non_readable_streams_are_not_supported);
|
||||
|
||||
_baseStreams[_streamLength] = stream;
|
||||
_streamLength += stream.Length;
|
||||
}
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="share">
|
||||
/// A bitwise combination of the enumeration values that determines how the file will be shared by
|
||||
/// processes.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
/// <param name="options">A bitwise combination of the enumeration values that specifies additional file options.</param>
|
||||
public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize,
|
||||
FileOptions options) => Add(new FileStream(path, mode, access, share, bufferSize, options));
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
public void Add(SafeFileHandle handle, FileAccess access) => Add(new FileStream(handle, access));
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
public void Add(SafeFileHandle handle, FileAccess access, int bufferSize) =>
|
||||
Add(new FileStream(handle, access, bufferSize));
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="handle">A file handle for the file that the stream will encapsulate.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
/// <param name="isAsync">Specifies whether to use asynchronous I/O or synchronous I/O.</param>
|
||||
public void Add(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) =>
|
||||
Add(new FileStream(handle, access, bufferSize, isAsync));
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="share">
|
||||
/// A bitwise combination of the enumeration values that determines how the file will be shared by
|
||||
/// processes.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
/// <param name="useAsync">Specifies whether to use asynchronous I/O or synchronous I/O.</param>
|
||||
public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, bool useAsync) =>
|
||||
Add(new FileStream(path, mode, access, share, bufferSize, useAsync));
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="share">
|
||||
/// A bitwise combination of the enumeration values that determines how the file will be shared by
|
||||
/// processes.
|
||||
/// </param>
|
||||
/// <param name="bufferSize">
|
||||
/// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is
|
||||
/// 4096.
|
||||
/// </param>
|
||||
public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) =>
|
||||
Add(new FileStream(path, mode, access, share, bufferSize));
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
/// <param name="share">
|
||||
/// A bitwise combination of the enumeration values that determines how the file will be shared by
|
||||
/// processes.
|
||||
/// </param>
|
||||
public void Add(string path, FileMode mode, FileAccess access, FileShare share) =>
|
||||
Add(new FileStream(path, mode, access, share));
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
public void Add(string path, FileMode mode, FileAccess access) => Add(new FileStream(path, mode, access));
|
||||
|
||||
/// <summary>Adds the specified file to the end of the current stream</summary>
|
||||
/// <param name="path">A relative or absolute path for the file that the stream will encapsulate.</param>
|
||||
/// <param name="mode">One of the enumeration values that determines how to open or create the file.</param>
|
||||
public void Add(string path, FileMode mode) => Add(new FileStream(path, mode));
|
||||
|
||||
/// <summary>Adds the specified byte array to the end of the current stream</summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
|
||||
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
|
||||
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
|
||||
/// <param name="publiclyVisible">Currently ignored.</param>
|
||||
public void Add(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) =>
|
||||
Add(new MemoryStream(buffer, index, count, writable, publiclyVisible));
|
||||
|
||||
/// <summary>Adds the specified byte array to the end of the current stream</summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
|
||||
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
|
||||
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
|
||||
public void Add(byte[] buffer, int index, int count, bool writable) =>
|
||||
Add(new MemoryStream(buffer, index, count, writable));
|
||||
|
||||
/// <summary>Adds the specified byte array to the end of the current stream</summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="index">The index into <paramref name="buffer" /> at which the stream begins.</param>
|
||||
/// <param name="count">The length in bytes to add to the end of the current stream.</param>
|
||||
public void Add(byte[] buffer, int index, int count) => Add(new MemoryStream(buffer, index, count));
|
||||
|
||||
/// <summary>Adds the specified byte array to the end of the current stream</summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
/// <param name="writable">The setting of the CanWrite property, currently ignored.</param>
|
||||
public void Add(byte[] buffer, bool writable) => Add(new MemoryStream(buffer, writable));
|
||||
|
||||
/// <summary>Adds the specified byte array to the end of the current stream</summary>
|
||||
/// <param name="buffer">The array of unsigned bytes to add at the end of this stream.</param>
|
||||
public void Add(byte[] buffer) => Add(new MemoryStream(buffer));
|
||||
|
||||
/// <summary>Adds a range of files to the end of the current stream, alphabetically sorted</summary>
|
||||
/// <param name="basePath">Base file path, directory path only</param>
|
||||
/// <param name="counterFormat">Counter format, includes filename and a formatting string</param>
|
||||
/// <param name="counterStart">Counter start, defaults to 0</param>
|
||||
/// <param name="access">
|
||||
/// A bitwise combination of the enumeration values that determines how the file can be accessed by a
|
||||
/// <see cref="FileStream" /> object.
|
||||
/// </param>
|
||||
public void AddRange(string basePath, string counterFormat = "{0:D3}", int counterStart = 0,
|
||||
FileAccess access = FileAccess.Read)
|
||||
{
|
||||
while(true)
|
||||
{
|
||||
string filePath = Path.Combine(basePath, string.Format(counterFormat, counterStart));
|
||||
|
||||
if(!File.Exists(filePath))
|
||||
break;
|
||||
|
||||
Add(filePath, FileMode.Open, access);
|
||||
|
||||
counterStart++;
|
||||
}
|
||||
}
|
||||
|
||||
~SplitJoinStream()
|
||||
{
|
||||
foreach(Stream stream in _baseStreams.Values)
|
||||
{
|
||||
stream.Close();
|
||||
stream.Dispose();
|
||||
}
|
||||
|
||||
_baseStreams.Clear();
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IAsyncResult
|
||||
BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) =>
|
||||
throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IAsyncResult
|
||||
BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) =>
|
||||
throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Close()
|
||||
{
|
||||
foreach(Stream stream in _baseStreams.Values)
|
||||
stream.Close();
|
||||
|
||||
_baseStreams.Clear();
|
||||
_position = 0;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int EndRead(IAsyncResult asyncResult) =>
|
||||
throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void EndWrite(IAsyncResult asyncResult) =>
|
||||
throw new NotSupportedException(Localization.Asynchronous_IO_is_not_supported);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int ReadByte()
|
||||
{
|
||||
if(_position >= _streamLength)
|
||||
return -1;
|
||||
|
||||
KeyValuePair<long, Stream> baseStream = _baseStreams.FirstOrDefault(s => s.Key >= _position);
|
||||
|
||||
if(baseStream.Value == null)
|
||||
return -1;
|
||||
|
||||
baseStream.Value.Position = _position - baseStream.Key;
|
||||
_position++;
|
||||
|
||||
return baseStream.Value.ReadByte();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void WriteByte(byte value) => throw new ReadOnlyException(Localization.This_stream_is_read_only);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Flush() {}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
{
|
||||
var read = 0;
|
||||
|
||||
while(count > 0)
|
||||
{
|
||||
KeyValuePair<long, Stream> baseStream = _baseStreams.LastOrDefault(s => s.Key <= _position);
|
||||
|
||||
if(baseStream.Value == null)
|
||||
break;
|
||||
|
||||
baseStream.Value.Position = _position - baseStream.Key;
|
||||
|
||||
int currentCount = count;
|
||||
|
||||
if(baseStream.Value.Position + currentCount > baseStream.Value.Length)
|
||||
currentCount = (int)(baseStream.Value.Length - baseStream.Value.Position);
|
||||
|
||||
read += baseStream.Value.Read(buffer, offset, currentCount);
|
||||
|
||||
count -= currentCount;
|
||||
offset += currentCount;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
switch(origin)
|
||||
{
|
||||
case SeekOrigin.Begin:
|
||||
if(offset >= _streamLength)
|
||||
throw new IOException(Localization.Cannot_seek_after_stream_end);
|
||||
|
||||
_position = offset;
|
||||
|
||||
break;
|
||||
case SeekOrigin.End:
|
||||
if(_position - offset < 0)
|
||||
throw new IOException(Localization.Cannot_seek_before_stream_start);
|
||||
|
||||
_position -= offset;
|
||||
|
||||
break;
|
||||
default:
|
||||
if(_position + offset >= _streamLength)
|
||||
throw new IOException(Localization.Cannot_seek_after_stream_end);
|
||||
|
||||
_position += offset;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return _position;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetLength(long value) => throw new ReadOnlyException(Localization.This_stream_is_read_only);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(byte[] buffer, int offset, int count) =>
|
||||
throw new ReadOnlyException(Localization.This_stream_is_read_only);
|
||||
}
|
||||
117
Localization/Localization.Designer.cs
generated
117
Localization/Localization.Designer.cs
generated
@@ -59,6 +59,87 @@ namespace Aaru.Helpers {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Asynchronous I/O is not supported..
|
||||
/// </summary>
|
||||
internal static string Asynchronous_IO_is_not_supported {
|
||||
get {
|
||||
return ResourceManager.GetString("Asynchronous_IO_is_not_supported", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot read past stream end..
|
||||
/// </summary>
|
||||
internal static string Cannot_read_past_stream_end {
|
||||
get {
|
||||
return ResourceManager.GetString("Cannot_read_past_stream_end", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot seek after stream end..
|
||||
/// </summary>
|
||||
internal static string Cannot_seek_after_stream_end {
|
||||
get {
|
||||
return ResourceManager.GetString("Cannot_seek_after_stream_end", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot seek before stream start..
|
||||
/// </summary>
|
||||
internal static string Cannot_seek_before_stream_start {
|
||||
get {
|
||||
return ResourceManager.GetString("Cannot_seek_before_stream_start", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot set position past stream end..
|
||||
/// </summary>
|
||||
internal static string Cannot_set_position_past_stream_end {
|
||||
get {
|
||||
return ResourceManager.GetString("Cannot_set_position_past_stream_end", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Cannot write past stream end..
|
||||
/// </summary>
|
||||
internal static string Cannot_write_past_stream_end {
|
||||
get {
|
||||
return ResourceManager.GetString("Cannot_write_past_stream_end", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to End can't be a negative number..
|
||||
/// </summary>
|
||||
internal static string End_cant_be_a_negative_number {
|
||||
get {
|
||||
return ResourceManager.GetString("End_cant_be_a_negative_number", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to End is after stream end..
|
||||
/// </summary>
|
||||
internal static string End_is_after_stream_end {
|
||||
get {
|
||||
return ResourceManager.GetString("End_is_after_stream_end", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Growing OffsetStream is not supported..
|
||||
/// </summary>
|
||||
internal static string Growing_OffsetStream_is_not_supported {
|
||||
get {
|
||||
return ResourceManager.GetString("Growing_OffsetStream_is_not_supported", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Length of value array must not be more than length of destination.
|
||||
/// </summary>
|
||||
@@ -68,6 +149,24 @@ namespace Aaru.Helpers {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Non-readable streams are not supported.
|
||||
/// </summary>
|
||||
internal static string Non_readable_streams_are_not_supported {
|
||||
get {
|
||||
return ResourceManager.GetString("Non_readable_streams_are_not_supported", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Non-seekable streams are not supported.
|
||||
/// </summary>
|
||||
internal static string Non_seekable_streams_are_not_supported {
|
||||
get {
|
||||
return ResourceManager.GetString("Non_seekable_streams_are_not_supported", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Offset.
|
||||
/// </summary>
|
||||
@@ -76,5 +175,23 @@ namespace Aaru.Helpers {
|
||||
return ResourceManager.GetString("Offset", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to Start can't be a negative number..
|
||||
/// </summary>
|
||||
internal static string Start_cant_be_a_negative_number {
|
||||
get {
|
||||
return ResourceManager.GetString("Start_cant_be_a_negative_number", resourceCulture);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to This stream is read-only.
|
||||
/// </summary>
|
||||
internal static string This_stream_is_read_only {
|
||||
get {
|
||||
return ResourceManager.GetString("This_stream_is_read_only", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,4 +22,43 @@
|
||||
<data name="Length_of_value_array_must_not_be_more_than_length_of_destination" xml:space="preserve">
|
||||
<value>La longitud de una colección no puede ser mayor que la longitud del destino</value>
|
||||
</data>
|
||||
<data name="Non_readable_streams_are_not_supported" xml:space="preserve">
|
||||
<value>Las secuencias no legíbles no están soportadas.</value>
|
||||
</data>
|
||||
<data name="Non_seekable_streams_are_not_supported" xml:space="preserve">
|
||||
<value>Las secuencias no posicionables no están soportadas.</value>
|
||||
</data>
|
||||
<data name="Cannot_read_past_stream_end" xml:space="preserve">
|
||||
<value>No se puede leer más allá del final de la secuencia.</value>
|
||||
</data>
|
||||
<data name="Cannot_seek_after_stream_end" xml:space="preserve">
|
||||
<value>No se puede posicionar después del final de la secuencia.</value>
|
||||
</data>
|
||||
<data name="Cannot_seek_before_stream_start" xml:space="preserve">
|
||||
<value>No se puede posicionar antes del comienzo de la secuencia.</value>
|
||||
</data>
|
||||
<data name="Cannot_set_position_past_stream_end" xml:space="preserve">
|
||||
<value>No se puede establecer la posición más allá del final de la secuencia.</value>
|
||||
</data>
|
||||
<data name="Cannot_write_past_stream_end" xml:space="preserve">
|
||||
<value>No se puede escribir después del final de la secuencia.</value>
|
||||
</data>
|
||||
<data name="End_cant_be_a_negative_number" xml:space="preserve">
|
||||
<value>El final no puede ser un número negativo.</value>
|
||||
</data>
|
||||
<data name="End_is_after_stream_end" xml:space="preserve">
|
||||
<value>El final está después del final de la secuencia.</value>
|
||||
</data>
|
||||
<data name="Growing_OffsetStream_is_not_supported" xml:space="preserve">
|
||||
<value>No se puede agrandar un OffsetStream.</value>
|
||||
</data>
|
||||
<data name="Asynchronous_IO_is_not_supported" xml:space="preserve">
|
||||
<value>E/S asíncrona no soportada.</value>
|
||||
</data>
|
||||
<data name="Start_cant_be_a_negative_number" xml:space="preserve">
|
||||
<value>El comienzo no puede ser un número negativo.</value>
|
||||
</data>
|
||||
<data name="This_stream_is_read_only" xml:space="preserve">
|
||||
<value>Esta secuencia es de sólo lectura.</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -29,4 +29,43 @@
|
||||
<data name="Offset" xml:space="preserve">
|
||||
<value>Offset</value>
|
||||
</data>
|
||||
<data name="Start_cant_be_a_negative_number" xml:space="preserve">
|
||||
<value>Start can't be a negative number.</value>
|
||||
</data>
|
||||
<data name="End_cant_be_a_negative_number" xml:space="preserve">
|
||||
<value>End can't be a negative number.</value>
|
||||
</data>
|
||||
<data name="End_is_after_stream_end" xml:space="preserve">
|
||||
<value>End is after stream end.</value>
|
||||
</data>
|
||||
<data name="Cannot_set_position_past_stream_end" xml:space="preserve">
|
||||
<value>Cannot set position past stream end.</value>
|
||||
</data>
|
||||
<data name="Cannot_read_past_stream_end" xml:space="preserve">
|
||||
<value>Cannot read past stream end.</value>
|
||||
</data>
|
||||
<data name="Cannot_write_past_stream_end" xml:space="preserve">
|
||||
<value>Cannot write past stream end.</value>
|
||||
</data>
|
||||
<data name="Growing_OffsetStream_is_not_supported" xml:space="preserve">
|
||||
<value>Growing OffsetStream is not supported.</value>
|
||||
</data>
|
||||
<data name="Cannot_seek_before_stream_start" xml:space="preserve">
|
||||
<value>Cannot seek before stream start.</value>
|
||||
</data>
|
||||
<data name="Cannot_seek_after_stream_end" xml:space="preserve">
|
||||
<value>Cannot seek after stream end.</value>
|
||||
</data>
|
||||
<data name="Non_seekable_streams_are_not_supported" xml:space="preserve">
|
||||
<value>Non-seekable streams are not supported</value>
|
||||
</data>
|
||||
<data name="Non_readable_streams_are_not_supported" xml:space="preserve">
|
||||
<value>Non-readable streams are not supported</value>
|
||||
</data>
|
||||
<data name="Asynchronous_IO_is_not_supported" xml:space="preserve">
|
||||
<value>Asynchronous I/O is not supported.</value>
|
||||
</data>
|
||||
<data name="This_stream_is_read_only" xml:space="preserve">
|
||||
<value>This stream is read-only</value>
|
||||
</data>
|
||||
</root>
|
||||
Reference in New Issue
Block a user