|
//------------------------------------------------------------------------------
// <copyright file="WebSocketBuffer.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation. All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
namespace System.Net.WebSockets
{
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
// This class helps to abstract the internal WebSocket buffer, which is used to interact with the native WebSocket
// protocol component (WSPC). It helps to shield the details of the layout and the involved pointer arithmetic.
// The internal WebSocket buffer also contains a segment, which is used by the WebSocketBase class to buffer
// payload (parsed by WSPC already) for the application, if the application requested fewer bytes than the
// WSPC returned. The internal buffer is pinned for the whole lifetime if this class.
// LAYOUT:
// | Native buffer | PayloadReceiveBuffer | PropertyBuffer |
// | RBS + SBS + 144 | RBS | PBS |
// | Only WSPC may modify | Only WebSocketBase may modify |
//
// *RBS = ReceiveBufferSize, *SBS = SendBufferSize
// *PBS = PropertyBufferSize (32-bit: 16, 64 bit: 20 bytes)
internal class WebSocketBuffer : IDisposable
{
private const int NativeOverheadBufferSize = 144;
internal const int MinSendBufferSize = 16;
internal const int MinReceiveBufferSize = 256;
internal const int MaxBufferSize = 64 * 1024;
private static readonly int s_SizeOfUInt = Marshal.SizeOf(typeof(uint));
private static readonly int s_SizeOfBool = Marshal.SizeOf(typeof(bool));
private static readonly int s_PropertyBufferSize = 2 * s_SizeOfUInt + s_SizeOfBool + IntPtr.Size;
private readonly int m_ReceiveBufferSize;
// Indicates the range of the pinned byte[] that can be used by the WSPC (nativeBuffer + pinnedSendBuffer)
private readonly long m_StartAddress;
private readonly long m_EndAddress;
private readonly GCHandle m_GCHandle;
private readonly ArraySegment<byte> m_InternalBuffer;
private readonly ArraySegment<byte> m_NativeBuffer;
private readonly ArraySegment<byte> m_PayloadBuffer;
private readonly ArraySegment<byte> m_PropertyBuffer;
private readonly int m_SendBufferSize;
private volatile int m_PayloadOffset;
private volatile WebSocketReceiveResult m_BufferedPayloadReceiveResult;
private long m_PinnedSendBufferStartAddress;
private long m_PinnedSendBufferEndAddress;
private ArraySegment<byte> m_PinnedSendBuffer;
private GCHandle m_PinnedSendBufferHandle;
private int m_StateWhenDisposing = int.MinValue;
private int m_SendBufferState;
private WebSocketBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize, int sendBufferSize)
{
Contract.Assert(internalBuffer != null, "'internalBuffer' MUST NOT be NULL.");
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize >= MinSendBufferSize,
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(receiveBufferSize <= MaxBufferSize,
"'receiveBufferSize' MUST NOT exceed " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize <= MaxBufferSize,
"'sendBufferSize' MUST NOT exceed " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
m_ReceiveBufferSize = receiveBufferSize;
m_SendBufferSize = sendBufferSize;
m_InternalBuffer = internalBuffer;
m_GCHandle = GCHandle.Alloc(internalBuffer.Array, GCHandleType.Pinned);
// Size of the internal buffer owned exclusively by the WSPC.
int nativeBufferSize = m_ReceiveBufferSize + m_SendBufferSize + NativeOverheadBufferSize;
m_StartAddress = Marshal.UnsafeAddrOfPinnedArrayElement(internalBuffer.Array, internalBuffer.Offset).ToInt64();
m_EndAddress = m_StartAddress + nativeBufferSize;
m_NativeBuffer = new ArraySegment<byte>(internalBuffer.Array, internalBuffer.Offset, nativeBufferSize);
m_PayloadBuffer = new ArraySegment<byte>(internalBuffer.Array,
m_NativeBuffer.Offset + m_NativeBuffer.Count,
m_ReceiveBufferSize);
m_PropertyBuffer = new ArraySegment<byte>(internalBuffer.Array,
m_PayloadBuffer.Offset + m_PayloadBuffer.Count,
s_PropertyBufferSize);
m_SendBufferState = SendBufferState.None;
}
internal static int SizeOfUInt
{
get { return s_SizeOfUInt; }
}
public int ReceiveBufferSize
{
get { return m_ReceiveBufferSize; }
}
public int SendBufferSize
{
get { return m_SendBufferSize; }
}
internal static WebSocketBuffer CreateClientBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize, int sendBufferSize)
{
Contract.Assert(internalBuffer.Count >= GetInternalBufferSize(receiveBufferSize, sendBufferSize, false),
"Array 'internalBuffer' is TOO SMALL. Call Validate before instantiating WebSocketBuffer.");
return new WebSocketBuffer(internalBuffer, receiveBufferSize, GetNativeSendBufferSize(sendBufferSize, false));
}
internal static WebSocketBuffer CreateServerBuffer(ArraySegment<byte> internalBuffer, int receiveBufferSize)
{
int sendBufferSize = GetNativeSendBufferSize(MinSendBufferSize, true);
Contract.Assert(internalBuffer.Count >= GetInternalBufferSize(receiveBufferSize, sendBufferSize, true),
"Array 'internalBuffer' is TOO SMALL. Call Validate before instantiating WebSocketBuffer.");
return new WebSocketBuffer(internalBuffer, receiveBufferSize, sendBufferSize);
}
public void Dispose(WebSocketState webSocketState)
{
if (Interlocked.CompareExchange(ref m_StateWhenDisposing, (int)webSocketState, int.MinValue) != int.MinValue)
{
return;
}
this.CleanUp();
}
public void Dispose()
{
this.Dispose(WebSocketState.None);
}
internal WebSocketProtocolComponent.Property[] CreateProperties(bool useZeroMaskingKey)
{
ThrowIfDisposed();
// serialize marshaled property values in the property segment of the internal buffer
// m_GCHandle.AddrOfPinnedObject() points to the address of m_InternalBuffer.Array
IntPtr internalBufferPtr = m_GCHandle.AddrOfPinnedObject();
int offset = m_PropertyBuffer.Offset;
Marshal.WriteInt32(internalBufferPtr, offset, m_ReceiveBufferSize);
offset += s_SizeOfUInt;
Marshal.WriteInt32(internalBufferPtr, offset, m_SendBufferSize);
offset += s_SizeOfUInt;
Marshal.WriteIntPtr(internalBufferPtr, offset, internalBufferPtr + m_InternalBuffer.Offset);
offset += IntPtr.Size;
Marshal.WriteInt32(internalBufferPtr, offset, useZeroMaskingKey ? (int)1 : (int)0);
int propertyCount = useZeroMaskingKey ? 4 : 3;
WebSocketProtocolComponent.Property[] properties =
new WebSocketProtocolComponent.Property[propertyCount];
// Calculate the pointers to the positions of the properties within the internal buffer
offset = m_PropertyBuffer.Offset;
properties[0] = new WebSocketProtocolComponent.Property()
{
Type = WebSocketProtocolComponent.PropertyType.ReceiveBufferSize,
PropertySize = (uint)s_SizeOfUInt,
PropertyData = IntPtr.Add(internalBufferPtr, offset)
};
offset += s_SizeOfUInt;
properties[1] = new WebSocketProtocolComponent.Property()
{
Type = WebSocketProtocolComponent.PropertyType.SendBufferSize,
PropertySize = (uint)s_SizeOfUInt,
PropertyData = IntPtr.Add(internalBufferPtr, offset)
};
offset += s_SizeOfUInt;
properties[2] = new WebSocketProtocolComponent.Property()
{
Type = WebSocketProtocolComponent.PropertyType.AllocatedBuffer,
PropertySize = (uint)m_NativeBuffer.Count,
PropertyData = IntPtr.Add(internalBufferPtr, offset)
};
offset += IntPtr.Size;
if (useZeroMaskingKey)
{
properties[3] = new WebSocketProtocolComponent.Property()
{
Type = WebSocketProtocolComponent.PropertyType.DisableMasking,
PropertySize = (uint)s_SizeOfBool,
PropertyData = IntPtr.Add(internalBufferPtr, offset)
};
}
return properties;
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal void PinSendBuffer(ArraySegment<byte> payload, out bool bufferHasBeenPinned)
{
bufferHasBeenPinned = false;
WebSocketHelpers.ValidateBuffer(payload.Array, payload.Offset, payload.Count);
int previousState = Interlocked.Exchange(ref m_SendBufferState, SendBufferState.SendPayloadSpecified);
if (previousState != SendBufferState.None)
{
Contract.Assert(false, "'m_SendBufferState' MUST BE 'None' at this point.");
// Indicates a violation in the API contract that could indicate
// memory corruption because the pinned sendbuffer is shared between managed and native code
throw new AccessViolationException();
}
m_PinnedSendBuffer = payload;
m_PinnedSendBufferHandle = GCHandle.Alloc(m_PinnedSendBuffer.Array, GCHandleType.Pinned);
bufferHasBeenPinned = true;
m_PinnedSendBufferStartAddress =
Marshal.UnsafeAddrOfPinnedArrayElement(m_PinnedSendBuffer.Array, m_PinnedSendBuffer.Offset).ToInt64();
m_PinnedSendBufferEndAddress = m_PinnedSendBufferStartAddress + m_PinnedSendBuffer.Count;
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal IntPtr ConvertPinnedSendPayloadToNative(ArraySegment<byte> payload)
{
return ConvertPinnedSendPayloadToNative(payload.Array, payload.Offset, payload.Count);
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal IntPtr ConvertPinnedSendPayloadToNative(byte[] buffer, int offset, int count)
{
if (!IsPinnedSendPayloadBuffer(buffer, offset, count))
{
// Indicates a violation in the API contract that could indicate
// memory corruption because the pinned sendbuffer is shared between managed and native code
throw new AccessViolationException();
}
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(m_PinnedSendBuffer.Array,
m_PinnedSendBuffer.Offset).ToInt64() == m_PinnedSendBufferStartAddress,
"'m_PinnedSendBuffer.Array' MUST be pinned during the entire send operation.");
return new IntPtr(m_PinnedSendBufferStartAddress + offset - m_PinnedSendBuffer.Offset);
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal ArraySegment<byte> ConvertPinnedSendPayloadFromNative(WebSocketProtocolComponent.Buffer buffer,
WebSocketProtocolComponent.BufferType bufferType)
{
if (!IsPinnedSendPayloadBuffer(buffer, bufferType))
{
// Indicates a violation in the API contract that could indicate
// memory corruption because the pinned sendbuffer is shared between managed and native code
throw new AccessViolationException();
}
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(m_PinnedSendBuffer.Array,
m_PinnedSendBuffer.Offset).ToInt64() == m_PinnedSendBufferStartAddress,
"'m_PinnedSendBuffer.Array' MUST be pinned during the entire send operation.");
IntPtr bufferData;
uint bufferSize;
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferSize);
int internalOffset = (int)(bufferData.ToInt64() - m_PinnedSendBufferStartAddress);
return new ArraySegment<byte>(m_PinnedSendBuffer.Array, m_PinnedSendBuffer.Offset + internalOffset, (int)bufferSize);
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
private bool IsPinnedSendPayloadBuffer(byte[] buffer, int offset, int count)
{
if (m_SendBufferState != SendBufferState.SendPayloadSpecified)
{
return false;
}
return object.ReferenceEquals(buffer, m_PinnedSendBuffer.Array) &&
offset >= m_PinnedSendBuffer.Offset &&
offset + count <= m_PinnedSendBuffer.Offset + m_PinnedSendBuffer.Count;
}
// This method is not thread safe. It must only be called after enforcing at most 1 outstanding send operation
internal bool IsPinnedSendPayloadBuffer(WebSocketProtocolComponent.Buffer buffer,
WebSocketProtocolComponent.BufferType bufferType)
{
if (m_SendBufferState != SendBufferState.SendPayloadSpecified)
{
return false;
}
IntPtr bufferData;
uint bufferSize;
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferSize);
long nativeBufferStartAddress = bufferData.ToInt64();
long nativeBufferEndAddress = nativeBufferStartAddress + bufferSize;
return nativeBufferStartAddress >= m_PinnedSendBufferStartAddress &&
nativeBufferEndAddress >= m_PinnedSendBufferStartAddress &&
nativeBufferStartAddress <= m_PinnedSendBufferEndAddress &&
nativeBufferEndAddress <= m_PinnedSendBufferEndAddress;
}
// This method is only thread safe for ----s between Abort and at most 1 uncompleted send operation
internal void ReleasePinnedSendBuffer()
{
int previousState = Interlocked.Exchange(ref m_SendBufferState, SendBufferState.None);
if (previousState != SendBufferState.SendPayloadSpecified)
{
return;
}
if (m_PinnedSendBufferHandle.IsAllocated)
{
m_PinnedSendBufferHandle.Free();
}
m_PinnedSendBuffer = WebSocketHelpers.EmptyPayload;
}
internal void BufferPayload(ArraySegment<byte> payload,
int unconsumedDataOffset,
WebSocketMessageType messageType,
bool endOfMessage)
{
ThrowIfDisposed();
int bytesBuffered = payload.Count - unconsumedDataOffset;
Contract.Assert(m_PayloadOffset == 0,
"'m_PayloadOffset' MUST be '0' at this point.");
Contract.Assert(m_BufferedPayloadReceiveResult == null || m_BufferedPayloadReceiveResult.Count == 0,
"'m_BufferedPayloadReceiveResult.Count' MUST be '0' at this point.");
Buffer.BlockCopy(payload.Array,
payload.Offset + unconsumedDataOffset,
m_PayloadBuffer.Array,
m_PayloadBuffer.Offset,
bytesBuffered);
m_BufferedPayloadReceiveResult =
new WebSocketReceiveResult(bytesBuffered, messageType, endOfMessage);
this.ValidateBufferedPayload();
}
internal bool ReceiveFromBufferedPayload(ArraySegment<byte> buffer, out WebSocketReceiveResult receiveResult)
{
ThrowIfDisposed();
ValidateBufferedPayload();
int bytesTransferred = Math.Min(buffer.Count, m_BufferedPayloadReceiveResult.Count);
receiveResult = m_BufferedPayloadReceiveResult.Copy(bytesTransferred);
Buffer.BlockCopy(m_PayloadBuffer.Array,
m_PayloadBuffer.Offset + m_PayloadOffset,
buffer.Array,
buffer.Offset,
bytesTransferred);
bool morePayloadBuffered;
if (m_BufferedPayloadReceiveResult.Count == 0)
{
m_PayloadOffset = 0;
m_BufferedPayloadReceiveResult = null;
morePayloadBuffered = false;
}
else
{
m_PayloadOffset += bytesTransferred;
morePayloadBuffered = true;
this.ValidateBufferedPayload();
}
return morePayloadBuffered;
}
internal ArraySegment<byte> ConvertNativeBuffer(WebSocketProtocolComponent.Action action,
WebSocketProtocolComponent.Buffer buffer,
WebSocketProtocolComponent.BufferType bufferType)
{
ThrowIfDisposed();
IntPtr bufferData;
uint bufferLength;
UnwrapWebSocketBuffer(buffer, bufferType, out bufferData, out bufferLength);
if (bufferData == IntPtr.Zero)
{
return WebSocketHelpers.EmptyPayload;
}
if (this.IsNativeBuffer(bufferData, bufferLength))
{
return new ArraySegment<byte>(m_InternalBuffer.Array,
this.GetOffset(bufferData),
(int)bufferLength);
}
Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
internal void ConvertCloseBuffer(WebSocketProtocolComponent.Action action,
WebSocketProtocolComponent.Buffer buffer,
out WebSocketCloseStatus closeStatus,
out string reason)
{
ThrowIfDisposed();
IntPtr bufferData;
uint bufferLength;
closeStatus = (WebSocketCloseStatus)buffer.CloseStatus.CloseStatus;
UnwrapWebSocketBuffer(buffer, WebSocketProtocolComponent.BufferType.Close, out bufferData, out bufferLength);
if (bufferData == IntPtr.Zero)
{
reason = null;
}
else
{
ArraySegment<byte> reasonBlob;
if (this.IsNativeBuffer(bufferData, bufferLength))
{
reasonBlob = new ArraySegment<byte>(m_InternalBuffer.Array,
this.GetOffset(bufferData),
(int)bufferLength);
}
else
{
Contract.Assert(false, "'buffer' MUST reference a memory segment within the pinned InternalBuffer.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
// No need to wrap DecoderFallbackException for invalid UTF8 chacters, because
// Encoding.UTF8 will not throw but replace invalid characters instead.
reason = Encoding.UTF8.GetString(reasonBlob.Array, reasonBlob.Offset, reasonBlob.Count);
}
}
internal void ValidateNativeBuffers(WebSocketProtocolComponent.Action action,
WebSocketProtocolComponent.BufferType bufferType,
WebSocketProtocolComponent.Buffer[] dataBuffers,
uint dataBufferCount)
{
Contract.Assert(dataBufferCount <= (uint)int.MaxValue,
"'dataBufferCount' MUST NOT be bigger than Int32.MaxValue.");
Contract.Assert(dataBuffers != null, "'dataBuffers' MUST NOT be NULL.");
ThrowIfDisposed();
if (dataBufferCount > dataBuffers.Length)
{
Contract.Assert(false, "'dataBufferCount' MUST NOT be bigger than 'dataBuffers.Length'.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
int count = dataBuffers.Length;
bool isSendActivity = action == WebSocketProtocolComponent.Action.IndicateSendComplete ||
action == WebSocketProtocolComponent.Action.SendToNetwork;
if (isSendActivity)
{
count = (int)dataBufferCount;
}
bool nonZeroBufferFound = false;
for (int i = 0; i < count; i++)
{
WebSocketProtocolComponent.Buffer dataBuffer = dataBuffers[i];
IntPtr bufferData;
uint bufferLength;
UnwrapWebSocketBuffer(dataBuffer, bufferType, out bufferData, out bufferLength);
if (bufferData == IntPtr.Zero)
{
continue;
}
nonZeroBufferFound = true;
bool isPinnedSendPayloadBuffer = IsPinnedSendPayloadBuffer(dataBuffer, bufferType);
if (bufferLength > GetMaxBufferSize())
{
if (!isSendActivity || !isPinnedSendPayloadBuffer)
{
Contract.Assert(false,
"'dataBuffer.BufferLength' MUST NOT be bigger than 'm_ReceiveBufferSize' and 'm_SendBufferSize'.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
}
if (!isPinnedSendPayloadBuffer && !IsNativeBuffer(bufferData, bufferLength))
{
Contract.Assert(false,
"WebSocketGetAction MUST return a pointer within the pinned internal buffer.");
// Indicates a violation in the contract with native Websocket.dll and could indicate
// memory corruption because the internal buffer is shared between managed and native code
throw new AccessViolationException();
}
}
if (!nonZeroBufferFound &&
action != WebSocketProtocolComponent.Action.NoAction &&
action != WebSocketProtocolComponent.Action.IndicateReceiveComplete &&
action != WebSocketProtocolComponent.Action.IndicateSendComplete)
{
Contract.Assert(false, "At least one 'dataBuffer.Buffer' MUST NOT be NULL.");
}
}
private static int GetNativeSendBufferSize(int sendBufferSize, bool isServerBuffer)
{
return isServerBuffer ? MinSendBufferSize : sendBufferSize;
}
internal static void UnwrapWebSocketBuffer(WebSocketProtocolComponent.Buffer buffer,
WebSocketProtocolComponent.BufferType bufferType,
out IntPtr bufferData,
out uint bufferLength)
{
bufferData = IntPtr.Zero;
bufferLength = 0;
switch (bufferType)
{
case WebSocketProtocolComponent.BufferType.Close:
bufferData = buffer.CloseStatus.ReasonData;
bufferLength = buffer.CloseStatus.ReasonLength;
break;
case WebSocketProtocolComponent.BufferType.None:
case WebSocketProtocolComponent.BufferType.BinaryFragment:
case WebSocketProtocolComponent.BufferType.BinaryMessage:
case WebSocketProtocolComponent.BufferType.UTF8Fragment:
case WebSocketProtocolComponent.BufferType.UTF8Message:
case WebSocketProtocolComponent.BufferType.PingPong:
case WebSocketProtocolComponent.BufferType.UnsolicitedPong:
bufferData = buffer.Data.BufferData;
bufferLength = buffer.Data.BufferLength;
break;
default:
Contract.Assert(false,
string.Format(CultureInfo.InvariantCulture,
"BufferType '{0}' is invalid/unknown.",
bufferType));
break;
}
}
private void ThrowIfDisposed()
{
switch (m_StateWhenDisposing)
{
case int.MinValue:
return;
case (int)WebSocketState.Closed:
case (int)WebSocketState.Aborted:
throw new WebSocketException(WebSocketError.InvalidState,
SR.GetString(SR.net_WebSockets_InvalidState_ClosedOrAborted, typeof(WebSocketBase), m_StateWhenDisposing));
default:
throw new ObjectDisposedException(GetType().FullName);
}
}
[Conditional("DEBUG"), Conditional("CONTRACTS_FULL")]
private void ValidateBufferedPayload()
{
Contract.Assert(m_BufferedPayloadReceiveResult != null,
"'m_BufferedPayloadReceiveResult' MUST NOT be NULL.");
Contract.Assert(m_BufferedPayloadReceiveResult.Count >= 0,
"'m_BufferedPayloadReceiveResult.Count' MUST NOT be negative.");
Contract.Assert(m_PayloadOffset >= 0, "'m_PayloadOffset' MUST NOT be smaller than 0.");
Contract.Assert(m_PayloadOffset <= m_PayloadBuffer.Count,
"'m_PayloadOffset' MUST NOT be bigger than 'm_PayloadBuffer.Count'.");
Contract.Assert(m_PayloadOffset + m_BufferedPayloadReceiveResult.Count <= m_PayloadBuffer.Count,
"'m_PayloadOffset + m_PayloadBytesBuffered' MUST NOT be bigger than 'm_PayloadBuffer.Count'.");
}
private int GetOffset(IntPtr pBuffer)
{
Contract.Assert(pBuffer != IntPtr.Zero, "'pBuffer' MUST NOT be IntPtr.Zero.");
int offset = (int)(pBuffer.ToInt64() - m_StartAddress + m_InternalBuffer.Offset);
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
return offset;
}
[Pure]
private int GetMaxBufferSize()
{
return Math.Max(m_ReceiveBufferSize, m_SendBufferSize);
}
// This method is actually checking whether the array "buffer" is located
// within m_NativeBuffer not m_InternalBuffer
internal bool IsInternalBuffer(byte[] buffer, int offset, int count)
{
Contract.Assert(buffer != null, "'buffer' MUST NOT be NULL.");
Contract.Assert(m_NativeBuffer.Array != null, "'m_NativeBuffer.Array' MUST NOT be NULL.");
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
Contract.Assert(count >= 0, "'count' MUST NOT be negative.");
Contract.Assert(offset + count <= buffer.Length, "'offset + count' MUST NOT exceed 'buffer.Length'.");
return object.ReferenceEquals(buffer, m_NativeBuffer.Array) &&
offset >= m_NativeBuffer.Offset &&
offset + count <= m_NativeBuffer.Offset + m_NativeBuffer.Count;
}
internal IntPtr ToIntPtr(int offset)
{
Contract.Assert(offset >= 0, "'offset' MUST NOT be negative.");
Contract.Assert(m_StartAddress + offset - m_InternalBuffer.Offset <= m_EndAddress, "'offset' is TOO BIG.");
return new IntPtr(m_StartAddress + offset - m_InternalBuffer.Offset);
}
private bool IsNativeBuffer(IntPtr pBuffer, uint bufferSize)
{
Contract.Assert(pBuffer != IntPtr.Zero, "'pBuffer' MUST NOT be NULL.");
Contract.Assert(bufferSize <= GetMaxBufferSize(),
"'bufferSize' MUST NOT be bigger than 'm_ReceiveBufferSize' and 'm_SendBufferSize'.");
long nativeBufferStartAddress = pBuffer.ToInt64();
long nativeBufferEndAddress = bufferSize + nativeBufferStartAddress;
Contract.Assert(Marshal.UnsafeAddrOfPinnedArrayElement(m_InternalBuffer.Array, m_InternalBuffer.Offset).ToInt64() == m_StartAddress,
"'m_InternalBuffer.Array' MUST be pinned for the whole lifetime of a WebSocket.");
if (nativeBufferStartAddress >= m_StartAddress &&
nativeBufferStartAddress <= m_EndAddress &&
nativeBufferEndAddress >= m_StartAddress &&
nativeBufferEndAddress <= m_EndAddress)
{
return true;
}
return false;
}
private void CleanUp()
{
if (m_GCHandle.IsAllocated)
{
m_GCHandle.Free();
}
ReleasePinnedSendBuffer();
}
internal static ArraySegment<byte> CreateInternalBufferArraySegment(int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
{
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize >= MinSendBufferSize,
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
int internalBufferSize = GetInternalBufferSize(receiveBufferSize, sendBufferSize, isServerBuffer);
return new ArraySegment<byte>(new byte[internalBufferSize]);
}
internal static void Validate(int count, int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
{
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize >= MinSendBufferSize,
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
int minBufferSize = GetInternalBufferSize(receiveBufferSize, sendBufferSize, isServerBuffer);
if (count < minBufferSize)
{
throw new ArgumentOutOfRangeException("internalBuffer",
SR.GetString(SR.net_WebSockets_ArgumentOutOfRange_InternalBuffer, minBufferSize));
}
}
private static int GetInternalBufferSize(int receiveBufferSize, int sendBufferSize, bool isServerBuffer)
{
Contract.Assert(receiveBufferSize >= MinReceiveBufferSize,
"'receiveBufferSize' MUST be at least " + MinReceiveBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize >= MinSendBufferSize,
"'sendBufferSize' MUST be at least " + MinSendBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(receiveBufferSize <= MaxBufferSize,
"'receiveBufferSize' MUST be less than or equal to " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
Contract.Assert(sendBufferSize <= MaxBufferSize,
"'sendBufferSize' MUST be at less than or equal to " + MaxBufferSize.ToString(NumberFormatInfo.InvariantInfo) + ".");
int nativeSendBufferSize = GetNativeSendBufferSize(sendBufferSize, isServerBuffer);
return 2 * receiveBufferSize + nativeSendBufferSize + NativeOverheadBufferSize + s_PropertyBufferSize;
}
private static class SendBufferState
{
public const int None = 0;
public const int SendPayloadSpecified = 1;
}
}
}
|