diff --git a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs
index ecf064339..1f52e4bfd 100644
--- a/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs
+++ b/src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs
@@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Memory
///
/// The buffer implementation of
///
- private class Buffer : IBuffer
+ private class Buffer : ManagedBufferBase, IBuffer
where T : struct
{
///
@@ -45,12 +45,9 @@ namespace SixLabors.ImageSharp.Memory
protected byte[] Data { get; private set; }
///
- public Span GetSpan() => MemoryMarshal.Cast(this.Data.AsSpan()).Slice(0, this.length);
-
- ///
- public void Dispose()
+ protected override void Dispose(bool disposing)
{
- if (this.Data == null || this.sourcePoolReference == null)
+ if (!disposing || this.Data == null || this.sourcePoolReference == null)
{
return;
}
@@ -63,6 +60,10 @@ namespace SixLabors.ImageSharp.Memory
this.sourcePoolReference = null;
this.Data = null;
}
+
+ public override Span GetSpan() => MemoryMarshal.Cast(this.Data.AsSpan()).Slice(0, this.length);
+
+ protected override object GetPinnableObject() => this.Data;
}
///
diff --git a/src/ImageSharp/Memory/BasicArrayBuffer.cs b/src/ImageSharp/Memory/BasicArrayBuffer.cs
index 757488755..2fc62b11e 100644
--- a/src/ImageSharp/Memory/BasicArrayBuffer.cs
+++ b/src/ImageSharp/Memory/BasicArrayBuffer.cs
@@ -1,4 +1,5 @@
using System;
+using System.Buffers;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory
@@ -6,7 +7,7 @@ namespace SixLabors.ImageSharp.Memory
///
/// Exposes an array through the interface.
///
- internal class BasicArrayBuffer : IBuffer
+ internal class BasicArrayBuffer : ManagedBufferBase, IBuffer
where T : struct
{
public BasicArrayBuffer(T[] array, int length)
@@ -42,11 +43,15 @@ namespace SixLabors.ImageSharp.Memory
}
}
- ///
- public Span GetSpan() => this.Array.AsSpan(0, this.Length);
+ protected override void Dispose(bool disposing)
+ {
+ }
+
+ public override Span GetSpan() => this.Array.AsSpan(0, this.Length);
- public void Dispose()
+ protected override object GetPinnableObject()
{
+ return this.Array;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Memory/BufferExtensions.cs b/src/ImageSharp/Memory/BufferExtensions.cs
index 0f04f34f2..8da9157f4 100644
--- a/src/ImageSharp/Memory/BufferExtensions.cs
+++ b/src/ImageSharp/Memory/BufferExtensions.cs
@@ -10,6 +10,22 @@ namespace SixLabors.ImageSharp.Memory
{
internal static class BufferExtensions
{
+ public static Memory GetMemory(this IBuffer buffer)
+ where T : struct
+ {
+ System.Buffers.MemoryManager bufferManager = buffer as System.Buffers.MemoryManager;
+
+ if (bufferManager == null)
+ {
+ // TODO: We need a better way to integrate IBuffer with MemoryManager. The prior should probably entirely replace the latter!
+ throw new ArgumentException(
+ "BufferExtensions.GetMemory(buffer): buffer should be convertable to System.Buffers.MemoryManager!",
+ nameof(buffer));
+ }
+
+ return bufferManager.Memory;
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Length(this IBuffer buffer)
where T : struct => buffer.GetSpan().Length;
diff --git a/src/ImageSharp/Memory/IBuffer{T}.cs b/src/ImageSharp/Memory/IBuffer{T}.cs
index 9943134f5..ab139e175 100644
--- a/src/ImageSharp/Memory/IBuffer{T}.cs
+++ b/src/ImageSharp/Memory/IBuffer{T}.cs
@@ -8,6 +8,7 @@ namespace SixLabors.ImageSharp.Memory
///
///
/// Represents a contigous memory buffer of value-type items "promising" a
+ /// A proper im
///
/// The value type
internal interface IBuffer : IDisposable
diff --git a/src/ImageSharp/Memory/ManagedBufferBase.cs b/src/ImageSharp/Memory/ManagedBufferBase.cs
new file mode 100644
index 000000000..1291bcbb1
--- /dev/null
+++ b/src/ImageSharp/Memory/ManagedBufferBase.cs
@@ -0,0 +1,40 @@
+// Copyright (c) Six Labors and contributors.
+// Licensed under the Apache License, Version 2.0.
+
+using System.Buffers;
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.Memory
+{
+ ///
+ /// Provides a base class for implementations by implementing pinning logic for adaption.
+ ///
+ internal abstract class ManagedBufferBase : System.Buffers.MemoryManager
+ {
+ private GCHandle pinHandle;
+
+ ///
+ /// Gets the object that should be pinned.
+ ///
+ protected abstract object GetPinnableObject();
+
+ public override unsafe MemoryHandle Pin(int elementIndex = 0)
+ {
+ if (!this.pinHandle.IsAllocated)
+ {
+ this.pinHandle = GCHandle.Alloc(this.GetPinnableObject(), GCHandleType.Pinned);
+ }
+
+ void* ptr = (void*)this.pinHandle.AddrOfPinnedObject();
+ return new MemoryHandle(ptr, this.pinHandle);
+ }
+
+ public override void Unpin()
+ {
+ if (this.pinHandle.IsAllocated)
+ {
+ this.pinHandle.Free();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs
index 68c6632b9..fe2b1b8bf 100644
--- a/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs
+++ b/tests/ImageSharp.Tests/Memory/BufferTestSuite.cs
@@ -10,7 +10,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Memory
{
-
+ using System.Buffers;
///
/// Inherit this class to test an implementation (provided by ).
@@ -282,5 +282,40 @@ namespace SixLabors.ImageSharp.Tests.Memory
Assert.True(buffer.Array.Length >= buffer.GetSpan().Length);
}
}
+
+ [Fact]
+ public void GetMemory_ReturnsValidMemory()
+ {
+ using (IBuffer buffer = this.MemoryManager.Allocate(42))
+ {
+ Span span0 = buffer.GetSpan();
+ span0[10].A = 30;
+ Memory memory = buffer.GetMemory();
+
+ Assert.Equal(42, memory.Length);
+ Span span1 = memory.Span;
+
+ Assert.Equal(42, span1.Length);
+ Assert.Equal(30, span1[10].A);
+ }
+ }
+
+ [Fact]
+ public unsafe void GetMemory_ResultIsPinnable()
+ {
+ using (IBuffer buffer = this.MemoryManager.Allocate(42))
+ {
+ Span span0 = buffer.GetSpan();
+ span0[10] = 30;
+
+ Memory memory = buffer.GetMemory();
+
+ using (MemoryHandle h = memory.Pin())
+ {
+ int* ptr = (int*) h.Pointer;
+ Assert.Equal(30, ptr[10]);
+ }
+ }
+ }
}
}
\ No newline at end of file