// ==++==
//
// 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;
//|
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.
//|
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.
//
//|
[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.
//
//|
[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).
//
//|
[MethodImpl(MethodImplOptions.InternalCall)]
[NoHeapAllocation]
public static extern int ByteLength(Array array);
}
}