mirror of https://github.com/SixLabors/ImageSharp
35 changed files with 322 additions and 1150 deletions
@ -0,0 +1,54 @@ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
namespace SixLabors.ImageSharp.Memory |
|||
{ |
|||
internal static class BufferExtensions |
|||
{ |
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static int Length<T>(this IBuffer<T> buffer) |
|||
where T : struct => buffer.Span.Length; |
|||
|
|||
/// <summary>
|
|||
/// Gets a <see cref="Span{T}"/> to an offseted position inside the buffer.
|
|||
/// </summary>
|
|||
/// <param name="buffer">The buffer</param>
|
|||
/// <param name="start">The start</param>
|
|||
/// <returns>The <see cref="Span{T}"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Span<T> Slice<T>(this IBuffer<T> buffer, int start) |
|||
where T : struct |
|||
{ |
|||
return buffer.Span.Slice(start); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets a <see cref="Span{T}"/> to an offsetted position inside the buffer.
|
|||
/// </summary>
|
|||
/// <param name="buffer">The buffer</param>
|
|||
/// <param name="start">The start</param>
|
|||
/// <param name="length">The length of the slice</param>
|
|||
/// <returns>The <see cref="Span{T}"/></returns>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static Span<T> Slice<T>(this IBuffer<T> buffer, int start, int length) |
|||
where T : struct |
|||
{ |
|||
return buffer.Span.Slice(start, length); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Clears the contents of this buffer.
|
|||
/// </summary>
|
|||
/// <param name="buffer">The buffer</param>
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public static void Clear<T>(this IBuffer<T> buffer) |
|||
where T : struct |
|||
{ |
|||
buffer.Span.Clear(); |
|||
} |
|||
|
|||
public static ref T DangerousGetPinnableReference<T>(this IBuffer<T> buffer) |
|||
where T : struct => |
|||
ref buffer.Span.DangerousGetPinnableReference(); |
|||
} |
|||
} |
|||
@ -1,42 +0,0 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Benchmarks.General |
|||
{ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
public unsafe class ClearBuffer |
|||
{ |
|||
private Buffer<Rgba32> buffer; |
|||
|
|||
[Params(32, 128, 512)] |
|||
public int Count { get; set; } |
|||
|
|||
[GlobalSetup] |
|||
public void Setup() |
|||
{ |
|||
this.buffer = Configuration.Default.MemoryManager.Allocate<Rgba32>(this.Count); |
|||
} |
|||
|
|||
[GlobalCleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.buffer.Dispose(); |
|||
} |
|||
|
|||
[Benchmark(Baseline = true)] |
|||
public void Array_Clear() |
|||
{ |
|||
Array.Clear(this.buffer.Array, 0, this.Count); |
|||
} |
|||
|
|||
[Benchmark] |
|||
public void Unsafe_InitBlock() |
|||
{ |
|||
Unsafe.InitBlock((void*)this.buffer.Pin(), default(byte), (uint)this.Count * sizeof(uint)); |
|||
} |
|||
} |
|||
} |
|||
@ -1,362 +0,0 @@ |
|||
namespace SixLabors.ImageSharp.Benchmarks.General |
|||
{ |
|||
using System.Numerics; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using BenchmarkDotNet.Attributes; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
// Pixel indexing benchmarks compare different methods for getting/setting all pixel values in a subsegment of a single pixel row.
|
|||
public abstract unsafe class PixelIndexing |
|||
{ |
|||
/// <summary>
|
|||
/// https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Pinnable.cs
|
|||
/// </summary>
|
|||
protected class Pinnable<T> |
|||
{ |
|||
public T Data; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The indexer methods are encapsulated into a struct to make sure everything is inlined.
|
|||
/// </summary>
|
|||
internal struct Data |
|||
{ |
|||
private Vector4* pointer; |
|||
|
|||
private Pinnable<Vector4> pinnable; |
|||
|
|||
private Vector4[] array; |
|||
|
|||
private int width; |
|||
|
|||
public Data(Buffer2D<Vector4> buffer) |
|||
{ |
|||
this.pointer = (Vector4*)buffer.Buffer.Pin(); |
|||
this.pinnable = Unsafe.As<Pinnable<Vector4>>(buffer.Buffer.Array); |
|||
this.array = buffer.Buffer.Array; |
|||
this.width = buffer.Width; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public Vector4 GetPointersBasicImpl(int x, int y) |
|||
{ |
|||
return this.pointer[y * this.width + x]; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public Vector4 GetPointersSrcsUnsafeImpl(int x, int y) |
|||
{ |
|||
// This is the original solution in PixelAccessor:
|
|||
return Unsafe.Read<Vector4>((byte*)this.pointer + (((y * this.width) + x) * Unsafe.SizeOf<Vector4>())); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public Vector4 GetReferencesImpl(int x, int y) |
|||
{ |
|||
int elementOffset = (y * this.width) + x; |
|||
return Unsafe.Add(ref this.pinnable.Data, elementOffset); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public ref Vector4 GetReferencesRefReturnsImpl(int x, int y) |
|||
{ |
|||
int elementOffset = (y * this.width) + x; |
|||
return ref Unsafe.Add(ref this.pinnable.Data, elementOffset); |
|||
} |
|||
|
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void IndexWithPointersBasicImpl(int x, int y, Vector4 v) |
|||
{ |
|||
this.pointer[y * this.width + x] = v; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void IndexWithPointersSrcsUnsafeImpl(int x, int y, Vector4 v) |
|||
{ |
|||
Unsafe.Write((byte*)this.pointer + (((y * this.width) + x) * Unsafe.SizeOf<Vector4>()), v); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void IndexWithReferencesOnPinnableIncorrectImpl(int x, int y, Vector4 v) |
|||
{ |
|||
int elementOffset = (y * this.width) + x; |
|||
// Incorrect, because also should add a runtime-specific byte offset here. See https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs#L68
|
|||
Unsafe.Add(ref this.pinnable.Data, elementOffset) = v; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public ref Vector4 IndexWithReferencesOnPinnableIncorrectRefReturnImpl(int x, int y) |
|||
{ |
|||
int elementOffset = (y * this.width) + x; |
|||
// Incorrect, because also should add a runtime-specific byte offset here. See https://github.com/dotnet/corefx/blob/master/src/System.Memory/src/System/Span.cs#L68
|
|||
return ref Unsafe.Add(ref this.pinnable.Data, elementOffset); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void IndexWithUnsafeReferenceArithmeticsOnArray0Impl(int x, int y, Vector4 v) |
|||
{ |
|||
int elementOffset = (y * this.width) + x; |
|||
Unsafe.Add(ref this.array[0], elementOffset) = v; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public ref Vector4 IndexWithUnsafeReferenceArithmeticsOnArray0RefReturnImpl(int x, int y) |
|||
{ |
|||
int elementOffset = (y * this.width) + x; |
|||
return ref Unsafe.Add(ref this.array[0], elementOffset); |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public void IndexSetArrayStraightforward(int x, int y, Vector4 v) |
|||
{ |
|||
// No magic.
|
|||
// We just index right into the array as normal people do.
|
|||
// And it looks like this is the fastest way!
|
|||
this.array[(y * this.width) + x] = v; |
|||
} |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public ref Vector4 IndexWithReferencesOnArrayStraightforwardRefReturnImpl(int x, int y) |
|||
{ |
|||
// No magic.
|
|||
// We just index right into the array as normal people do.
|
|||
// And it looks like this is the fastest way!
|
|||
return ref this.array[(y * this.width) + x]; |
|||
} |
|||
} |
|||
|
|||
internal Buffer2D<Vector4> buffer; |
|||
|
|||
protected int width; |
|||
|
|||
protected int startIndex; |
|||
|
|||
protected int endIndex; |
|||
|
|||
protected Vector4* pointer; |
|||
|
|||
protected Vector4[] array; |
|||
|
|||
protected Pinnable<Vector4> pinnable; |
|||
|
|||
// [Params(1024)]
|
|||
public int Count { get; set; } = 1024; |
|||
|
|||
[GlobalSetup] |
|||
public void Setup() |
|||
{ |
|||
this.width = 2048; |
|||
this.buffer = Configuration.Default.MemoryManager.Allocate2D<Vector4>(2048, 2048); |
|||
this.pointer = (Vector4*)this.buffer.Buffer.Pin(); |
|||
this.array = this.buffer.Buffer.Array; |
|||
this.pinnable = Unsafe.As<Pinnable<Vector4>>(this.array); |
|||
|
|||
this.startIndex = 2048 / 2 - (this.Count / 2); |
|||
this.endIndex = 2048 / 2 + (this.Count / 2); |
|||
} |
|||
|
|||
[GlobalCleanup] |
|||
public void Cleanup() |
|||
{ |
|||
this.buffer.Dispose(); |
|||
} |
|||
|
|||
} |
|||
|
|||
public class PixelIndexingGetter : PixelIndexing |
|||
{ |
|||
[Benchmark(Description = "Index.Get: Pointers+arithmetics", Baseline = true)] |
|||
public Vector4 IndexWithPointersBasic() |
|||
{ |
|||
Vector4 sum = Vector4.Zero; |
|||
Data data = new Data(this.buffer); |
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
sum += data.GetPointersBasicImpl(x, y); |
|||
} |
|||
|
|||
return sum; |
|||
} |
|||
|
|||
[Benchmark(Description = "Index.Get: Pointers+SRCS.Unsafe")] |
|||
public Vector4 IndexWithPointersSrcsUnsafe() |
|||
{ |
|||
Vector4 sum = Vector4.Zero; |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
sum += data.GetPointersSrcsUnsafeImpl(x, y); |
|||
} |
|||
|
|||
return sum; |
|||
} |
|||
|
|||
[Benchmark(Description = "Index.Get: References")] |
|||
public Vector4 IndexWithReferences() |
|||
{ |
|||
Vector4 sum = Vector4.Zero; |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
sum += data.GetReferencesImpl(x, y); |
|||
} |
|||
|
|||
return sum; |
|||
} |
|||
|
|||
[Benchmark(Description = "Index.Get: References|refreturns")] |
|||
public Vector4 IndexWithReferencesRefReturns() |
|||
{ |
|||
Vector4 sum = Vector4.Zero; |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
sum += data.GetReferencesRefReturnsImpl(x, y); |
|||
} |
|||
|
|||
return sum; |
|||
} |
|||
} |
|||
|
|||
public class PixelIndexingSetter : PixelIndexing |
|||
{ |
|||
[Benchmark(Description = "!!! Index.Set: Pointers|arithmetics", Baseline = true)] |
|||
public void IndexWithPointersBasic() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
data.IndexWithPointersBasicImpl(x, y, v); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Index.Set: Pointers|SRCS.Unsafe")] |
|||
public void IndexWithPointersSrcsUnsafe() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
data.IndexWithPointersSrcsUnsafeImpl(x, y, v); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Index.Set: References|IncorrectPinnable")] |
|||
public void IndexWithReferencesPinnableBasic() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
data.IndexWithReferencesOnPinnableIncorrectImpl(x, y, v); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Index.Set: References|IncorrectPinnable|refreturn")] |
|||
public void IndexWithReferencesPinnableRefReturn() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
data.IndexWithReferencesOnPinnableIncorrectRefReturnImpl(x, y) = v; |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Index.Set: References|Array[0]Unsafe")]
|
|||
public void IndexWithReferencesArrayBasic() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
data.IndexWithUnsafeReferenceArithmeticsOnArray0Impl(x, y, v); |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "Index.Set: References|Array[0]Unsafe|refreturn")]
|
|||
public void IndexWithReferencesArrayRefReturn() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
data.IndexWithUnsafeReferenceArithmeticsOnArray0RefReturnImpl(x, y) = v; |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "!!! Index.Set: References|Array+Straight")] |
|||
public void IndexWithReferencesArrayStraightforward() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
// No magic.
|
|||
// We just index right into the array as normal people do.
|
|||
// And it looks like this is the fastest way!
|
|||
data.IndexSetArrayStraightforward(x, y, v); |
|||
} |
|||
} |
|||
|
|||
|
|||
[Benchmark(Description = "!!! Index.Set: References|Array+Straight|refreturn")] |
|||
public void IndexWithReferencesArrayStraightforwardRefReturn() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
int y = this.startIndex; |
|||
for (int x = this.startIndex; x < this.endIndex; x++) |
|||
{ |
|||
// No magic.
|
|||
// We just index right into the array as normal people do.
|
|||
// And it looks like this is the fastest way!
|
|||
data.IndexWithReferencesOnArrayStraightforwardRefReturnImpl(x, y) = v; |
|||
} |
|||
} |
|||
|
|||
[Benchmark(Description = "!!! Index.Set: SmartUnsafe")] |
|||
public void SmartUnsafe() |
|||
{ |
|||
Vector4 v = new Vector4(1, 2, 3, 4); |
|||
Data data = new Data(this.buffer); |
|||
|
|||
// This method is basically an unsafe variant of .GetRowSpan(y) + indexing individual pixels in the row.
|
|||
// If a user seriously needs by-pixel manipulation to be performant, we should provide this option.
|
|||
|
|||
ref Vector4 rowStart = ref data.IndexWithReferencesOnArrayStraightforwardRefReturnImpl(this.startIndex, this.startIndex); |
|||
|
|||
for (int i = 0; i < this.Count; i++) |
|||
{ |
|||
// We don't have to add 'Width * y' here!
|
|||
Unsafe.Add(ref rowStart, i) = v; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -1,251 +0,0 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Memory |
|||
{ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
using Xunit; |
|||
|
|||
public unsafe class BufferTests |
|||
{ |
|||
// ReSharper disable once ClassNeverInstantiated.Local
|
|||
private class Assert : Xunit.Assert |
|||
{ |
|||
public static void SpanPointsTo<T>(Span<T> span, Buffer<T> buffer, int bufferOffset = 0) |
|||
where T : struct |
|||
{ |
|||
ref T actual = ref span.DangerousGetPinnableReference(); |
|||
ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset); |
|||
|
|||
Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position"); |
|||
} |
|||
|
|||
public static void Equal(void* expected, void* actual) |
|||
{ |
|||
Assert.Equal((IntPtr)expected, (IntPtr)actual); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1111)] |
|||
public void ConstructWithOwnArray(int count) |
|||
{ |
|||
using (Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(count)) |
|||
{ |
|||
Assert.False(buffer.IsDisposedOrLostArrayOwnership); |
|||
Assert.NotNull(buffer.Array); |
|||
Assert.Equal(count, buffer.Length); |
|||
Assert.True(buffer.Array.Length >= count); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1111)] |
|||
public void ConstructWithExistingArray(int count) |
|||
{ |
|||
TestStructs.Foo[] array = new TestStructs.Foo[count]; |
|||
using (Buffer<TestStructs.Foo> buffer = new Buffer<TestStructs.Foo>(array)) |
|||
{ |
|||
Assert.False(buffer.IsDisposedOrLostArrayOwnership); |
|||
Assert.Equal(array, buffer.Array); |
|||
Assert.Equal(count, buffer.Length); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Clear() |
|||
{ |
|||
TestStructs.Foo[] a = { new TestStructs.Foo() { A = 1, B = 2 }, new TestStructs.Foo() { A = 3, B = 4 } }; |
|||
using (Buffer<TestStructs.Foo> buffer = new Buffer<TestStructs.Foo>(a)) |
|||
{ |
|||
buffer.Clear(); |
|||
|
|||
Assert.Equal(default(TestStructs.Foo), a[0]); |
|||
Assert.Equal(default(TestStructs.Foo), a[1]); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void CreateClean() |
|||
{ |
|||
for (int i = 0; i < 100; i++) |
|||
{ |
|||
using (Buffer<int> buffer = Configuration.Default.MemoryManager.Allocate<int>(42, true)) |
|||
{ |
|||
for (int j = 0; j < buffer.Length; j++) |
|||
{ |
|||
Assert.Equal(0, buffer.Array[j]); |
|||
buffer.Array[j] = 666; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public class Indexer |
|||
{ |
|||
public static readonly TheoryData<int, int> IndexerData = |
|||
new TheoryData<int,int>() |
|||
{ |
|||
{ 10, 0 }, |
|||
{ 16, 3 }, |
|||
{ 10, 9 } |
|||
}; |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(IndexerData))] |
|||
public void Read(int length, int index) |
|||
{ |
|||
TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); |
|||
|
|||
using (Buffer<TestStructs.Foo> buffer = new Buffer<TestStructs.Foo>(a)) |
|||
{ |
|||
TestStructs.Foo element = buffer[index]; |
|||
|
|||
Assert.Equal(a[index], element); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[MemberData(nameof(IndexerData))] |
|||
public void Write(int length, int index) |
|||
{ |
|||
TestStructs.Foo[] a = TestStructs.Foo.CreateArray(length); |
|||
|
|||
using (Buffer<TestStructs.Foo> buffer = new Buffer<TestStructs.Foo>(a)) |
|||
{ |
|||
buffer[index] = new TestStructs.Foo(666, 666); |
|||
|
|||
Assert.Equal(new TestStructs.Foo(666, 666), a[index]); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Dispose() |
|||
{ |
|||
Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(42); |
|||
buffer.Dispose(); |
|||
|
|||
Assert.True(buffer.IsDisposedOrLostArrayOwnership); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(7)] |
|||
[InlineData(123)] |
|||
public void CastToSpan(int bufferLength) |
|||
{ |
|||
using (Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(bufferLength)) |
|||
{ |
|||
Span<TestStructs.Foo> span = buffer; |
|||
|
|||
//Assert.Equal(buffer.Array, span.ToArray());
|
|||
//Assert.Equal(0, span.Start);
|
|||
Assert.SpanPointsTo(span, buffer); |
|||
Assert.Equal(span.Length, bufferLength); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void Span() |
|||
{ |
|||
using (Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(42)) |
|||
{ |
|||
Span<TestStructs.Foo> span = buffer.Span; |
|||
|
|||
// Assert.Equal(buffer.Array, span.ToArray());
|
|||
// Assert.Equal(0, span.Start);
|
|||
Assert.SpanPointsTo(span, buffer); |
|||
Assert.Equal(42, span.Length); |
|||
} |
|||
} |
|||
|
|||
public class Slice |
|||
{ |
|||
|
|||
[Theory] |
|||
[InlineData(7, 2)] |
|||
[InlineData(123, 17)] |
|||
public void WithStartOnly(int bufferLength, int start) |
|||
{ |
|||
using (Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(bufferLength)) |
|||
{ |
|||
Span<TestStructs.Foo> span = buffer.Slice(start); |
|||
|
|||
Assert.SpanPointsTo(span, buffer, start); |
|||
Assert.Equal(span.Length, bufferLength - start); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(7, 2, 5)] |
|||
[InlineData(123, 17, 42)] |
|||
public void WithStartAndLength(int bufferLength, int start, int spanLength) |
|||
{ |
|||
using (Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(bufferLength)) |
|||
{ |
|||
Span<TestStructs.Foo> span = buffer.Slice(start, spanLength); |
|||
|
|||
Assert.SpanPointsTo(span, buffer, start); |
|||
Assert.Equal(span.Length, spanLength); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void UnPinAndTakeArrayOwnership() |
|||
{ |
|||
TestStructs.Foo[] data = null; |
|||
using (Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(42)) |
|||
{ |
|||
data = buffer.TakeArrayOwnership(); |
|||
Assert.True(buffer.IsDisposedOrLostArrayOwnership); |
|||
} |
|||
|
|||
Assert.NotNull(data); |
|||
Assert.True(data.Length >= 42); |
|||
} |
|||
|
|||
public class Pin |
|||
{ |
|||
[Fact] |
|||
public void ReturnsPinnedPointerToTheBeginningOfArray() |
|||
{ |
|||
using (Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(42)) |
|||
{ |
|||
TestStructs.Foo* actual = (TestStructs.Foo*)buffer.Pin(); |
|||
fixed (TestStructs.Foo* expected = buffer.Array) |
|||
{ |
|||
Assert.Equal(expected, actual); |
|||
} |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void SecondCallReturnsTheSamePointer() |
|||
{ |
|||
using (Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(42)) |
|||
{ |
|||
IntPtr ptr1 = buffer.Pin(); |
|||
IntPtr ptr2 = buffer.Pin(); |
|||
|
|||
Assert.Equal(ptr1, ptr2); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException() |
|||
{ |
|||
Buffer<TestStructs.Foo> buffer = Configuration.Default.MemoryManager.Allocate<TestStructs.Foo>(42); |
|||
buffer.Dispose(); |
|||
|
|||
Assert.Throws<InvalidOperationException>(() => buffer.Pin()); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue