Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -217,26 +217,30 @@ public void ReadArray<T>(ulong byteOffset, T[] array, int index, int count)
if (array.Length - index < count)
throw new ArgumentException(SR.Argument_InvalidOffLen);

ReadSpan(byteOffset, new Span<T>(array, index, count));
}

[CLSCompliant(false)]
public void ReadSpan<T>(ulong byteOffset, Span<T> buffer)
where T : struct
{
if (_numBytes == Uninitialized)
throw NotInitialized();

uint sizeofT = SizeOf<T>();
uint alignedSizeofT = AlignedSizeOf<T>();
byte* ptr = (byte*)handle + byteOffset;
SpaceCheck(ptr, checked((nuint)(alignedSizeofT * count)));
SpaceCheck(ptr, checked((nuint)(alignedSizeofT * buffer.Length)));

bool mustCallRelease = false;
try
{
DangerousAddRef(ref mustCallRelease);

if (count > 0)
fixed (byte* pStructure = &Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(buffer)))
{
fixed (byte* pStructure = &Unsafe.As<T, byte>(ref array[index]))
{
for (int i = 0; i < count; i++)
Buffer.Memmove(pStructure + sizeofT * i, ptr + alignedSizeofT * i, sizeofT);
}
for (int i = 0; i < buffer.Length; i++)
Buffer.Memmove(pStructure + sizeofT * i, ptr + alignedSizeofT * i, sizeofT);
}
}
finally
Expand Down Expand Up @@ -293,28 +297,30 @@ public void WriteArray<T>(ulong byteOffset, T[] array, int index, int count)
if (array.Length - index < count)
throw new ArgumentException(SR.Argument_InvalidOffLen);

WriteSpan(byteOffset, new ReadOnlySpan<T>(array, index, count));
}

[CLSCompliant(false)]
public void WriteSpan<T>(ulong byteOffset, ReadOnlySpan<T> data)
where T : struct
{
if (_numBytes == Uninitialized)
throw NotInitialized();

uint sizeofT = SizeOf<T>();
uint alignedSizeofT = AlignedSizeOf<T>();
byte* ptr = (byte*)handle + byteOffset;
SpaceCheck(ptr, checked((nuint)(alignedSizeofT * count)));
SpaceCheck(ptr, checked((nuint)(alignedSizeofT * data.Length)));

bool mustCallRelease = false;
try
{
DangerousAddRef(ref mustCallRelease);

if (count > 0)
fixed (byte* pStructure = &Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(data)))
{
{
fixed (byte* pStructure = &Unsafe.As<T, byte>(ref array[index]))
{
for (int i = 0; i < count; i++)
Buffer.Memmove(ptr + alignedSizeofT * i, pStructure + sizeofT * i, sizeofT);
}
}
for (int i = 0; i < data.Length; i++)
Buffer.Memmove(ptr + alignedSizeofT * i, pStructure + sizeofT * i, sizeofT);
}
}
finally
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ public void ReadArray_NullArray_ThrowsArgumentNullException()
AssertExtensions.Throws<ArgumentNullException>("array", () => wrapper.WriteArray<int>(0, null, 0, 0));
}

[Fact]
public void ReadWriteSpan_EmptySpan_Passes()
{
var buffer = new SubBuffer(true);
buffer.Initialize(0);

buffer.ReadSpan<int>(0, Span<int>.Empty);
buffer.WriteSpan<int>(0, ReadOnlySpan<int>.Empty);
}

[Fact]
public void ReadArray_NegativeIndex_ThrowsArgumentOutOfRangeException()
{
Expand Down Expand Up @@ -121,6 +131,68 @@ public void ByteLength_GetNotInitialized_ThrowsInvalidOperationException()
Assert.Throws<InvalidOperationException>(() => wrapper.ByteLength);
}

[Fact]
public void ReadWrite_RoundTrip()
{
using var buffer = new HGlobalBuffer(100);

int intValue = 1234;
buffer.Write<int>(0, intValue);
Assert.Equal(intValue, buffer.Read<int>(0));

double doubleValue = 123.45;
buffer.Write<double>(10, doubleValue);
Assert.Equal(doubleValue, buffer.Read<double>(10));

TestStruct structValue = new TestStruct
{
I = 1234,
L = 987654321,
D = double.MaxValue
};
buffer.Write<TestStruct>(0, structValue);
Assert.Equal(structValue, buffer.Read<TestStruct>(0));
}

[Fact]
public void ReadWriteSpanArray_RoundTrip()
{
using var buffer = new HGlobalBuffer(200);

int[] intArray = new int[] { 11, 22, 33, 44 };
TestArray(intArray);
TestSpan<int>(intArray);

TestStruct[] structArray = new TestStruct[]
{
new TestStruct { I = 11, L = 22, D = 33 },
new TestStruct { I = 44, L = 55, D = 66 },
new TestStruct { I = 77, L = 88, D = 99 },
new TestStruct { I = 100, L = 200, D = 300 },
};
TestArray(structArray);
TestSpan<TestStruct>(structArray);

void TestArray<T>(T[] data)
where T : struct
{
T[] destination = new T[data.Length];
buffer.WriteArray(0, data, 0, data.Length);
buffer.ReadArray(0, destination, 0, data.Length);
Assert.Equal(data, destination);
}

void TestSpan<T>(ReadOnlySpan<T> data)
where T : unmanaged
{
Span<T> destination = stackalloc T[data.Length];
buffer.WriteSpan(0, data);
buffer.ReadSpan(0, destination);
for (int i = 0; i < data.Length; i++)
Assert.Equal(data[i], destination[i]);
}
}

public class SubBuffer : SafeBuffer
{
public SubBuffer(bool ownsHandle) : base(ownsHandle) { }
Expand All @@ -130,5 +202,27 @@ protected override bool ReleaseHandle()
throw new NotImplementedException();
}
}

public class HGlobalBuffer : SafeBuffer
{
public HGlobalBuffer(int length) : base(true)
{
SetHandle(Marshal.AllocHGlobal(length));
Initialize((ulong)length);
}

protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(handle);
return true;
}
}

public struct TestStruct
{
public int I;
public long L;
public double D;
}
}
}
4 changes: 4 additions & 0 deletions src/libraries/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9736,11 +9736,15 @@ public void Initialize<T>(uint numElements) where T : struct { }
[System.CLSCompliantAttribute(false)]
public void ReadArray<T>(ulong byteOffset, T[] array, int index, int count) where T : struct { }
[System.CLSCompliantAttribute(false)]
public void ReadSpan<T>(ulong byteOffset, System.Span<T> buffer) where T : struct { }
[System.CLSCompliantAttribute(false)]
public T Read<T>(ulong byteOffset) where T : struct { throw null; }
public void ReleasePointer() { }
[System.CLSCompliantAttribute(false)]
public void WriteArray<T>(ulong byteOffset, T[] array, int index, int count) where T : struct { }
[System.CLSCompliantAttribute(false)]
public void WriteSpan<T>(ulong byteOffset, System.ReadOnlySpan<T> data) where T : struct { }
[System.CLSCompliantAttribute(false)]
public void Write<T>(ulong byteOffset, T value) where T : struct { }
}
public abstract partial class SafeHandle : System.Runtime.ConstrainedExecution.CriticalFinalizerObject, System.IDisposable
Expand Down