Vanara/Core/BkwdComp/UnmanagedMemoryStream.PreNe...

804 lines
29 KiB
C#

#if NET20 || NET35
using System.Diagnostics.Contracts;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Threading;
using System.Threading.Tasks;
using Vanara.Extensions;
namespace System.IO
{
/// <summary>Provides access to unmanaged blocks of memory from managed code.</summary>
/// <seealso cref="System.IO.Stream"/>
public class UnmanagedMemoryStream : Stream
{
internal bool _isOpen;
private FileAccess _access;
[SecurityCritical]
private SafeBuffer _buffer;
private long _capacity;
[NonSerialized]
private Task<int> _lastReadTask;
private long _length;
[SecurityCritical]
private unsafe byte* _mem;
private long _offset;
private long _position;
/// <summary>
/// Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class in a safe buffer with a specified offset and length..
/// </summary>
/// <param name="buffer">The buffer to contain the unmanaged memory stream.</param>
/// <param name="offset">The byte position in the buffer at which to start the unmanaged memory stream.</param>
/// <param name="length">The length of the unmanaged memory stream.</param>
[SecuritySafeCritical]
public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length) => Initialize(buffer, offset, length, FileAccess.Read, false);
/// <summary>
/// Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class in a safe buffer with a specified offset, length, and
/// file access.
/// </summary>
/// <param name="buffer">The buffer to contain the unmanaged memory stream.</param>
/// <param name="offset">The byte position in the buffer at which to start the unmanaged memory stream.</param>
/// <param name="length">The length of the unmanaged memory stream.</param>
/// <param name="access">The mode of file access to the unmanaged memory stream.</param>
[SecuritySafeCritical]
public UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access) => Initialize(buffer, offset, length, access, false);
/// <summary>
/// Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class using the specified location and memory length.
/// </summary>
/// <param name="pointer">A pointer to an unmanaged memory location.</param>
/// <param name="length">The length of the memory to use.</param>
[SecurityCritical]
public unsafe UnmanagedMemoryStream(byte* pointer, long length) => Initialize(pointer, length, length, FileAccess.Read, false);
/// <summary>
/// Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class using the specified location, memory length, total
/// amount of memory, and file access values.
/// </summary>
/// <param name="pointer">A pointer to an unmanaged memory location.</param>
/// <param name="length">The length of the memory to use.</param>
/// <param name="capacity">The total amount of memory assigned to the stream.</param>
/// <param name="access">One of the <see cref="FileAccess"/> values.</param>
[SecurityCritical]
public unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access) => Initialize(pointer, length, capacity, access, false);
[SecurityCritical]
internal UnmanagedMemoryStream(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck) => Initialize(buffer, offset, length, access, skipSecurityCheck);
[SecurityCritical]
internal unsafe UnmanagedMemoryStream(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck) => Initialize(pointer, length, capacity, access, skipSecurityCheck);
/// <summary>Initializes a new instance of the <see cref="UnmanagedMemoryStream"/> class.</summary>
[SecuritySafeCritical]
protected UnmanagedMemoryStream()
{
unsafe
{
_mem = null;
}
_isOpen = false;
}
/// <summary>Gets a value indicating whether a stream supports reading.</summary>
public override bool CanRead
{
[Pure]
get => _isOpen && (_access & FileAccess.Read) != 0;
}
/// <summary>Gets a value indicating whether a stream supports seeking.</summary>
public override bool CanSeek
{
[Pure]
get => _isOpen;
}
/// <summary>Gets a value indicating whether a stream supports writing.</summary>
public override bool CanWrite
{
[Pure]
get => _isOpen && (_access & FileAccess.Write) != 0;
}
/// <summary>Gets the stream length (size) or the total amount of memory assigned to a stream (capacity).</summary>
/// <value>The size or capacity of the stream.</value>
public long Capacity
{
get
{
if (!_isOpen) ErrorStreamIsClosed();
return _capacity;
}
}
/// <summary>Gets the length of the data in a stream.</summary>
/// <value>The length of the data in the stream.</value>
public override long Length
{
get
{
if (!_isOpen) ErrorStreamIsClosed();
return Interlocked.Read(ref _length);
}
}
/// <summary>Gets or sets the position within the current stream.</summary>
public override long Position
{
get
{
if (!CanSeek) ErrorStreamIsClosed();
Contract.EndContractBlock();
return Interlocked.Read(ref _position);
}
[SecuritySafeCritical]
set
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
Contract.EndContractBlock();
if (!CanSeek) ErrorStreamIsClosed();
unsafe
{
// On 32 bit machines, ensure we don't wrap around.
if (value > int.MaxValue || _mem + value < _mem)
throw new ArgumentOutOfRangeException(nameof(value), ResourceHelper.GetString("ArgumentOutOfRange_StreamLength"));
}
Interlocked.Exchange(ref _position, value);
}
}
/// <summary>Gets or sets a byte pointer to a stream based on the current position in the stream.</summary>
/// <value>A byte pointer.</value>
public unsafe byte* PositionPointer
{
[SecurityCritical] // auto-generated_required
get
{
if (_buffer != null)
throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UmsSafeBuffer"));
// Use a temp to avoid a race
var pos = Interlocked.Read(ref _position);
if (pos > _capacity)
throw new IndexOutOfRangeException(ResourceHelper.GetString("IndexOutOfRange_UMSPosition"));
var ptr = _mem + pos;
if (!_isOpen) ErrorStreamIsClosed();
return ptr;
}
[SecurityCritical] // auto-generated_required
set
{
if (_buffer != null)
throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UmsSafeBuffer"));
if (!_isOpen)
ErrorStreamIsClosed();
if (value < _mem)
throw new IOException(ResourceHelper.GetString("IO.IO_SeekBeforeBegin"));
Interlocked.Exchange(ref _position, value - _mem);
}
}
internal unsafe byte* Pointer
{
[SecurityCritical]
get => _buffer == null ? _mem : throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UmsSafeBuffer"));
}
/// <summary>Clears all buffers for this stream and causes any buffered data to be written to the underlying device.</summary>
public override void Flush()
{
if (!_isOpen) ErrorStreamIsClosed();
}
/// <summary>
/// Asynchronously clears all buffers for this stream, causes any buffered data to be written to the underlying device, and monitors
/// cancellation requests.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is None.</param>
/// <returns>A task that represents the asynchronous flush operation.</returns>
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public virtual Task FlushAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled(cancellationToken);
try
{
Flush();
return Task.CompletedTask;
}
catch (Exception ex)
{
return Task.FromException(ex);
}
}
/// <summary>
/// When overridden in a derived class, reads a sequence of bytes from the current stream and advances the position within the stream
/// by the number of bytes read.
/// </summary>
/// <param name="buffer">
/// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between
/// <paramref name="offset"/> and ( <paramref name="offset"/> + <paramref name="count"/> - 1) replaced by the bytes read from the
/// current source.
/// </param>
/// <param name="offset">
/// The zero-based byte offset in <paramref name="buffer"/> at which to begin storing the data read from the current stream.
/// </param>
/// <param name="count">The maximum number of bytes to be read from the current stream.</param>
/// <returns>
/// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not
/// currently available, or zero (0) if the end of the stream has been reached.
/// </returns>
[SecuritySafeCritical]
public override int Read([In, Out] byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer), ResourceHelper.GetString("ArgumentNull_Buffer"));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.Length - offset < count)
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
Contract.EndContractBlock(); // Keep this in sync with contract validation in ReadAsync
if (!_isOpen) ErrorStreamIsClosed();
if (!CanRead) ErrorReadNotSupported();
// Use a local variable to avoid a race where another thread changes our position after we decide we can read some bytes.
var pos = Interlocked.Read(ref _position);
var len = Interlocked.Read(ref _length);
var n = len - pos;
if (n > count)
n = count;
if (n <= 0)
return 0;
var nInt = (int)n; // Safe because n <= count, which is an Int32
if (nInt < 0)
nInt = 0; // _position could be beyond EOF
Contract.Assert(pos + nInt >= 0, "_position + n >= 0"); // len is less than 2^63 -1.
if (_buffer != null)
{
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
BufferMemcpy(buffer, offset, pointer + pos + _offset, 0, nInt);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
else
{
unsafe
{
BufferMemcpy(buffer, offset, _mem + pos, 0, nInt);
}
}
Interlocked.Exchange(ref _position, pos + n);
return nInt;
}
/// <summary>
/// Asynchronously reads a sequence of bytes from the current stream and advances the position within the stream by the number of
/// bytes read.
/// </summary>
/// <param name="buffer">The buffer to write the data into.</param>
/// <param name="offset">The byte offset in buffer at which to begin writing data from the stream.</param>
/// <param name="count">The maximum number of bytes to read.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is None.</param>
/// <returns>
/// A task that represents the asynchronous read operation. The value of the TResult parameter contains the total number of bytes
/// read into the buffer. The result value can be less than the number of bytes requested if the number of bytes currently available
/// is less than the requested number, or it can be 0 (zero) if the end of the stream has been reached.
/// </returns>
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public virtual Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer), ResourceHelper.GetString("ArgumentNull_Buffer"));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.Length - offset < count)
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
Contract.EndContractBlock(); // contract validation copied from Read(...)
if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled<int>(cancellationToken);
try
{
var n = Read(buffer, offset, count);
var t = _lastReadTask;
return t != null && t.Result == n ? t : _lastReadTask = Task.FromResult(n);
}
catch (Exception ex)
{
Contract.Assert(!(ex is OperationCanceledException));
return Task.FromException<int>(ex);
}
}
/// <summary>
/// Reads a byte from the stream and advances the position within the stream by one byte, or returns -1 if at the end of the stream.
/// </summary>
/// <returns>The unsigned byte cast to an Int32, or -1 if at the end of the stream.</returns>
[SecuritySafeCritical]
public override int ReadByte()
{
if (!_isOpen) ErrorStreamIsClosed();
if (!CanRead) ErrorReadNotSupported();
var pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
var len = Interlocked.Read(ref _length);
if (pos >= len)
return -1;
Interlocked.Exchange(ref _position, pos + 1);
int result;
if (_buffer != null)
{
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = *(pointer + pos + _offset);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
else
{
unsafe
{
result = _mem[pos];
}
}
return result;
}
/// <summary>When overridden in a derived class, sets the position within the current stream.</summary>
/// <param name="offset">A byte offset relative to the origin parameter.</param>
/// <param name="loc">A value of type SeekOrigin indicating the reference point used to obtain the new position.</param>
/// <returns>The new position within the current stream.</returns>
public override long Seek(long offset, SeekOrigin loc)
{
if (!_isOpen) ErrorStreamIsClosed();
switch (loc)
{
case SeekOrigin.Begin:
if (offset < 0)
throw new IOException(ResourceHelper.GetString("IO.IO_SeekBeforeBegin"));
Interlocked.Exchange(ref _position, offset);
break;
case SeekOrigin.Current:
var pos = Interlocked.Read(ref _position);
if (offset + pos < 0)
throw new IOException(ResourceHelper.GetString("IO.IO_SeekBeforeBegin"));
Interlocked.Exchange(ref _position, offset + pos);
break;
case SeekOrigin.End:
var len = Interlocked.Read(ref _length);
if (len + offset < 0)
throw new IOException(ResourceHelper.GetString("IO.IO_SeekBeforeBegin"));
Interlocked.Exchange(ref _position, len + offset);
break;
default:
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidSeekOrigin"));
}
var finalPos = Interlocked.Read(ref _position);
Contract.Assert(finalPos >= 0, "_position >= 0");
return finalPos;
}
/// <summary>When overridden in a derived class, sets the length of the current stream.</summary>
/// <param name="value">The desired length of the current stream in bytes.</param>
[SecuritySafeCritical]
public override void SetLength(long value)
{
if (value < 0)
throw new ArgumentOutOfRangeException(nameof(value), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
Contract.EndContractBlock();
if (_buffer != null)
throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UmsSafeBuffer"));
if (!_isOpen) ErrorStreamIsClosed();
if (!CanWrite) ErrorWriteNotSupported();
if (value > _capacity)
throw new IOException(ResourceHelper.GetString("IO.IO_FixedCapacity"));
var pos = Interlocked.Read(ref _position);
var len = Interlocked.Read(ref _length);
if (value > len)
{
unsafe
{
BufferZeroMemory(_mem + len, value - len);
}
}
Interlocked.Exchange(ref _length, value);
if (pos > value)
{
Interlocked.Exchange(ref _position, value);
}
}
/// <summary>
/// When overridden in a derived class, writes a sequence of bytes to the current stream and advances the current position within
/// this stream by the number of bytes written.
/// </summary>
/// <param name="buffer">
/// An array of bytes. This method copies <paramref name="count"/> bytes from <paramref name="buffer"/> to the current stream.
/// </param>
/// <param name="offset">
/// The zero-based byte offset in <paramref name="buffer"/> at which to begin copying bytes to the current stream.
/// </param>
/// <param name="count">The number of bytes to be written to the current stream.</param>
[SecuritySafeCritical]
public override void Write(byte[] buffer, int offset, int count)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer), ResourceHelper.GetString("ArgumentNull_Buffer"));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.Length - offset < count)
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
Contract.EndContractBlock(); // Keep contract validation in sync with WriteAsync(..)
if (!_isOpen) ErrorStreamIsClosed();
if (!CanWrite) ErrorWriteNotSupported();
var pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
var len = Interlocked.Read(ref _length);
var n = pos + count;
// Check for overflow
if (n < 0)
throw new IOException(ResourceHelper.GetString("IO.IO_StreamTooLong"));
if (n > _capacity)
{
throw new NotSupportedException(ResourceHelper.GetString("IO.IO_FixedCapacity"));
}
if (_buffer == null)
{
// Check to see whether we are now expanding the stream and must zero any memory in the middle.
if (pos > len)
{
unsafe
{
BufferZeroMemory(_mem + len, pos - len);
}
}
// set length after zeroing memory to avoid race condition of accessing unzeroed memory
if (n > len)
{
Interlocked.Exchange(ref _length, n);
}
}
if (_buffer != null)
{
var bytesLeft = _capacity - pos;
if (bytesLeft < count)
{
throw new ArgumentException(ResourceHelper.GetString("Arg_BufferTooSmall"));
}
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
BufferMemcpy(pointer + pos + _offset, 0, buffer, offset, count);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
else
{
unsafe
{
BufferMemcpy(_mem + pos, 0, buffer, offset, count);
}
}
Interlocked.Exchange(ref _position, n);
}
/// <summary>
/// Asynchronously writes a sequence of bytes to the current stream, advances the current position within this stream by the number
/// of bytes written, and monitors cancellation requests.
/// </summary>
/// <param name="buffer">The buffer to write data from.</param>
/// <param name="offset">The zero-based byte offset in buffer from which to begin copying bytes to the stream.</param>
/// <param name="count">The maximum number of bytes to write.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is None.</param>
/// <returns>A task that represents the asynchronous write operation.</returns>
[HostProtection(ExternalThreading = true)]
[ComVisible(false)]
public virtual Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer), ResourceHelper.GetString("ArgumentNull_Buffer"));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (count < 0)
throw new ArgumentOutOfRangeException(nameof(count), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.Length - offset < count)
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidOffLen"));
Contract.EndContractBlock(); // contract validation copied from Write(..)
if (cancellationToken.IsCancellationRequested)
return Task.FromCanceled(cancellationToken);
try
{
Write(buffer, offset, count);
return Task.CompletedTask;
}
catch (Exception ex)
{
Contract.Assert(!(ex is OperationCanceledException));
return Task.FromException<int>(ex);
}
}
/// <summary>Writes a byte to the current position in the stream and advances the position within the stream by one byte.</summary>
/// <param name="value">The byte to write to the stream.</param>
[SecuritySafeCritical]
public override void WriteByte(byte value)
{
if (!_isOpen) ErrorStreamIsClosed();
if (!CanWrite) ErrorWriteNotSupported();
var pos = Interlocked.Read(ref _position); // Use a local to avoid a race condition
var len = Interlocked.Read(ref _length);
var n = pos + 1;
if (pos >= len)
{
// Check for overflow
if (n < 0)
throw new IOException(ResourceHelper.GetString("IO.IO_StreamTooLong"));
if (n > _capacity)
throw new NotSupportedException(ResourceHelper.GetString("IO.IO_FixedCapacity"));
// Check to see whether we are now expanding the stream and must zero any memory in the middle. don't do if created from SafeBuffer
if (_buffer == null)
{
if (pos > len)
{
unsafe
{
BufferZeroMemory(_mem + len, pos - len);
}
}
// set length after zeroing memory to avoid race condition of accessing unzeroed memory
Interlocked.Exchange(ref _length, n);
}
}
if (_buffer != null)
{
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
*(pointer + pos + _offset) = value;
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
else
{
unsafe
{
_mem[pos] = value;
}
}
Interlocked.Exchange(ref _position, n);
}
[SecurityCritical]
internal void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access, bool skipSecurityCheck)
{
if (buffer == null)
throw new ArgumentNullException(nameof(buffer));
if (offset < 0)
throw new ArgumentOutOfRangeException(nameof(offset), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (length < 0)
throw new ArgumentOutOfRangeException(nameof(length), ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (buffer.ByteLength < (ulong)(offset + length))
throw new ArgumentException(ResourceHelper.GetString("Argument_InvalidSafeBufferOffLen"));
if (access < FileAccess.Read || access > FileAccess.ReadWrite)
throw new ArgumentOutOfRangeException(nameof(access));
Contract.EndContractBlock();
if (_isOpen)
throw new InvalidOperationException(ResourceHelper.GetString("InvalidOperation_CalledTwice"));
if (!skipSecurityCheck)
#pragma warning disable 618
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
#pragma warning restore 618
// check for wraparound
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
buffer.AcquirePointer(ref pointer);
if (pointer + offset + length < pointer)
{
throw new ArgumentException(ResourceHelper.GetString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
}
}
finally
{
if (pointer != null)
{
buffer.ReleasePointer();
}
}
}
_offset = offset;
_buffer = buffer;
_length = length;
_capacity = length;
_access = access;
_isOpen = true;
}
[SecurityCritical]
internal unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access, bool skipSecurityCheck)
{
if (pointer == null)
throw new ArgumentNullException(nameof(pointer));
if (length < 0 || capacity < 0)
throw new ArgumentOutOfRangeException(length < 0 ? "length" : "capacity", ResourceHelper.GetString("ArgumentOutOfRange_NeedNonNegNum"));
if (length > capacity)
throw new ArgumentOutOfRangeException(nameof(length), ResourceHelper.GetString("ArgumentOutOfRange_LengthGreaterThanCapacity"));
Contract.EndContractBlock();
// Check for wraparound.
if ((byte*)((long)pointer + capacity) < pointer)
throw new ArgumentOutOfRangeException(nameof(capacity), ResourceHelper.GetString("ArgumentOutOfRange_UnmanagedMemStreamWrapAround"));
if (access < FileAccess.Read || access > FileAccess.ReadWrite)
throw new ArgumentOutOfRangeException(nameof(access), ResourceHelper.GetString("ArgumentOutOfRange_Enum"));
if (_isOpen)
throw new InvalidOperationException(ResourceHelper.GetString("InvalidOperation_CalledTwice"));
if (!skipSecurityCheck)
#pragma warning disable 618
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
#pragma warning restore 618
_mem = pointer;
_offset = 0;
_length = length;
_capacity = capacity;
_access = access;
_isOpen = true;
}
/// <summary>
/// Releases the unmanaged resources used by the <see cref="T:System.IO.Stream"/> and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
[SecuritySafeCritical]
protected override void Dispose(bool disposing)
{
_isOpen = false;
unsafe { _mem = null; }
// Stream allocates WaitHandles for async calls. So for correctness call base.Dispose(disposing) for better perf, avoiding
// waiting for the finalizers to run on those types.
base.Dispose(disposing);
}
/// <summary>
/// Initializes a new instance of the UnmanagedMemoryStream class in a safe buffer with a specified offset, length, and file access.
/// </summary>
/// <param name="buffer">The buffer to contain the unmanaged memory stream.</param>
/// <param name="offset">The byte position in the buffer at which to start the unmanaged memory stream.</param>
/// <param name="length">The length of the unmanaged memory stream.</param>
/// <param name="access">The mode of file access to the unmanaged memory stream.</param>
[SecuritySafeCritical]
protected void Initialize(SafeBuffer buffer, long offset, long length, FileAccess access) => Initialize(buffer, offset, length, access, false);
/// <summary>Initializes a new instance of the UnmanagedMemoryStream class by using a pointer to an unmanaged memory location.</summary>
/// <param name="pointer">A pointer to an unmanaged memory location.</param>
/// <param name="length">The length of the memory to use.</param>
/// <param name="capacity">The total amount of memory assigned to the stream.</param>
/// <param name="access">One of the <see cref="FileAccess"/> values.</param>
[SecurityCritical]
protected unsafe void Initialize(byte* pointer, long length, long capacity, FileAccess access) => Initialize(pointer, length, capacity, access, false);
[SecurityCritical]
private static unsafe void BufferMemcpy(byte[] dest, int destIndex, byte* src, int srcIndex, int len)
{
Contract.Assert(srcIndex >= 0 && destIndex >= 0 && len >= 0, "Index and length must be non-negative!");
Contract.Assert(dest.Length - destIndex >= len, "not enough bytes in dest");
if (len == 0)
return;
Marshal.Copy((IntPtr)src, dest, srcIndex, len);
}
[SecurityCritical]
private static unsafe void BufferMemcpy(byte* dest, int destIndex, byte[] src, int srcIndex, int len)
{
Contract.Assert(srcIndex >= 0 && destIndex >= 0 && len >= 0, "Index and length must be non-negative!");
Contract.Assert(src.Length - srcIndex >= len, "not enough bytes in src");
if (len == 0)
return;
Marshal.Copy(src, srcIndex, (IntPtr)dest, len);
}
private static unsafe void BufferZeroMemory(byte* src, long len) => ((IntPtr)src).FillMemory(0, len);
private static void ErrorReadNotSupported() => throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UnreadableStream"));
private static void ErrorStreamIsClosed() => throw new ObjectDisposedException(null, ResourceHelper.GetString("ObjectDisposed_StreamClosed"));
private static void ErrorWriteNotSupported() => throw new NotSupportedException(ResourceHelper.GetString("NotSupported_UnwritableStream"));
}
}
#endif