Browse Source

test cases for MemoryGroup.Allocate()

pull/1109/head
Anton Firszov 6 years ago
parent
commit
c756c3a274
  1. 2
      src/ImageSharp/ImageSharp.csproj.DotSettings
  2. 2
      src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs
  3. 5
      src/ImageSharp/Memory/Allocators/MemoryAllocator.cs
  4. 2
      src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs
  5. 21
      src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
  6. 2
      src/ImageSharp/Memory/DiscontiguousBuffers/InvalidMemoryOperationException.cs
  7. 18
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs
  8. 6
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
  9. 8
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
  10. 16
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
  11. 15
      src/ImageSharp/Memory/DiscontinuousProto/IUniformMemoryGroup{T}.cs
  12. 96
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs
  13. 4
      tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs

2
src/ImageSharp/ImageSharp.csproj.DotSettings

@ -2,6 +2,8 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=color/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Cexceptions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=memory_005Callocators/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=memory_005Cdiscontiguousbuffers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cgenerated/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpackedpixels/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=pixelformats_005Cpixelimplementations/@EntryIndexedValue">True</s:Boolean>

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

@ -95,7 +95,7 @@ namespace SixLabors.ImageSharp.Memory
public int MaximumContiguousBufferLength { get; set; } = Int32.MaxValue;
/// <inheritdoc />
protected internal override int GetMaximumContiguousBufferLength() => this.MaximumContiguousBufferLength;
protected internal override int GetBlockCapacity() => this.MaximumContiguousBufferLength;
/// <inheritdoc />
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)

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

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

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

@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Memory
public sealed class SimpleGcMemoryAllocator : MemoryAllocator
{
/// <inheritdoc />
protected internal override int GetMaximumContiguousBufferLength() => int.MaxValue;
protected internal override int GetBlockCapacity() => int.MaxValue;
/// <inheritdoc />
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)

21
src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// Represents discontiguous group of multiple uniformly-sized memory segments.
/// The last segment can be smaller than the preceding ones.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
public interface IMemoryGroup<T> : IReadOnlyList<Memory<T>>
where T : struct
{
/// <summary>
/// Gets the number of elements per contiguous sub-block.
/// </summary>
public int BlockSize { get; }
bool IsValid { get; }
}
}

2
src/ImageSharp/Memory/DiscontinuousProto/InvalidMemoryOperationException.cs → src/ImageSharp/Memory/DiscontiguousBuffers/InvalidMemoryOperationException.cs

@ -1,6 +1,6 @@
using System;
namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
namespace SixLabors.ImageSharp.Memory
{
public class InvalidMemoryOperationException : InvalidOperationException
{

18
src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroupView{T}.cs → src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs

@ -3,24 +3,24 @@ using System.Buffers;
using System.Collections;
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// Implements <see cref="IUniformMemoryGroup{T}"/>, defining a view for <see cref="UniformMemoryGroup{T}"/>
/// Implements <see cref="IMemoryGroup{T}"/>, defining a view for <see cref="Memory.MemoryGroup{T}"/>
/// rather than owning the segments.
/// </summary>
/// <remarks>
/// This type provides an indirection, protecting the users of publicly exposed memory API-s
/// from internal memory-swaps. Whenever an internal swap happens, the <see cref="UniformMemoryGroupView{T}"/>
/// from internal memory-swaps. Whenever an internal swap happens, the <see cref="MemoryGroupView{T}"/>
/// instance becomes invalid, throwing an exception on all operations.
/// </remarks>
/// <typeparam name="T">The element type.</typeparam>
internal class UniformMemoryGroupView<T> : IUniformMemoryGroup<T> where T : struct
internal class MemoryGroupView<T> : IMemoryGroup<T> where T : struct
{
private readonly UniformMemoryGroup<T> owner;
private readonly Memory.MemoryGroup<T> owner;
private readonly MemoryOwnerWrapper[] memoryWrappers;
public UniformMemoryGroupView(UniformMemoryGroup<T> owner)
public MemoryGroupView(Memory.MemoryGroup<T> owner)
{
this.IsValid = true;
this.owner = owner;
@ -40,15 +40,17 @@ namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
public Memory<T> this[int index] => throw new NotImplementedException();
public int BlockSize => this.owner.BlockSize;
public bool IsValid { get; internal set; }
class MemoryOwnerWrapper : MemoryManager<T>
{
private UniformMemoryGroupView<T> view;
private MemoryGroupView<T> view;
private int index;
public MemoryOwnerWrapper(UniformMemoryGroupView<T> view, int index)
public MemoryOwnerWrapper(MemoryGroupView<T> view, int index)
{
this.view = view;
this.index = index;

6
src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.Consumed.cs → src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs

@ -1,12 +1,12 @@
using System;
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
namespace SixLabors.ImageSharp.Memory
{
internal abstract partial class UniformMemoryGroup<T>
internal abstract partial class MemoryGroup<T>
{
// Analogous to the "consumed" variant of MemorySource
private class Consumed : UniformMemoryGroup<T>
private class Consumed : MemoryGroup<T>
{
private readonly ReadOnlyMemory<Memory<T>> source;

8
src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.Owned.cs → src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs

@ -3,12 +3,12 @@ using System.Buffers;
using System.Collections.Generic;
using System.Linq;
namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
namespace SixLabors.ImageSharp.Memory
{
// Analogous to the "owned" variant of MemorySource
internal abstract partial class UniformMemoryGroup<T>
internal abstract partial class MemoryGroup<T>
{
private class Owned : UniformMemoryGroup<T>
private class Owned : MemoryGroup<T>
{
private IMemoryOwner<T>[] memoryOwners;
@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
{
if (this.memoryOwners == null)
{
throw new ObjectDisposedException(nameof(UniformMemoryGroup<T>));
throw new ObjectDisposedException(nameof(MemoryGroup<T>));
}
}
}

16
src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.cs → src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs

@ -2,7 +2,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// Represents discontinuous group of multiple uniformly-sized memory segments.
@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
/// <see cref="Image{TPixel}"/> and <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
internal abstract partial class UniformMemoryGroup<T> : IUniformMemoryGroup<T>, IDisposable where T : struct
internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable where T : struct
{
public abstract IEnumerator<Memory<T>> GetEnumerator();
@ -22,26 +22,28 @@ namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
public abstract void Dispose();
public int BlockSize { get; }
public bool IsValid { get; protected set; }
// bufferLengthAlignment == image.Width in row-major images
public static UniformMemoryGroup<T> Allocate(MemoryAllocator allocator, long length, int bufferLengthAlignment)
public static MemoryGroup<T> Allocate(MemoryAllocator allocator, long totalLength, int blockAlignment)
{
long bufferCount = length / allocator.GetMaximumContiguousBufferLength();
long bufferCount = totalLength / allocator.GetBlockCapacity();
// TODO: Adjust bufferCount, and calculate the uniform buffer length with respect to bufferLengthAlignment, and allocate bufferCount buffers
throw new NotImplementedException();
}
public static UniformMemoryGroup<T> Wrap(params Memory<T>[] source) => Wrap(source.AsMemory());
public static MemoryGroup<T> Wrap(params Memory<T>[] source) => Wrap(source.AsMemory());
public static UniformMemoryGroup<T> Wrap(ReadOnlyMemory<Memory<T>> source)
public static MemoryGroup<T> Wrap(ReadOnlyMemory<Memory<T>> source)
{
return new Consumed(source);
}
// Analogous to current MemorySource.SwapOrCopyContent()
public static void SwapOrCopyContent(UniformMemoryGroup<T> destination, UniformMemoryGroup<T> source)
public static void SwapOrCopyContent(MemoryGroup<T> destination, MemoryGroup<T> source)
{
throw new NotImplementedException();
}

15
src/ImageSharp/Memory/DiscontinuousProto/IUniformMemoryGroup{T}.cs

@ -1,15 +0,0 @@
using System;
using System.Collections.Generic;
namespace SixLabors.ImageSharp.Memory.DiscontinuousProto
{
/// <summary>
/// Represents discontinuous group of multiple uniformly-sized memory segments.
/// The last segment can be smaller than the preceding ones.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
public interface IUniformMemoryGroup<T> : IReadOnlyList<Memory<T>> where T : struct
{
bool IsValid { get; }
}
}

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

@ -0,0 +1,96 @@
using System.Linq;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{
public class MemoryGroupTests
{
public class Allocate
{
private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator();
#pragma warning disable SA1509
public static TheoryData<object, int, int, long, int, int, int> AllocateData =
new TheoryData<object, int, int, long, int, int, int>()
{
{ default(S5), 22, 4, 4, 1, 4, 4 },
{ default(S5), 22, 4, 7, 2, 4, 3 },
{ default(S5), 22, 4, 8, 2, 4, 4 },
{ default(S5), 22, 4, 21, 5, 4, 1 },
{ default(S5), 22, 4, 0, 0, -1, -1 },
{ default(S4), 50, 12, 12, 1, 12, 12 },
{ default(S4), 50, 7, 12, 2, 7, 5 },
{ default(S4), 50, 6, 12, 2, 6, 6 },
{ default(S4), 50, 5, 12, 2, 10, 2 },
{ default(S4), 50, 4, 12, 1, 12, 12 },
{ default(S4), 50, 3, 12, 1, 12, 12 },
{ default(S4), 50, 2, 12, 1, 12, 12 },
{ default(S4), 50, 1, 12, 1, 12, 12 },
{ default(S4), 50, 12, 13, 2, 12, 1 },
{ default(S4), 50, 7, 21, 3, 7, 7 },
{ default(S4), 50, 7, 23, 3, 7, 2 },
{ default(byte), 1000, 512, 2047, 4, 512, 511 }
};
[Theory]
[MemberData(nameof(AllocateData))]
public void CreatesBlocksOfCorrectSizes<T>(
T dummy,
int blockCapacity,
int blockAlignment,
long totalLength,
int expectedNumberOfBlocks,
int expectedBlockSize,
int expectedSizeOfLastBlock)
where T : struct
{
this.memoryAllocator.BlockCapacity = blockCapacity;
// Act:
using var g = MemoryGroup<T>.Allocate(this.memoryAllocator, totalLength, blockAlignment);
// Assert:
Assert.Equal(expectedNumberOfBlocks, g.Count);
Assert.Equal(expectedBlockSize, g.BlockSize);
if (g.Count == 0)
{
return;
}
for (int i = 0; i < g.Count - 1; i++)
{
Assert.Equal(g[i].Length, expectedBlockSize);
}
Assert.Equal(g.Last().Length, expectedSizeOfLastBlock);
}
[Fact]
public void WhenBlockAlignmentIsOverCapacity_Throws_InvalidMemoryOperationException()
{
this.memoryAllocator.BlockCapacity = 42;
Assert.Throws<InvalidMemoryOperationException>(() =>
{
MemoryGroup<byte>.Allocate(this.memoryAllocator, 50, 43);
});
}
}
[StructLayout(LayoutKind.Sequential, Size = 5)]
private struct S5
{
}
[StructLayout(LayoutKind.Sequential, Size = 4)]
private struct S4
{
}
}
}

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

@ -25,9 +25,11 @@ namespace SixLabors.ImageSharp.Tests.Memory
/// </summary>
public byte DirtyValue { get; }
public int BlockCapacity { get; set; } = int.MaxValue;
public IList<AllocationRequest> AllocationLog => this.allocationLog;
protected internal override int GetMaximumContiguousBufferLength() => int.MaxValue;
protected internal override int GetBlockCapacity() => this.BlockCapacity;
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)
{

Loading…
Cancel
Save