389 lines
15 KiB
C#
389 lines
15 KiB
C#
// ==++==
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// ==--==
|
|
//
|
|
// Note: These routines assume that the processor supports
|
|
// 32-bit aligned accesses. Migration to non-x86 platforms will
|
|
// need to examine and tune the implementations accordingly.
|
|
//
|
|
|
|
namespace System
|
|
{
|
|
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
//| <include path='docs/doc[@for="Buffer"]/*' />
|
|
|
|
public sealed partial class Buffer
|
|
{
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private static unsafe void WriteHelperByte(byte* dst, byte val,
|
|
bool vol) {
|
|
if (vol) {
|
|
System.Threading.Thread.VolatileWriteUnsafe(dst, val);
|
|
}
|
|
else {
|
|
*dst = val;
|
|
}
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private static unsafe byte ReadHelperByte(byte* src, bool vol) {
|
|
if (vol) {
|
|
return System.Threading.Thread.VolatileReadUnsafe(src);
|
|
}
|
|
else {
|
|
return *src;
|
|
}
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private static unsafe void WriteHelperShort(short* dst, short val,
|
|
bool vol) {
|
|
if (vol) {
|
|
System.Threading.Thread.VolatileWriteUnsafe(dst, val);
|
|
}
|
|
else {
|
|
*dst = val;
|
|
}
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private static unsafe short ReadHelperShort(short* src, bool vol) {
|
|
if (vol) {
|
|
return System.Threading.Thread.VolatileReadUnsafe(src);
|
|
}
|
|
else {
|
|
return *src;
|
|
}
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private static unsafe void WriteHelperInt(int* dst, int val, bool vol) {
|
|
if (vol) {
|
|
System.Threading.Thread.VolatileWriteUnsafe(dst, val);
|
|
}
|
|
else {
|
|
*dst = val;
|
|
}
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private static unsafe int ReadHelperInt(int* src, bool vol) {
|
|
if (vol) {
|
|
return System.Threading.Thread.VolatileReadUnsafe(src);
|
|
}
|
|
else {
|
|
return *src;
|
|
}
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
public static unsafe void MoveMemory(byte* dmem, byte* smem,
|
|
UIntPtr size)
|
|
{
|
|
MoveMemory(dmem, smem, (int)size);
|
|
}
|
|
|
|
// This is a replacement for the memmove intrinsic.
|
|
// It performs better than the CRT one and the inline version
|
|
// originally from Lightning\Src\VM\COMSystem.cpp
|
|
[NoHeapAllocation]
|
|
[Inline]
|
|
private static unsafe void MoveMemoryImpl(byte* dmem, byte* smem,
|
|
int size, bool vol) {
|
|
if (dmem <= smem) {
|
|
// take the slow path if the source and dest and co-aligned.
|
|
if (((int)smem & 0x3) != ((int)dmem & 0x3)) {
|
|
for (; size > 0; size--) {
|
|
WriteHelperByte(dmem++, ReadHelperByte(smem++, vol), vol);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// make sure the destination is dword aligned
|
|
while ((((int)dmem) & 0x3) != 0 && size >= 3) {
|
|
WriteHelperByte(dmem++, ReadHelperByte(smem++, vol), vol);
|
|
size -= 1;
|
|
}
|
|
// copy 16 bytes at a time
|
|
if (size >= 16) {
|
|
size -= 16;
|
|
do {
|
|
WriteHelperInt(&((int*)dmem)[0],
|
|
ReadHelperInt(&((int*)smem)[0], vol),
|
|
vol);
|
|
WriteHelperInt(&((int*)dmem)[1],
|
|
ReadHelperInt(&((int*)smem)[1], vol),
|
|
vol);
|
|
WriteHelperInt(&((int*)dmem)[2],
|
|
ReadHelperInt(&((int*)smem)[2], vol),
|
|
vol);
|
|
WriteHelperInt(&((int*)dmem)[3],
|
|
ReadHelperInt(&((int*)smem)[3], vol),
|
|
vol);
|
|
dmem += 16;
|
|
smem += 16;
|
|
}
|
|
while ((size -= 16) >= 0);
|
|
}
|
|
|
|
// still 8 bytes or more left to copy?
|
|
if ((size & 8) != 0) {
|
|
WriteHelperInt(&((int *)dmem)[0],
|
|
ReadHelperInt(&((int *)smem)[0], vol), vol);
|
|
WriteHelperInt(&((int *)dmem)[1],
|
|
ReadHelperInt(&((int *)smem)[1], vol), vol);
|
|
dmem += 8;
|
|
smem += 8;
|
|
}
|
|
|
|
// still 4 bytes or more left to copy?
|
|
if ((size & 4) != 0) {
|
|
WriteHelperInt(&((int *)dmem)[0],
|
|
ReadHelperInt(&((int *)smem)[0], vol), vol);
|
|
dmem += 4;
|
|
smem += 4;
|
|
}
|
|
|
|
// still 2 bytes or more left to copy?
|
|
if ((size & 2) != 0) {
|
|
WriteHelperShort(&((short *)dmem)[0],
|
|
ReadHelperShort(&((short *)smem)[0], vol),
|
|
vol);
|
|
dmem += 2;
|
|
smem += 2;
|
|
}
|
|
|
|
// still 1 byte left to copy?
|
|
if ((size & 1) != 0) {
|
|
WriteHelperByte(dmem, ReadHelperByte(smem, vol), vol);
|
|
dmem += 1;
|
|
smem += 1;
|
|
}
|
|
}
|
|
else {
|
|
smem += size;
|
|
dmem += size;
|
|
|
|
// take the slow path if the source and dest and co-aligned.
|
|
if (((int)smem & 0x3) != ((int)dmem & 0x3)) {
|
|
for (; size > 0; size--) {
|
|
WriteHelperByte(--dmem, ReadHelperByte(--smem, vol), vol);
|
|
}
|
|
return;
|
|
}
|
|
|
|
// make sure the destination is dword aligned
|
|
while ((((int)dmem) & 0x3) != 0 && size >= 3) {
|
|
WriteHelperByte(--dmem, ReadHelperByte(--smem, vol), vol);
|
|
size -= 1;
|
|
}
|
|
|
|
// copy 16 bytes at a time
|
|
if (size >= 16) {
|
|
size -= 16;
|
|
do {
|
|
dmem -= 16;
|
|
smem -= 16;
|
|
WriteHelperInt(&((int *)dmem)[3],
|
|
ReadHelperInt(&((int *)smem)[3], vol),
|
|
vol);
|
|
WriteHelperInt(&((int *)dmem)[2],
|
|
ReadHelperInt(&((int *)smem)[2], vol),
|
|
vol);
|
|
WriteHelperInt(&((int *)dmem)[1],
|
|
ReadHelperInt(&((int *)smem)[1], vol),
|
|
vol);
|
|
WriteHelperInt(&((int *)dmem)[0],
|
|
ReadHelperInt(&((int *)smem)[0], vol),
|
|
vol);
|
|
}
|
|
while ((size -= 16) >= 0);
|
|
}
|
|
|
|
// still 8 bytes or more left to copy?
|
|
if ((size & 8) != 0) {
|
|
dmem -= 8;
|
|
smem -= 8;
|
|
WriteHelperInt(&((int *)dmem)[1],
|
|
ReadHelperInt(&((int *)smem)[1], vol), vol);
|
|
WriteHelperInt(&((int *)dmem)[0],
|
|
ReadHelperInt(&((int *)smem)[0], vol), vol);
|
|
}
|
|
|
|
// still 4 bytes or more left to copy?
|
|
if ((size & 4) != 0) {
|
|
dmem -= 4;
|
|
smem -= 4;
|
|
WriteHelperInt(&((int *)dmem)[0],
|
|
ReadHelperInt(&((int *)smem)[0], vol), vol);
|
|
}
|
|
|
|
// still 2 bytes or more left to copy?
|
|
if ((size & 2) != 0) {
|
|
dmem -= 2;
|
|
smem -= 2;
|
|
WriteHelperShort(&((short *)dmem)[0],
|
|
ReadHelperShort(&((short *)smem)[0], vol),
|
|
vol);
|
|
}
|
|
|
|
// still 1 byte left to copy?
|
|
if ((size & 1) != 0) {
|
|
dmem -= 1;
|
|
smem -= 1;
|
|
WriteHelperByte(dmem,
|
|
ReadHelperByte(smem, vol), vol);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Copies from one primitive array to another primitive array without
|
|
// respecting types. This calls memmove internally.
|
|
//| <include path='docs/doc[@for="Buffer.BlockCopy"]/*' />
|
|
public static void BlockCopy(Array src, int srcOffset,
|
|
Array dst, int dstOffset, int count) {
|
|
if (src == null) {
|
|
throw new ArgumentNullException("src");
|
|
}
|
|
if (dst == null) {
|
|
throw new ArgumentNullException("dst");
|
|
}
|
|
InternalBlockCopy(src, srcOffset, dst, dstOffset, count);
|
|
}
|
|
|
|
// A very simple and efficient array copy that assumes all of the
|
|
// parameter validation has already been done. All counts here are
|
|
// in bytes.
|
|
internal static unsafe void InternalBlockCopy(Array src, int srcOffset,
|
|
Array dst, int dstOffset,
|
|
int count) {
|
|
VTable.Assert(src != null);
|
|
VTable.Assert(dst != null);
|
|
|
|
// Unfortunately, we must do a check to make sure we're writing
|
|
// within the bounds of the array. This will ensure that we don't
|
|
// overwrite memory elsewhere in the system nor do we write out junk.
|
|
// This can happen if multiple threads screw with our IO classes
|
|
// simultaneously without being threadsafe. Throw here.
|
|
int srcLen = src.Length * src.vtable.arrayElementSize;
|
|
if (srcOffset < 0 || dstOffset < 0 || count < 0 ||
|
|
srcOffset > srcLen - count)
|
|
throw new IndexOutOfRangeException
|
|
("IndexOutOfRange_IORaceCondition");
|
|
if (src == dst) {
|
|
if (dstOffset > srcLen - count)
|
|
throw new IndexOutOfRangeException
|
|
("IndexOutOfRange_IORaceCondition");
|
|
}
|
|
else {
|
|
int dstLen = dst.Length * dst.vtable.arrayElementSize;
|
|
if (dstOffset > dstLen - count)
|
|
throw new IndexOutOfRangeException
|
|
("IndexOutOfRange_IORaceCondition");
|
|
}
|
|
|
|
// Copy the data.
|
|
// Call our faster version of memmove, not the CRT one.
|
|
fixed (int *srcFieldPtr = &src.field1) {
|
|
fixed (int *dstFieldPtr = &dst.field1) {
|
|
byte *srcPtr = (byte *)
|
|
src.GetFirstElementAddress(srcFieldPtr);
|
|
byte *dstPtr = (byte *)
|
|
dst.GetFirstElementAddress(dstFieldPtr);
|
|
MoveMemory(dstPtr + dstOffset, srcPtr + srcOffset, count);
|
|
}
|
|
}
|
|
}
|
|
|
|
[NoHeapAllocation]
|
|
internal static unsafe void ZeroMemory(byte* dst, UIntPtr len)
|
|
{
|
|
ZeroMemory(dst, (int)len);
|
|
}
|
|
|
|
[Inline]
|
|
[NoHeapAllocation]
|
|
internal unsafe static void InitMemoryImpl(byte* dest, byte value,
|
|
int len, bool vol) {
|
|
int intValue = (value == 0) ? 0 :
|
|
(value | (value << 8) | (value << 16) | (value << 24));
|
|
// This is based on Peter Sollich's faster memcpy implementation,
|
|
// from COMString.cpp.
|
|
while ((((int)dest) & 0x03) != 0 && len >= 3) {
|
|
WriteHelperByte(dest++, value, vol);
|
|
len -= 1;
|
|
}
|
|
|
|
if (len >= 16) {
|
|
len -= 16;
|
|
do {
|
|
WriteHelperInt(&((int*)dest)[0], intValue, vol);
|
|
WriteHelperInt(&((int*)dest)[1], intValue, vol);
|
|
WriteHelperInt(&((int*)dest)[2], intValue, vol);
|
|
WriteHelperInt(&((int*)dest)[3], intValue, vol);
|
|
dest += 16;
|
|
} while ((len -= 16) >= 0);
|
|
}
|
|
if ((len & 8) > 0) {
|
|
WriteHelperInt(&((int*)dest)[0], intValue, vol);
|
|
WriteHelperInt(&((int*)dest)[1], intValue, vol);
|
|
dest += 8;
|
|
}
|
|
if ((len & 4) > 0) {
|
|
WriteHelperInt(&((int*)dest)[0], intValue, vol);
|
|
dest += 4;
|
|
}
|
|
if ((len & 2) != 0) {
|
|
short shortValue = (value == 0) ? (short) 0 :
|
|
(short)(value | value << 8) ;
|
|
dest += 2;
|
|
}
|
|
if ((len & 1) != 0)
|
|
WriteHelperByte(dest++, value, vol);
|
|
}
|
|
|
|
// Gets a particular byte out of the array. The array must be an
|
|
// array of primitives.
|
|
//
|
|
// This essentially does the following:
|
|
// return ((byte*)array) + index.
|
|
//
|
|
//| <include path='docs/doc[@for="Buffer.GetByte"]/*' />
|
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
|
[NoHeapAllocation]
|
|
public static extern byte GetByte(Array array, int index);
|
|
|
|
// Sets a particular byte in an the array. The array must be an
|
|
// array of primitives.
|
|
//
|
|
// This essentially does the following:
|
|
// *(((byte*)array) + index) = value.
|
|
//
|
|
//| <include path='docs/doc[@for="Buffer.SetByte"]/*' />
|
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
|
[NoHeapAllocation]
|
|
public static extern void SetByte(Array array, int index, byte value);
|
|
|
|
// Gets a particular byte out of the array. The array must be an
|
|
// array of primitives.
|
|
//
|
|
// This essentially does the following:
|
|
// return array.length * sizeof(array.UnderlyingElementType).
|
|
//
|
|
//| <include path='docs/doc[@for="Buffer.ByteLength"]/*' />
|
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
|
[NoHeapAllocation]
|
|
public static extern int ByteLength(Array array);
|
|
}
|
|
}
|