Browse Source

System.Buffers.MemoryManager<T> is adapted

pull/607/head
Anton Firszov 8 years ago
parent
commit
8c45946078
  1. 13
      src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs
  2. 13
      src/ImageSharp/Memory/BasicArrayBuffer.cs
  3. 16
      src/ImageSharp/Memory/BufferExtensions.cs
  4. 1
      src/ImageSharp/Memory/IBuffer{T}.cs
  5. 40
      src/ImageSharp/Memory/ManagedBufferBase.cs
  6. 37
      tests/ImageSharp.Tests/Memory/BufferTestSuite.cs

13
src/ImageSharp/Memory/ArrayPoolMemoryManager.Buffer{T}.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Memory
/// <summary>
/// The buffer implementation of <see cref="ArrayPoolMemoryManager"/>
/// </summary>
private class Buffer<T> : IBuffer<T>
private class Buffer<T> : ManagedBufferBase<T>, IBuffer<T>
where T : struct
{
/// <summary>
@ -45,12 +45,9 @@ namespace SixLabors.ImageSharp.Memory
protected byte[] Data { get; private set; }
/// <inheritdoc />
public Span<T> GetSpan() => MemoryMarshal.Cast<byte, T>(this.Data.AsSpan()).Slice(0, this.length);
/// <inheritdoc />
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<T> GetSpan() => MemoryMarshal.Cast<byte, T>(this.Data.AsSpan()).Slice(0, this.length);
protected override object GetPinnableObject() => this.Data;
}
/// <summary>

13
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
/// <summary>
/// Exposes an array through the <see cref="IBuffer{T}"/> interface.
/// </summary>
internal class BasicArrayBuffer<T> : IBuffer<T>
internal class BasicArrayBuffer<T> : ManagedBufferBase<T>, IBuffer<T>
where T : struct
{
public BasicArrayBuffer(T[] array, int length)
@ -42,11 +43,15 @@ namespace SixLabors.ImageSharp.Memory
}
}
/// <inheritdoc />
public Span<T> GetSpan() => this.Array.AsSpan(0, this.Length);
protected override void Dispose(bool disposing)
{
}
public override Span<T> GetSpan() => this.Array.AsSpan(0, this.Length);
public void Dispose()
protected override object GetPinnableObject()
{
return this.Array;
}
}
}

16
src/ImageSharp/Memory/BufferExtensions.cs

@ -10,6 +10,22 @@ namespace SixLabors.ImageSharp.Memory
{
internal static class BufferExtensions
{
public static Memory<T> GetMemory<T>(this IBuffer<T> buffer)
where T : struct
{
System.Buffers.MemoryManager<T> bufferManager = buffer as System.Buffers.MemoryManager<T>;
if (bufferManager == null)
{
// TODO: We need a better way to integrate IBuffer<T> with MemoryManager<T>. The prior should probably entirely replace the latter!
throw new ArgumentException(
"BufferExtensions.GetMemory<T>(buffer): buffer should be convertable to System.Buffers.MemoryManager<T>!",
nameof(buffer));
}
return bufferManager.Memory;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int Length<T>(this IBuffer<T> buffer)
where T : struct => buffer.GetSpan().Length;

1
src/ImageSharp/Memory/IBuffer{T}.cs

@ -8,6 +8,7 @@ namespace SixLabors.ImageSharp.Memory
/// <inheritdoc />
/// <summary>
/// Represents a contigous memory buffer of value-type items "promising" a <see cref="Span{T}"/>
/// A proper im
/// </summary>
/// <typeparam name="T">The value type</typeparam>
internal interface IBuffer<T> : IDisposable

40
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
{
/// <summary>
/// Provides a base class for <see cref="IBuffer{T}"/> implementations by implementing pinning logic for <see cref="MemoryManager{T}"/> adaption.
/// </summary>
internal abstract class ManagedBufferBase<T> : System.Buffers.MemoryManager<T>
{
private GCHandle pinHandle;
/// <summary>
/// Gets the object that should be pinned.
/// </summary>
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();
}
}
}
}

37
tests/ImageSharp.Tests/Memory/BufferTestSuite.cs

@ -10,7 +10,7 @@ using Xunit;
namespace SixLabors.ImageSharp.Tests.Memory
{
using System.Buffers;
/// <summary>
/// Inherit this class to test an <see cref="IBuffer{T}"/> implementation (provided by <see cref="MemoryManager"/>).
@ -282,5 +282,40 @@ namespace SixLabors.ImageSharp.Tests.Memory
Assert.True(buffer.Array.Length >= buffer.GetSpan().Length);
}
}
[Fact]
public void GetMemory_ReturnsValidMemory()
{
using (IBuffer<CustomStruct> buffer = this.MemoryManager.Allocate<CustomStruct>(42))
{
Span<CustomStruct> span0 = buffer.GetSpan();
span0[10].A = 30;
Memory<CustomStruct> memory = buffer.GetMemory();
Assert.Equal(42, memory.Length);
Span<CustomStruct> span1 = memory.Span;
Assert.Equal(42, span1.Length);
Assert.Equal(30, span1[10].A);
}
}
[Fact]
public unsafe void GetMemory_ResultIsPinnable()
{
using (IBuffer<int> buffer = this.MemoryManager.Allocate<int>(42))
{
Span<int> span0 = buffer.GetSpan();
span0[10] = 30;
Memory<int> memory = buffer.GetMemory();
using (MemoryHandle h = memory.Pin())
{
int* ptr = (int*) h.Pointer;
Assert.Equal(30, ptr[10]);
}
}
}
}
}
Loading…
Cancel
Save