// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
/*============================================================
**
**
**
**
** Purpose: Provides a fast, AV free, cross-language way of
** accessing unmanaged memory in a random fashion.
**
**
===========================================================*/
using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.Versioning;
using Microsoft.Win32.SafeHandles;
using System.Diagnostics;
using System.Diagnostics.Contracts;
namespace System.IO
{
/// Perf notes: ReadXXX, WriteXXX (for basic types) acquire and release the
/// SafeBuffer pointer rather than relying on generic Read(T) from SafeBuffer because
/// this gives better throughput; benchmarks showed about 12-15% better.
public class UnmanagedMemoryAccessor : IDisposable
{
private SafeBuffer _buffer;
private Int64 _offset;
[ContractPublicPropertyName("Capacity")]
private Int64 _capacity;
private FileAccess _access;
private bool _isOpen;
private bool _canRead;
private bool _canWrite;
protected UnmanagedMemoryAccessor()
{
_isOpen = false;
}
#region SafeBuffer ctors and initializers
//
//
//
public UnmanagedMemoryAccessor(SafeBuffer buffer, Int64 offset, Int64 capacity)
{
Initialize(buffer, offset, capacity, FileAccess.Read);
}
public UnmanagedMemoryAccessor(SafeBuffer buffer, Int64 offset, Int64 capacity, FileAccess access)
{
Initialize(buffer, offset, capacity, access);
}
protected void Initialize(SafeBuffer buffer, Int64 offset, Int64 capacity, FileAccess access)
{
if (buffer == null)
{
throw new ArgumentNullException(nameof(buffer));
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
}
if (capacity < 0)
{
throw new ArgumentOutOfRangeException(nameof(capacity), SR.ArgumentOutOfRange_NeedNonNegNum);
}
if (buffer.ByteLength < (UInt64)(offset + capacity))
{
throw new ArgumentException(SR.Argument_OffsetAndCapacityOutOfBounds);
}
if (access < FileAccess.Read || access > FileAccess.ReadWrite)
{
throw new ArgumentOutOfRangeException(nameof(access));
}
Contract.EndContractBlock();
if (_isOpen)
{
throw new InvalidOperationException(SR.InvalidOperation_CalledTwice);
}
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
buffer.AcquirePointer(ref pointer);
if (((byte*)((Int64)pointer + offset + capacity)) < pointer)
{
throw new ArgumentException(SR.Argument_UnmanagedMemAccessorWrapAround);
}
}
finally
{
if (pointer != null)
{
buffer.ReleasePointer();
}
}
}
_offset = offset;
_buffer = buffer;
_capacity = capacity;
_access = access;
_isOpen = true;
_canRead = (_access & FileAccess.Read) != 0;
_canWrite = (_access & FileAccess.Write) != 0;
}
#endregion
public Int64 Capacity
{
get
{
return _capacity;
}
}
public bool CanRead
{
get
{
return _isOpen && _canRead;
}
}
public bool CanWrite
{
get
{
return _isOpen && _canWrite;
}
}
protected virtual void Dispose(bool disposing)
{
_isOpen = false;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected bool IsOpen
{
get { return _isOpen; }
}
public bool ReadBoolean(Int64 position)
{
int sizeOfType = sizeof(bool);
EnsureSafeToRead(position, sizeOfType);
byte b = InternalReadByte(position);
return b != 0;
}
public byte ReadByte(Int64 position)
{
int sizeOfType = sizeof(byte);
EnsureSafeToRead(position, sizeOfType);
return InternalReadByte(position);
}
public char ReadChar(Int64 position)
{
EnsureSafeToRead(position, sizeof(char));
char result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
// See comment above.
public Int16 ReadInt16(Int64 position)
{
EnsureSafeToRead(position, sizeof(Int16));
Int16 result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
public Int32 ReadInt32(Int64 position)
{
EnsureSafeToRead(position, sizeof(Int32));
Int32 result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
public Int64 ReadInt64(Int64 position)
{
EnsureSafeToRead(position, sizeof(Int64));
Int64 result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
public Decimal ReadDecimal(Int64 position)
{
const int ScaleMask = 0x00FF0000;
const int SignMask = unchecked((int)0x80000000);
EnsureSafeToRead(position, sizeof(Decimal));
int lo, mid, hi, flags;
unsafe
{
byte* pointer = null;
try
{
_buffer.AcquirePointer(ref pointer);
lo = Unsafe.ReadUnaligned(pointer + _offset + position);
mid = Unsafe.ReadUnaligned(pointer + _offset + position + 4);
hi = Unsafe.ReadUnaligned(pointer + _offset + position + 8);
flags = Unsafe.ReadUnaligned(pointer + _offset + position + 12);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
// Check for invalid Decimal values
if (!((flags & ~(SignMask | ScaleMask)) == 0 && (flags & ScaleMask) <= (28 << 16)))
{
throw new ArgumentException(SR.Arg_BadDecimal); // Throw same Exception type as Decimal(int[]) ctor for compat
}
bool isNegative = (flags & SignMask) != 0;
byte scale = (byte)(flags >> 16);
return new decimal(lo, mid, hi, isNegative, scale);
}
public Single ReadSingle(Int64 position)
{
EnsureSafeToRead(position, sizeof(Single));
Single result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
public Double ReadDouble(Int64 position)
{
EnsureSafeToRead(position, sizeof(Double));
Double result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
[CLSCompliant(false)]
public SByte ReadSByte(Int64 position)
{
int sizeOfType = sizeof(SByte);
EnsureSafeToRead(position, sizeOfType);
SByte result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = *((SByte*)(pointer + _offset + position));
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
[CLSCompliant(false)]
public UInt16 ReadUInt16(Int64 position)
{
EnsureSafeToRead(position, sizeof(UInt16));
UInt16 result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
[CLSCompliant(false)]
public UInt32 ReadUInt32(Int64 position)
{
EnsureSafeToRead(position, sizeof(UInt32));
UInt32 result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
[CLSCompliant(false)]
public UInt64 ReadUInt64(Int64 position)
{
EnsureSafeToRead(position, sizeof(UInt64));
UInt64 result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = Unsafe.ReadUnaligned(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
// Reads a struct of type T from unmanaged memory, into the reference pointed to by ref value.
// Note: this method is not safe, since it overwrites the contents of a structure, it can be
// used to modify the private members of a struct. Furthermore, using this with a struct that
// contains reference members will most likely cause the runtime to AV. Note, that despite
// various checks made by the C++ code used by Marshal.PtrToStructure, Marshal.PtrToStructure
// will still overwrite privates and will also crash the runtime when used with structs
// containing reference members. For this reason, I am sticking an UnmanagedCode requirement
// on this method to match Marshal.PtrToStructure.
// Alos note that this method is most performant when used with medium to large sized structs
// (larger than 8 bytes -- though this is number is JIT and architecture dependent). As
// such, it is best to use the ReadXXX methods for small standard types such as ints, longs,
// bools, etc.
public void Read(Int64 position, out T structure) where T : struct
{
if (position < 0)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
}
Contract.EndContractBlock();
if (!_isOpen)
{
throw new ObjectDisposedException("UnmanagedMemoryAccessor", SR.ObjectDisposed_ViewAccessorClosed);
}
if (!CanRead)
{
throw new NotSupportedException(SR.NotSupported_Reading);
}
UInt32 sizeOfT = Marshal.SizeOfType(typeof(T));
if (position > _capacity - sizeOfT)
{
if (position >= _capacity)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
}
else
{
throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToRead, typeof (T).FullName), nameof(position));
}
}
structure = _buffer.Read((UInt64)(_offset + position));
}
// Reads 'count' structs of type T from unmanaged memory, into 'array' starting at 'offset'.
// Note: this method is not safe, since it overwrites the contents of structures, it can
// be used to modify the private members of a struct. Furthermore, using this with a
// struct that contains reference members will most likely cause the runtime to AV. This
// is consistent with Marshal.PtrToStructure.
public int ReadArray(Int64 position, T[] array, Int32 offset, Int32 count) where T : struct
{
if (array == null)
{
throw new ArgumentNullException(nameof(array), "Buffer cannot be null.");
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
}
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
}
if (array.Length - offset < count)
{
throw new ArgumentException(SR.Argument_OffsetAndLengthOutOfBounds);
}
Contract.EndContractBlock();
if (!CanRead)
{
if (!_isOpen)
{
throw new ObjectDisposedException("UnmanagedMemoryAccessor", SR.ObjectDisposed_ViewAccessorClosed);
}
else
{
throw new NotSupportedException(SR.NotSupported_Reading);
}
}
if (position < 0)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
}
UInt32 sizeOfT = Marshal.AlignedSizeOf();
// only check position and ask for fewer Ts if count is too big
if (position >= _capacity)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
}
int n = count;
long spaceLeft = _capacity - position;
if (spaceLeft < 0)
{
n = 0;
}
else
{
ulong spaceNeeded = (ulong)(sizeOfT * count);
if ((ulong)spaceLeft < spaceNeeded)
{
n = (int)(spaceLeft / sizeOfT);
}
}
_buffer.ReadArray((UInt64)(_offset + position), array, offset, n);
return n;
}
// ************** Write Methods ****************/
// The following 13 WriteXXX methods write a value of type XXX into unmanaged memory at 'positon'.
// The bounds of the unmanaged memory are checked against to ensure that there is enough
// space after 'position' to write a value of type XXX. XXX can be a bool, byte, char, decimal,
// double, short, int, long, sbyte, float, ushort, uint, or ulong.
public void Write(Int64 position, bool value)
{
int sizeOfType = sizeof(bool);
EnsureSafeToWrite(position, sizeOfType);
byte b = (byte)(value ? 1 : 0);
InternalWrite(position, b);
}
public void Write(Int64 position, byte value)
{
int sizeOfType = sizeof(byte);
EnsureSafeToWrite(position, sizeOfType);
InternalWrite(position, value);
}
public void Write(Int64 position, char value)
{
EnsureSafeToWrite(position, sizeof(char));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
public void Write(Int64 position, Int16 value)
{
EnsureSafeToWrite(position, sizeof(Int16));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
public void Write(Int64 position, Int32 value)
{
EnsureSafeToWrite(position, sizeof(Int32));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
public void Write(Int64 position, Int64 value)
{
EnsureSafeToWrite(position, sizeof(Int64));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
public void Write(Int64 position, Decimal value)
{
EnsureSafeToWrite(position, sizeof(Decimal));
unsafe
{
int* valuePtr = (int*)(&value);
int flags = *valuePtr;
int hi = *(valuePtr + 1);
int lo = *(valuePtr + 2);
int mid = *(valuePtr + 3);
byte* pointer = null;
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, lo);
Unsafe.WriteUnaligned(pointer + _offset + position + 4, mid);
Unsafe.WriteUnaligned(pointer + _offset + position + 8, hi);
Unsafe.WriteUnaligned(pointer + _offset + position + 12, flags);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
public void Write(Int64 position, Single value)
{
EnsureSafeToWrite(position, sizeof(Single));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
public void Write(Int64 position, Double value)
{
EnsureSafeToWrite(position, sizeof(Double));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
[CLSCompliant(false)]
public void Write(Int64 position, SByte value)
{
EnsureSafeToWrite(position, sizeof(SByte));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
*((SByte*)(pointer + _offset + position)) = value;
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
[CLSCompliant(false)]
public void Write(Int64 position, UInt16 value)
{
int sizeOfType = sizeof(UInt16);
EnsureSafeToWrite(position, sizeOfType);
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
[CLSCompliant(false)]
public void Write(Int64 position, UInt32 value)
{
EnsureSafeToWrite(position, sizeof(UInt32));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
[CLSCompliant(false)]
public void Write(Int64 position, UInt64 value)
{
EnsureSafeToWrite(position, sizeof(UInt64));
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
Unsafe.WriteUnaligned(pointer + _offset + position, value);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
// Writes the struct pointed to by ref value into unmanaged memory. Note that this method
// is most performant when used with medium to large sized structs (larger than 8 bytes
// though this is number is JIT and architecture dependent). As such, it is best to use
// the WriteX methods for small standard types such as ints, longs, bools, etc.
public void Write(Int64 position, ref T structure) where T : struct
{
if (position < 0)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
}
Contract.EndContractBlock();
if (!_isOpen)
{
throw new ObjectDisposedException("UnmanagedMemoryAccessor", SR.ObjectDisposed_ViewAccessorClosed);
}
if (!CanWrite)
{
throw new NotSupportedException(SR.NotSupported_Writing);
}
UInt32 sizeOfT = Marshal.SizeOfType(typeof(T));
if (position > _capacity - sizeOfT)
{
if (position >= _capacity)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
}
else
{
throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToWrite, typeof (T).FullName), nameof(position));
}
}
_buffer.Write((UInt64)(_offset + position), structure);
}
// Writes 'count' structs of type T from 'array' (starting at 'offset') into unmanaged memory.
public void WriteArray(Int64 position, T[] array, Int32 offset, Int32 count) where T : struct
{
if (array == null)
{
throw new ArgumentNullException(nameof(array), "Buffer cannot be null.");
}
if (offset < 0)
{
throw new ArgumentOutOfRangeException(nameof(offset), SR.ArgumentOutOfRange_NeedNonNegNum);
}
if (count < 0)
{
throw new ArgumentOutOfRangeException(nameof(count), SR.ArgumentOutOfRange_NeedNonNegNum);
}
if (array.Length - offset < count)
{
throw new ArgumentException(SR.Argument_OffsetAndLengthOutOfBounds);
}
if (position < 0)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
}
if (position >= Capacity)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
}
Contract.EndContractBlock();
if (!_isOpen)
{
throw new ObjectDisposedException("UnmanagedMemoryAccessor", SR.ObjectDisposed_ViewAccessorClosed);
}
if (!CanWrite)
{
throw new NotSupportedException(SR.NotSupported_Writing);
}
_buffer.WriteArray((UInt64)(_offset + position), array, offset, count);
}
private byte InternalReadByte(Int64 position)
{
Debug.Assert(CanRead, "UMA not readable");
Debug.Assert(position >= 0, "position less than 0");
Debug.Assert(position <= _capacity - sizeof(byte), "position is greater than capacity - sizeof(byte)");
byte result;
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
result = *(pointer + _offset + position);
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
return result;
}
private void InternalWrite(Int64 position, byte value)
{
Debug.Assert(CanWrite, "UMA not writable");
Debug.Assert(position >= 0, "position less than 0");
Debug.Assert(position <= _capacity - sizeof(byte), "position is greater than capacity - sizeof(byte)");
unsafe
{
byte* pointer = null;
RuntimeHelpers.PrepareConstrainedRegions();
try
{
_buffer.AcquirePointer(ref pointer);
*(pointer + _offset + position) = value;
}
finally
{
if (pointer != null)
{
_buffer.ReleasePointer();
}
}
}
}
private void EnsureSafeToRead(Int64 position, int sizeOfType)
{
if (!_isOpen)
{
throw new ObjectDisposedException("UnmanagedMemoryAccessor", SR.ObjectDisposed_ViewAccessorClosed);
}
if (!CanRead)
{
throw new NotSupportedException(SR.NotSupported_Reading);
}
if (position < 0)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
}
Contract.EndContractBlock();
if (position > _capacity - sizeOfType)
{
if (position >= _capacity)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
}
else
{
throw new ArgumentException(SR.Argument_NotEnoughBytesToRead, nameof(position));
}
}
}
private void EnsureSafeToWrite(Int64 position, int sizeOfType)
{
if (!_isOpen)
{
throw new ObjectDisposedException("UnmanagedMemoryAccessor", SR.ObjectDisposed_ViewAccessorClosed);
}
if (!CanWrite)
{
throw new NotSupportedException(SR.NotSupported_Writing);
}
if (position < 0)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_NeedNonNegNum);
}
Contract.EndContractBlock();
if (position > _capacity - sizeOfType)
{
if (position >= _capacity)
{
throw new ArgumentOutOfRangeException(nameof(position), SR.ArgumentOutOfRange_PositionLessThanCapacityRequired);
}
else
{
throw new ArgumentException(SR.Format(SR.Argument_NotEnoughBytesToWrite, nameof(Byte)), nameof(position));
}
}
}
}
}