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