Browse Source

CopyTo WIP

pull/1109/head
Anton Firszov 6 years ago
parent
commit
bbd4daffe5
  1. 12
      src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs
  2. 4
      src/ImageSharp/Memory/Allocators/MemoryAllocator.cs
  3. 4
      src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs
  4. 83
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
  5. 2
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
  6. 5
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndex.cs
  7. 5
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs
  8. 6
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs
  9. 5
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs
  10. 26
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs
  11. 32
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTestsBase.cs
  12. 4
      tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs

12
src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs

@ -83,19 +83,19 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
public int PoolSelectorThresholdInBytes { get; }
/// <summary>
/// Gets or sets the length of the largest contiguous buffer that can be handled by this allocator instance.
/// </summary>
public int BufferCapacityInBytes { get; set; } = DefaultBufferCapacity;
/// <inheritdoc />
public override void ReleaseRetainedResources()
{
this.InitArrayPools();
}
/// <summary>
/// Gets or sets the length of the largest contiguous buffer that can be handled by this allocator instance.
/// </summary>
public int MaximumContiguousBufferLength { get; set; } = Int32.MaxValue;
/// <inheritdoc />
protected internal override int GetBufferCapacity() => this.MaximumContiguousBufferLength;
protected internal override int GetBufferCapacityInBytes() => this.BufferCapacityInBytes;
/// <inheritdoc />
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)

4
src/ImageSharp/Memory/Allocators/MemoryAllocator.cs

@ -10,11 +10,13 @@ namespace SixLabors.ImageSharp.Memory
/// </summary>
public abstract class MemoryAllocator
{
internal const int DefaultBufferCapacity = int.MaxValue / 2;
/// <summary>
/// Gets the length of the largest contiguous buffer that can be handled by this allocator instance in bytes.
/// </summary>
/// <returns>The length of the largest contiguous buffer that can be handled by this allocator instance.</returns>
protected internal abstract int GetBufferCapacity();
protected internal abstract int GetBufferCapacityInBytes();
/// <summary>
/// Allocates an <see cref="IMemoryOwner{T}" />, holding a <see cref="System.Memory{T}"/> of length <paramref name="length"/>.

4
src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs

@ -7,12 +7,12 @@ using SixLabors.ImageSharp.Memory.Internals;
namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// Implements <see cref="MemoryAllocator"/> by newing up arrays by the GC on every allocation requests.
/// Implements <see cref="MemoryAllocator"/> by newing up managed arrays on every allocation request.
/// </summary>
public sealed class SimpleGcMemoryAllocator : MemoryAllocator
{
/// <inheritdoc />
protected internal override int GetBufferCapacity() => int.MaxValue;
protected internal override int GetBufferCapacityInBytes() => DefaultBufferCapacity;
/// <inheritdoc />
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)

83
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs

@ -10,7 +10,88 @@ namespace SixLabors.ImageSharp.Memory
public static void CopyTo<T>(this IMemoryGroup<T> source, IMemoryGroup<T> target)
where T : struct
{
throw new NotImplementedException();
Guard.NotNull(source, nameof(source));
Guard.NotNull(target, nameof(target));
Guard.IsTrue(source.IsValid, nameof(source), "Source group must be valid.");
Guard.IsTrue(target.IsValid, nameof(target), "Target group must be valid.");
Guard.MustBeLessThanOrEqualTo(source.TotalLength, target.TotalLength, "Destination buffer too short!");
if (source.IsEmpty())
{
return;
}
long position = 0;
var srcCur = new MemoryGroupCursor<T>(source);
var trgCur = new MemoryGroupCursor<T>(target);
while (position < source.TotalLength)
{
int fwd = Math.Min(srcCur.LookAhead(), trgCur.LookAhead());
Span<T> srcSpan = srcCur.GetSpan(fwd);
Span<T> trgSpan = trgCur.GetSpan(fwd);
srcSpan.CopyTo(trgSpan);
srcCur.Forward(fwd);
trgCur.Forward(fwd);
position += fwd;
}
}
public static bool IsEmpty<T>(this IMemoryGroup<T> group)
where T : struct
=> group.Count == 0;
private struct MemoryGroupCursor<T>
where T : struct
{
private readonly IMemoryGroup<T> memoryGroup;
private int bufferIndex;
private int elementIndex;
public MemoryGroupCursor(IMemoryGroup<T> memoryGroup)
{
this.memoryGroup = memoryGroup;
this.bufferIndex = 0;
this.elementIndex = 0;
}
private bool IsAtLastBuffer => this.bufferIndex == this.memoryGroup.Count - 1;
private int CurrentBufferLength => this.memoryGroup[this.bufferIndex].Length;
public Span<T> GetSpan(int length)
{
return this.memoryGroup[this.bufferIndex].Span.Slice(this.elementIndex, length);
}
public int LookAhead()
{
return this.CurrentBufferLength - this.elementIndex;
}
public void Forward(int steps)
{
int nextIdx = this.elementIndex + steps;
int currentBufferLength = this.CurrentBufferLength;
if (nextIdx < currentBufferLength)
{
this.elementIndex = nextIdx;
}
else if (nextIdx == currentBufferLength)
{
this.bufferIndex++;
this.elementIndex = 0;
}
else
{
// If we get here, it indicates a bug in CopyTo<T>:
throw new ArgumentException("Can't forward multiple buffers!", nameof(steps));
}
}
}
}
}

2
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Memory
Guard.MustBeGreaterThanOrEqualTo(totalLength, 0, nameof(totalLength));
Guard.MustBeGreaterThan(blockAlignment, 0, nameof(blockAlignment));
int blockCapacityInElements = allocator.GetBufferCapacity() / ElementSize;
int blockCapacityInElements = allocator.GetBufferCapacityInBytes() / ElementSize;
if (blockAlignment > blockCapacityInElements)
{
throw new InvalidMemoryOperationException();

5
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndex.cs

@ -1,4 +1,7 @@
using System;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers

5
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndexTests.cs

@ -1,4 +1,7 @@
using Xunit;
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using Xunit;
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{

6
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs

@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
int expectedSizeOfLastBuffer)
where T : struct
{
this.MemoryAllocator.BufferCapacity = bufferCapacity;
this.MemoryAllocator.BufferCapacityInBytes = bufferCapacity;
// Act:
using var g = MemoryGroup<T>.Allocate(this.MemoryAllocator, totalLength, bufferAlignment);
@ -77,7 +77,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
[Fact]
public void WhenBlockAlignmentIsOverCapacity_Throws_InvalidMemoryOperationException()
{
this.MemoryAllocator.BufferCapacity = 84; // 42 * Int16
this.MemoryAllocator.BufferCapacityInBytes = 84; // 42 * Int16
Assert.Throws<InvalidMemoryOperationException>(() =>
{
@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
[InlineData(AllocationOptions.Clean)]
public void MemoryAllocatorIsUtilizedCorrectly(AllocationOptions allocationOptions)
{
this.MemoryAllocator.BufferCapacity = 200;
this.MemoryAllocator.BufferCapacityInBytes = 200;
HashSet<int> bufferHashes;

5
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs

@ -10,15 +10,20 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{
public class CopyTo : MemoryGroupTestsBase
{
#pragma warning disable SA1509
public static readonly TheoryData<int, int, int, int> WhenSourceBufferIsShorterOrEqual_Data =
new TheoryData<int, int, int, int>()
{
{ 20, 10, 20, 10 },
{ 20, 5, 20, 4 },
{ 20, 4, 20, 5 },
{ 18, 6, 20, 5 },
{ 19, 10, 20, 10 },
{ 21, 10, 22, 2 },
{ 1, 5, 5, 4 },
{ 30, 12, 40, 5 },
{ 30, 5, 40, 12 },
};
[Theory]

26
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs

@ -26,7 +26,6 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
Assert.False(g.IsValid);
}
[StructLayout(LayoutKind.Sequential, Size = 5)]
private struct S5
{
@ -39,29 +38,4 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
public override string ToString() => "S4";
}
}
public abstract class MemoryGroupTestsBase
{
internal readonly TestMemoryAllocator MemoryAllocator = new TestMemoryAllocator();
internal MemoryGroup<int> CreateTestGroup(long totalLength, int bufferLength, bool fillSequence = false)
{
this.MemoryAllocator.BufferCapacity = bufferLength;
var g = MemoryGroup<int>.Allocate(this.MemoryAllocator, totalLength, bufferLength);
if (!fillSequence)
{
return g;
}
int j = 1;
for (MemoryGroupIndex i = g.MinIndex(); i < g.MaxIndex(); i += 1)
{
g.SetElementAt(i, j);
j++;
}
return g;
}
}
}

32
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTestsBase.cs

@ -0,0 +1,32 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using SixLabors.ImageSharp.Memory;
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{
public abstract class MemoryGroupTestsBase
{
internal readonly TestMemoryAllocator MemoryAllocator = new TestMemoryAllocator();
internal MemoryGroup<int> CreateTestGroup(long totalLength, int bufferLength, bool fillSequence = false)
{
this.MemoryAllocator.BufferCapacityInBytes = bufferLength * sizeof(int);
var g = MemoryGroup<int>.Allocate(this.MemoryAllocator, totalLength, bufferLength);
if (!fillSequence)
{
return g;
}
int j = 1;
for (MemoryGroupIndex i = g.MinIndex(); i < g.MaxIndex(); i += 1)
{
g.SetElementAt(i, j);
j++;
}
return g;
}
}
}

4
tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs

@ -26,13 +26,13 @@ namespace SixLabors.ImageSharp.Tests.Memory
/// </summary>
public byte DirtyValue { get; }
public int BufferCapacity { get; set; } = int.MaxValue;
public int BufferCapacityInBytes { get; set; } = int.MaxValue;
public IReadOnlyList<AllocationRequest> AllocationLog => this.allocationLog;
public IReadOnlyList<ReturnRequest> ReturnLog => this.returnLog;
protected internal override int GetBufferCapacity() => this.BufferCapacity;
protected internal override int GetBufferCapacityInBytes() => this.BufferCapacityInBytes;
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)
{

Loading…
Cancel
Save