Skip to content

Instantly share code, notes, and snippets.

@juddgaddie
Created April 26, 2019 15:52
Show Gist options
  • Save juddgaddie/eeafbbb195a332bf72808748cdcd4336 to your computer and use it in GitHub Desktop.
Save juddgaddie/eeafbbb195a332bf72808748cdcd4336 to your computer and use it in GitHub Desktop.
Unsafe buffer with Bounds Checking
/*
* Copyright 2014 - 2017 Adaptive Financial Consulting Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using Adaptive.Agrona.Util;
namespace Adaptive.Agrona.Concurrent
{
/// <summary>
/// Supports regular, byte ordered, and atomic (memory ordered) access to an underlying buffer.
/// The buffer can be a byte[] or an unmanaged buffer.
///
/// <seealso cref="ByteOrder"/> of a wrapped buffer is not applied to the <seealso cref="UnsafeBuffer"/>; <seealso cref="UnsafeBuffer"/>s are
/// stateless and can be used concurrently. To control <seealso cref="ByteOrder"/> use the appropriate accessor method
/// with the <seealso cref="ByteOrder"/> overload.
///
/// Note: This class has a natural ordering that is inconsistent with equals.
/// Types my be different but equal on buffer contents.
///
/// Note: The wrap methods on this class are not thread safe. Concurrent access should only happen after a successful wrap.
/// </summary>
public unsafe class UnsafeBuffer : IAtomicBuffer, IDisposable
{
public static readonly string DISABLE_BOUNDS_CHECKS_PROP_NAME = "AGRONA_DISABLE_BOUNDS_CHECKS";
public static readonly bool SHOULD_BOUNDS_CHECK = !bool.Parse(Environment.GetEnvironmentVariable(DISABLE_BOUNDS_CHECKS_PROP_NAME) ?? "");
/// <summary>
/// Buffer alignment to ensure atomic word accesses.
/// </summary>
public const int ALIGNMENT = BitUtil.SIZE_OF_LONG;
private byte* _pBuffer;
private bool _disposed;
private GCHandle _pinnedGcHandle;
private bool _needToFreeGcHandle;
/// <summary>
/// Attach a view to a byte[] for providing direct access.
/// </summary>
/// <param name="buffer"> to which the view is attached. </param>
public UnsafeBuffer(byte[] buffer)
{
Wrap(buffer);
}
public UnsafeBuffer()
{
Capacity = -1; // this is only used in the test, by the mock infrastructure
}
/// <summary>
/// Attach a view to a byte[] for providing direct access.
/// </summary>
/// <param name="buffer"> to which the view is attached. </param>
/// <param name="offset"> within the buffer to begin. </param>
/// <param name="length"> of the buffer to be included. </param>
public UnsafeBuffer(byte[] buffer, int offset, int length)
{
Wrap(buffer, offset, length);
}
/// <summary>
/// Attach a view to a bytebuffer for providing direct access.
/// </summary>
/// <param name="buffer"> to which the view is attached. </param>
public UnsafeBuffer(ByteBuffer buffer)
{
Wrap(buffer);
}
/// <summary>
/// Attach a view to an existing <seealso cref="IDirectBuffer"/>
/// </summary>
/// <param name="buffer"> to which the view is attached. </param>
public UnsafeBuffer(IDirectBuffer buffer)
{
Wrap(buffer);
}
/// <summary>
/// Attach a view to an existing <seealso cref="IDirectBuffer"/>
/// </summary>
/// <param name="buffer"> to which the view is attached. </param>
/// <param name="offset"> within the buffer to begin. </param>
/// <param name="length"> of the buffer to be included. </param>
public UnsafeBuffer(IDirectBuffer buffer, int offset, int length)
{
Wrap(buffer, offset, length);
}
/// <summary>
/// Attach a view to an off-heap memory region by address.
/// </summary>
/// <param name="address"> where the memory begins off-heap </param>
/// <param name="length"> of the buffer from the given address </param>
public UnsafeBuffer(IntPtr address, int length)
{
Wrap(address, length);
}
/// <summary>
/// Attach a view to an off-heap memory region by address.
/// </summary>
/// <param name="address"> where the memory begins off-heap </param>
/// <param name="offset"></param>
/// <param name="length"> of the buffer from the given address </param>
public UnsafeBuffer(IntPtr address, int offset, int length)
{
Wrap(address, offset, length);
}
public UnsafeBuffer(MappedByteBuffer buffer)
{
Wrap(buffer.Pointer, 0, (int) buffer.Capacity);
}
public void Wrap(byte[] buffer)
{
if (buffer == null)
{
ThrowHelper.ThrowArgumentNullException(nameof(buffer));
}
FreeGcHandle();
// pin the buffer so it does not get moved around by GC, this is required since we use pointers
_pinnedGcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
_needToFreeGcHandle = true;
_pBuffer = (byte*) _pinnedGcHandle.AddrOfPinnedObject().ToPointer();
Capacity = buffer.Length;
ByteArray = buffer;
ByteBuffer = null;
}
public void Wrap(byte[] buffer, int offset, int length)
{
if (buffer == null)
{
ThrowHelper.ThrowArgumentException(nameof(buffer));
}
if (SHOULD_BOUNDS_CHECK)
{
int bufferLength = buffer.Length;
if (offset != 0 && (offset < 0 || offset > bufferLength - 1))
{
ThrowHelper.ThrowArgumentException("offset=" + offset + " not valid for capacity=" + bufferLength);
}
if (length < 0 || length > bufferLength - offset)
{
ThrowHelper.ThrowArgumentException(
"offset=" +
offset +
" length=" +
length +
" not valid for capacity=" +
bufferLength);
}
}
FreeGcHandle();
// pin the buffer so it does not get moved around by GC, this is required since we use pointers
_pinnedGcHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
_needToFreeGcHandle = true;
_pBuffer = (byte*) _pinnedGcHandle.AddrOfPinnedObject().ToPointer() + offset;
Capacity = length;
ByteArray = buffer;
ByteBuffer = null;
}
public void Wrap(IDirectBuffer buffer)
{
FreeGcHandle();
_needToFreeGcHandle = false;
_pBuffer = (byte*) buffer.BufferPointer.ToPointer();
Capacity = buffer.Capacity;
ByteArray = buffer.ByteArray;
ByteBuffer = buffer.ByteBuffer;
}
public void Wrap(ByteBuffer buffer)
{
FreeGcHandle();
_needToFreeGcHandle = false;
_pBuffer = (byte*) buffer.BufferPointer.ToPointer();
Capacity = buffer.Capacity;
ByteArray = null;
ByteBuffer = buffer;
}
public void Wrap(IDirectBuffer buffer, int offset, int length)
{
if (SHOULD_BOUNDS_CHECK)
{
int bufferCapacity = buffer.Capacity;
if (offset != 0 && (offset < 0 || offset > bufferCapacity - 1))
{
ThrowHelper.ThrowArgumentException("offset=" + offset + " not valid for capacity=" + bufferCapacity);
}
if (length < 0 || length > bufferCapacity - offset)
{
ThrowHelper.ThrowArgumentException(
"offset=" +
offset +
" length=" +
length +
" not valid for capacity=" +
bufferCapacity);
}
}
FreeGcHandle();
_needToFreeGcHandle = false;
_pBuffer = (byte*) buffer.BufferPointer.ToPointer() + offset;
Capacity = length;
ByteArray = buffer.ByteArray;
ByteBuffer = buffer.ByteBuffer;
}
public void Wrap(IntPtr pointer, int length)
{
FreeGcHandle();
_needToFreeGcHandle = false;
_pBuffer = (byte*) pointer.ToPointer();
Capacity = length;
ByteBuffer = null;
ByteArray = null;
}
public void Wrap(IntPtr pointer, int offset, int length)
{
FreeGcHandle();
_needToFreeGcHandle = false;
_pBuffer = (byte*) pointer.ToPointer() + offset;
Capacity = length;
ByteBuffer = null;
ByteArray = null;
}
public unsafe void Wrap(byte* pointer, int length)
{
FreeGcHandle();
_needToFreeGcHandle = false;
_pBuffer = pointer;
Capacity = length;
ByteBuffer = null;
ByteArray = null;
}
public unsafe void Wrap(byte* pointer, int offset, int length)
{
FreeGcHandle();
_needToFreeGcHandle = false;
_pBuffer = pointer + offset;
Capacity = length;
ByteBuffer = null;
ByteArray = null;
}
public IntPtr BufferPointer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get { return new IntPtr(_pBuffer); }
}
public byte[] ByteArray { get; private set; }
public ByteBuffer ByteBuffer { get; private set; }
public int Capacity { get; private set; }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void SetMemory(int index, int length, byte value)
{
BoundsCheck0(index, length);
Unsafe.InitBlock(_pBuffer + index, value, (uint) length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void CheckLimit(int limit)
{
if (limit > Capacity)
{
ThrowHelper.ThrowIndexOutOfRangeException($"limit={limit:D} is beyond capacity={Capacity:D}");
}
}
public bool IsExpandable => false;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void VerifyAlignment()
{
var address = new IntPtr(_pBuffer).ToInt64();
if (0 != (address & (ALIGNMENT - 1)))
{
ThrowHelper.ThrowInvalidOperationException(
$"AtomicBuffer is not correctly aligned: addressOffset={address:D} in not divisible by {ALIGNMENT:D}");
}
}
///////////////////////////////////////////////////////////////////////////
public long GetLong(int index, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
var value = *(long*) (_pBuffer + index);
return EndianessConverter.ApplyInt64(byteOrder, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public long GetLong(int index)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
return *(long*) (_pBuffer + index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutLong(int index, long value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
*(long*) (_pBuffer + index) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutLong(int index, long value, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
value = EndianessConverter.ApplyInt64(byteOrder, value);
*(long*) (_pBuffer + index) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public long GetLongVolatile(int index)
{
if (SHOULD_BOUNDS_CHECK)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
}
return Volatile.Read(ref *(long*) (_pBuffer + index));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutLongVolatile(int index, long value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
Interlocked.Exchange(ref *(long*) (_pBuffer + index), value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutLongOrdered(int index, long value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
Volatile.Write(ref *(long*) (_pBuffer + index), value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public long AddLongOrdered(int index, long increment)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
var value = GetLong(index);
PutLongOrdered(index, value + increment);
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool CompareAndSetLong(int index, long expectedValue, long updateValue)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
var original = Interlocked.CompareExchange(ref *(long*) (_pBuffer + index), updateValue, expectedValue);
return original == expectedValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public long GetAndAddLong(int index, long delta)
{
BoundsCheck0(index, BitUtil.SIZE_OF_LONG);
return Interlocked.Add(ref *(long*) (_pBuffer + index), delta) - delta;
}
public void PutInt(int index, int value, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
value = EndianessConverter.ApplyInt32(byteOrder, value);
*(int*) (_pBuffer + index) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetInt(int index)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
return *(int*) (_pBuffer + index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetInt(int index, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
var value = *(int*) (_pBuffer + index);
return EndianessConverter.ApplyInt32(byteOrder, value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutInt(int index, int value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
*(int*) (_pBuffer + index) = value;
}
public int PutIntAscii(int index, int value)
{
throw new NotImplementedException();
}
public int PutLongAscii(int index, long value)
{
throw new NotImplementedException();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetIntVolatile(int index)
{
if (SHOULD_BOUNDS_CHECK)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
}
return Volatile.Read(ref *(int*) (_pBuffer + index));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutIntVolatile(int index, int value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
Interlocked.Exchange(ref *(int*) (_pBuffer + index), value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutIntOrdered(int index, int value)
{
if (SHOULD_BOUNDS_CHECK)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
}
Volatile.Write(ref *(int*) (_pBuffer + index), value);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int AddIntOrdered(int index, int increment)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
var value = GetInt(index);
PutIntOrdered(index, value + increment);
return value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool CompareAndSetInt(int index, int expectedValue, int updateValue)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
var original = Interlocked.CompareExchange(ref *(int*) (_pBuffer + index), updateValue, expectedValue);
return original == expectedValue;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetAndAddInt(int index, int delta)
{
BoundsCheck0(index, BitUtil.SIZE_OF_INT);
return Interlocked.Add(ref *(int*) (_pBuffer + index), delta) - delta;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double GetDouble(int index, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_DOUBLE);
return EndianessConverter.ApplyDouble(byteOrder, *(double*) (_pBuffer + index));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutDouble(int index, double value, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_DOUBLE);
value = EndianessConverter.ApplyDouble(byteOrder, value);
*(double*) (_pBuffer + index) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public double GetDouble(int index)
{
BoundsCheck0(index, BitUtil.SIZE_OF_DOUBLE);
return *(double*) (_pBuffer + index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutDouble(int index, double value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_DOUBLE);
*(double*) (_pBuffer + index) = value;
}
///////////////////////////////////////////////////////////////////////////
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetFloat(int index, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_FLOAT);
return EndianessConverter.ApplyFloat(byteOrder, *(float*) (_pBuffer + index));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutFloat(int index, float value, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_FLOAT);
value = EndianessConverter.ApplyFloat(byteOrder, value);
*(float*) (_pBuffer + index) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetFloat(int index)
{
BoundsCheck0(index, BitUtil.SIZE_OF_FLOAT);
return *(float*) (_pBuffer + index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutFloat(int index, float value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_FLOAT);
*(float*) (_pBuffer + index) = value;
}
///////////////////////////////////////////////////////////////////////////
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public short GetShort(int index)
{
if (SHOULD_BOUNDS_CHECK)
{
BoundsCheck0(index, BitUtil.SIZE_OF_SHORT);
}
return *(short*) (_pBuffer + index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public short GetShort(int index, ByteOrder byteOrder)
{
if (SHOULD_BOUNDS_CHECK)
{
BoundsCheck0(index, BitUtil.SIZE_OF_SHORT);
}
return *(short*) (_pBuffer + index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutShort(int index, short value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_SHORT);
*(short*) (_pBuffer + index) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutShort(int index, short value, ByteOrder byteOrder)
{
BoundsCheck0(index, BitUtil.SIZE_OF_SHORT);
*(short*) (_pBuffer + index) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public short GetShortVolatile(int index)
{
BoundsCheck0(index, BitUtil.SIZE_OF_SHORT);
return Volatile.Read(ref *(short*) (_pBuffer + index));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutShortVolatile(int index, short value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_SHORT);
Volatile.Write(ref (*(short*) (_pBuffer + index)), value);
}
///////////////////////////////////////////////////////////////////////////
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetByte(int index)
{
BoundsCheck(index);
return *(_pBuffer + index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutByte(int index, byte value)
{
BoundsCheck(index);
*(_pBuffer + index) = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public byte GetByteVolatile(int index)
{
BoundsCheck(index);
return Volatile.Read(ref *(_pBuffer + index));
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutByteVolatile(int index, byte value)
{
BoundsCheck(index);
Volatile.Write(ref *(_pBuffer + index), value);
}
///////////////////////////////////////////////////////////////////////////
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void GetBytes(int index, byte[] dst)
{
GetBytes(index, dst, 0, dst.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void GetBytes(int index, byte[] dst, int offset, int length)
{
if (length == 0) return;
BoundsCheck0(index, length);
BufferUtil.BoundsCheck(dst, offset, length);
byte* source = _pBuffer + index;
fixed (byte* destination = &dst[offset])
{
ByteUtil.MemoryCopy(destination, source, (uint) length);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void GetBytes(int index, IMutableDirectBuffer dstBuffer, int dstIndex, int length)
{
dstBuffer.PutBytes(dstIndex, this, index, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutBytes(int index, byte[] src)
{
PutBytes(index, src, 0, src.Length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutBytes(int index, byte[] src, int offset, int length)
{
if (length == 0)
{
return;
}
BoundsCheck0(index, length);
BufferUtil.BoundsCheck(src, offset, length);
byte* destination = _pBuffer + index;
fixed (byte* source = &src[offset])
{
ByteUtil.MemoryCopy(destination, source, (uint) length);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutBytes(int index, IDirectBuffer srcBuffer, int srcIndex, int length)
{
if (length == 0)
{
return;
}
BoundsCheck0(index, length);
srcBuffer.BoundsCheck(srcIndex, length);
byte* destination = _pBuffer + index;
byte* source = (byte*) srcBuffer.BufferPointer.ToPointer() + srcIndex;
ByteUtil.MemoryCopy(destination, source, (uint) length);
}
///////////////////////////////////////////////////////////////////////////
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public char GetChar(int index)
{
BoundsCheck0(index, BitUtil.SIZE_OF_CHAR);
return *(char*) (_pBuffer + index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void PutChar(int index, char value)
{
BoundsCheck0(index, BitUtil.SIZE_OF_CHAR);
*(char*) (_pBuffer + index) = value;
}
//public char GetCharVolatile(int index)
//{
// BoundsCheck0(index, BitUtil.SIZE_OF_CHAR);
// return (char)Volatile.Read(ref *(short*)(_pBuffer + index));
//}
//public void PutCharVolatile(int index, char value)
//{
// BoundsCheck0(index, BitUtil.SIZE_OF_CHAR);
// Interlocked.Exchange(ref *(short*)(_pBuffer + index), (short)value);
//}
///////////////////////////////////////////////////////////////////////////
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string GetStringUtf8(int index)
{
var length = GetInt(index);
return GetStringUtf8(index, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string GetStringAscii(int index)
{
var length = GetInt(index);
return GetStringAscii(index, length);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string GetStringUtf8(int index, int length)
{
var stringInBytes = new byte[length];
GetBytes(index + BitUtil.SIZE_OF_INT, stringInBytes);
return Encoding.UTF8.GetString(stringInBytes);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string GetStringAscii(int index, int length)
{
var stringInBytes = new byte[length];
GetBytes(index + BitUtil.SIZE_OF_INT, stringInBytes);
return Encoding.ASCII.GetString(stringInBytes);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int PutStringUtf8(int index, string value)
{
return PutStringUtf8(index, value, int.MaxValue);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int PutStringAscii(int index, string value)
{
return PutStringAscii(index, value, int.MaxValue);
}
public int PutStringWithoutLengthAscii(int index, string value)
{
int length = value?.Length ?? 0;
if (SHOULD_BOUNDS_CHECK)
{
BoundsCheck0(index, length);
}
for (int i = 0; i < length; i++)
{
var c = value[i];
if (c > (char) 127)
{
c = '?';
}
*(char*) (_pBuffer + index + i) = c;
}
return length;
}
public int PutStringWithoutLengthAscii(int index, string value, int valueOffset, int length)
{
var len = value != null ? Math.Min(value.Length - valueOffset, length) : 0;
if (SHOULD_BOUNDS_CHECK)
{
BoundsCheck0(index, len);
}
for (int i = 0; i < len; i++)
{
char c = value[valueOffset + i];
if (c > (char) 127)
{
c = '?';
}
*(char*) (_pBuffer + index + i) = c;
}
return len;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int PutStringUtf8(int index, string value, int maxEncodedSize)
{
var bytes = value == null
? BufferUtil.NullBytes
: Encoding.UTF8.GetBytes(value);
if (bytes.Length > maxEncodedSize)
{
ThrowHelper.ThrowArgumentException("Encoded string larger than maximum size: " + maxEncodedSize);
}
PutInt(index, bytes.Length);
PutBytes(index + BitUtil.SIZE_OF_INT, bytes);
return BitUtil.SIZE_OF_INT + bytes.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int PutStringAscii(int index, string value, int maxEncodedSize)
{
var bytes = value == null
? BufferUtil.NullBytes
: Encoding.ASCII.GetBytes(value);
if (bytes.Length > maxEncodedSize)
{
ThrowHelper.ThrowArgumentException("Encoded string larger than maximum size: " + maxEncodedSize);
}
PutInt(index, bytes.Length);
PutBytes(index + BitUtil.SIZE_OF_INT, bytes);
return BitUtil.SIZE_OF_INT + bytes.Length;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public string GetStringWithoutLengthUtf8(int index, int length)
{
var stringInBytes = new byte[length];
GetBytes(index, stringInBytes);
return Encoding.UTF8.GetString(stringInBytes);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int PutStringWithoutLengthUtf8(int index, string value)
{
var bytes = value == null
? BufferUtil.NullBytes
: Encoding.UTF8.GetBytes(value);
PutBytes(index, bytes);
return bytes.Length;
}
///////////////////////////////////////////////////////////////////////////
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BoundsCheck(int index)
{
if (SHOULD_BOUNDS_CHECK)
{
if (index < 0 || index >= Capacity)
{
ThrowHelper.ThrowIndexOutOfRangeException($"index={index:D}, capacity={Capacity:D}");
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void BoundsCheck0(int index, int length)
{
if (SHOULD_BOUNDS_CHECK)
{
long resultingPosition = index + (long) length;
if (index < 0 || resultingPosition > Capacity)
{
ThrowHelper.ThrowIndexOutOfRangeException($"index={index:D}, length={length:D}, capacity={Capacity:D}");
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void BoundsCheck(int index, int length)
{
BoundsCheck0(index, length);
}
///////////////////////////////////////////////////////////////////////////
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(IDirectBuffer that)
{
var thisCapacity = this.Capacity;
var thatCapacity = that.Capacity;
var thisPointer = this._pBuffer;
var thatPointer = (byte*) that.BufferPointer.ToPointer();
for (int i = 0, length = Math.Min(thisCapacity, thatCapacity); i < length; i++)
{
var cmp = thisPointer[i] - thatPointer[i];
if (0 != cmp)
{
return cmp;
}
}
if (thisCapacity != thatCapacity)
{
return thisCapacity - thatCapacity;
}
return 0;
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Destructor for <see cref="UnsafeBuffer"/>
/// </summary>
~UnsafeBuffer()
{
Dispose(false);
}
private void Dispose(bool disposing)
{
if (_disposed)
return;
FreeGcHandle();
ByteArray = null;
ByteBuffer = null;
_disposed = true;
}
private void FreeGcHandle()
{
if (_needToFreeGcHandle)
{
_pinnedGcHandle.Free();
_needToFreeGcHandle = false;
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment