Browse Source

Allocate works

af/octree-no-pixelmap
Anton Firszov 6 years ago
parent
commit
b74e1b7695
  1. 2
      src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs
  2. 2
      src/ImageSharp/Memory/Allocators/MemoryAllocator.cs
  3. 2
      src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs
  4. 2
      src/ImageSharp/Memory/DiscontiguousBuffers/IMemoryGroup{T}.cs
  5. 18
      src/ImageSharp/Memory/DiscontiguousBuffers/InvalidMemoryOperationException.cs
  6. 2
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs
  7. 13
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
  8. 3
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
  9. 79
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
  10. 64
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs
  11. 4
      tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs

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 GetBlockCapacity() => this.MaximumContiguousBufferLength;
protected internal override int GetBufferCapacity() => this.MaximumContiguousBufferLength;
/// <inheritdoc />
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)

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

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Memory
/// 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 GetBlockCapacity();
protected internal abstract int GetBufferCapacity();
/// <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 GetBlockCapacity() => int.MaxValue;
protected internal override int GetBufferCapacity() => int.MaxValue;
/// <inheritdoc />
public override IMemoryOwner<T> Allocate<T>(int length, AllocationOptions options = AllocationOptions.None)

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

@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Memory
/// <summary>
/// Gets the number of elements per contiguous sub-block.
/// </summary>
public int BlockSize { get; }
public int BufferSize { get; }
bool IsValid { get; }
}

18
src/ImageSharp/Memory/DiscontiguousBuffers/InvalidMemoryOperationException.cs

@ -2,7 +2,25 @@ using System;
namespace SixLabors.ImageSharp.Memory
{
/// <summary>
/// Exception thrown on invalid memory (allocation) requests.
/// </summary>
public class InvalidMemoryOperationException : InvalidOperationException
{
/// <summary>
/// Initializes a new instance of the <see cref="InvalidMemoryOperationException"/> class.
/// </summary>
/// <param name="message">The exception message text.</param>
public InvalidMemoryOperationException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="InvalidMemoryOperationException"/> class.
/// </summary>
public InvalidMemoryOperationException()
{
}
}
}

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

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Memory
public Memory<T> this[int index] => throw new NotImplementedException();
public int BlockSize => this.owner.BlockSize;
public int BufferSize => this.owner.BufferSize;
public bool IsValid { get; internal set; }

13
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs

@ -10,13 +10,16 @@ namespace SixLabors.ImageSharp.Memory
{
private readonly ReadOnlyMemory<Memory<T>> source;
public Consumed(ReadOnlyMemory<Memory<T>> source)
public Consumed(ReadOnlyMemory<Memory<T>> source, int bufferSize)
: base(bufferSize)
{
// TODO: sizes should be uniform, validate!
this.source = source;
}
public override int Count => this.source.Length;
public override Memory<T> this[int index] => this.source.Span[index];
public override IEnumerator<Memory<T>> GetEnumerator()
{
for (int i = 0; i < this.source.Length; i++)
@ -25,10 +28,6 @@ namespace SixLabors.ImageSharp.Memory
}
}
public override int Count => this.source.Length;
public override Memory<T> this[int index] => this.source.Span[index];
public override void Dispose()
{
// No ownership nothing to dispose

3
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs

@ -12,7 +12,8 @@ namespace SixLabors.ImageSharp.Memory
{
private IMemoryOwner<T>[] memoryOwners;
public Owned(IMemoryOwner<T>[] memoryOwners)
public Owned(IMemoryOwner<T>[] memoryOwners, int bufferSize)
: base(bufferSize)
{
this.memoryOwners = memoryOwners;
}

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

@ -1,6 +1,8 @@
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
namespace SixLabors.ImageSharp.Memory
{
@ -10,39 +12,96 @@ namespace SixLabors.ImageSharp.Memory
/// <see cref="Image{TPixel}"/> and <see cref="ImageFrame{TPixel}"/>.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable where T : struct
internal abstract partial class MemoryGroup<T> : IMemoryGroup<T>, IDisposable
where T : struct
{
public abstract IEnumerator<Memory<T>> GetEnumerator();
private static readonly int ElementSize = Unsafe.SizeOf<T>();
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
private MemoryGroup(int bufferSize) => this.BufferSize = bufferSize;
public abstract int Count { get; }
public int BufferSize { get; }
public bool IsValid { get; private set; } = true;
public abstract Memory<T> this[int index] { get; }
public abstract void Dispose();
public int BlockSize { get; }
public abstract IEnumerator<Memory<T>> GetEnumerator();
public bool IsValid { get; protected set; }
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
// bufferLengthAlignment == image.Width in row-major images
public static MemoryGroup<T> Allocate(MemoryAllocator allocator,
public static MemoryGroup<T> Allocate(
MemoryAllocator allocator,
long totalLength,
int blockAlignment,
AllocationOptions allocationOptions = AllocationOptions.None)
{
long bufferCount = totalLength / allocator.GetBlockCapacity();
Guard.NotNull(allocator, nameof(allocator));
Guard.MustBeGreaterThanOrEqualTo(totalLength, 0, nameof(totalLength));
Guard.MustBeGreaterThan(blockAlignment, 0, nameof(blockAlignment));
// TODO: Adjust bufferCount, and calculate the uniform buffer length with respect to bufferLengthAlignment, and allocate bufferCount buffers
throw new NotImplementedException();
int blockCapacityInElements = allocator.GetBufferCapacity() / ElementSize;
if (blockAlignment > blockCapacityInElements)
{
throw new InvalidMemoryOperationException();
}
int numberOfAlignedSegments = blockCapacityInElements / blockAlignment;
int bufferSize = numberOfAlignedSegments * blockAlignment;
if (totalLength > 0 && totalLength < bufferSize)
{
bufferSize = (int)totalLength;
}
int sizeOfLastBuffer = (int)(totalLength % bufferSize);
long bufferCount = totalLength / bufferSize;
if (sizeOfLastBuffer == 0)
{
sizeOfLastBuffer = bufferSize;
}
else
{
bufferCount++;
}
var buffers = new IMemoryOwner<T>[bufferCount];
for (int i = 0; i < buffers.Length - 1; i++)
{
buffers[i] = allocator.Allocate<T>(bufferSize, allocationOptions);
}
if (bufferCount > 0)
{
buffers[^1] = allocator.Allocate<T>(sizeOfLastBuffer, allocationOptions);
}
return new Owned(buffers, bufferSize);
}
public static MemoryGroup<T> Wrap(params Memory<T>[] source) => Wrap(source.AsMemory());
public static MemoryGroup<T> Wrap(ReadOnlyMemory<Memory<T>> source)
{
return new Consumed(source);
int bufferSize = source.Length > 0 ? source.Span[0].Length : 0;
for (int i = 1; i < source.Length - 1; i++)
{
if (source.Span[i].Length != bufferSize)
{
throw new InvalidMemoryOperationException("Wrap: buffers should be uniformly sized!");
}
}
if (source.Length > 0 && source.Span[^1].Length > bufferSize)
{
throw new InvalidMemoryOperationException("Wrap: the last buffer is too large!");
}
return new Consumed(source, bufferSize);
}
// Analogous to current MemorySource.SwapOrCopyContent()

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

@ -3,11 +3,14 @@ using System.Linq;
using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory;
using Xunit;
using Xunit.Sdk;
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{
public class MemoryGroupTests
{
private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator();
public class Allocate
{
private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator();
@ -19,12 +22,12 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{ 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(S5), 22, 4, 21, 6, 4, 1 },
{ default(S5), 22, 4, 0, 0, 4, -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, 6, 12, 1, 12, 12 },
{ default(S4), 50, 5, 12, 2, 10, 2 },
{ default(S4), 50, 4, 12, 1, 12, 12 },
{ default(S4), 50, 3, 12, 1, 12, 12 },
@ -33,31 +36,34 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{ 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(S4), 50, 7, 23, 4, 7, 2 },
{ default(S4), 50, 6, 13, 2, 12, 1 },
{ default(short), 200, 50, 49, 1, 49, 49 },
{ default(short), 200, 50, 1, 1, 1, 1 },
{ default(byte), 1000, 512, 2047, 4, 512, 511 }
};
[Theory]
[MemberData(nameof(AllocateData))]
public void CreatesBlocksOfCorrectSizes<T>(
public void BufferSizesAreCorrect<T>(
T dummy,
int blockCapacity,
int blockAlignment,
int bufferCapacity,
int bufferAlignment,
long totalLength,
int expectedNumberOfBlocks,
int expectedBlockSize,
int expectedSizeOfLastBlock)
int expectedNumberOfBuffers,
int expectedBufferSize,
int expectedSizeOfLastBuffer)
where T : struct
{
this.memoryAllocator.BlockCapacity = blockCapacity;
this.memoryAllocator.BufferCapacity = bufferCapacity;
// Act:
using var g = MemoryGroup<T>.Allocate(this.memoryAllocator, totalLength, blockAlignment);
using var g = MemoryGroup<T>.Allocate(this.memoryAllocator, totalLength, bufferAlignment);
// Assert:
Assert.Equal(expectedNumberOfBlocks, g.Count);
Assert.Equal(expectedBlockSize, g.BlockSize);
Assert.Equal(expectedNumberOfBuffers, g.Count);
Assert.Equal(expectedBufferSize, g.BufferSize);
if (g.Count == 0)
{
return;
@ -65,29 +71,29 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
for (int i = 0; i < g.Count - 1; i++)
{
Assert.Equal(g[i].Length, expectedBlockSize);
Assert.Equal(g[i].Length, expectedBufferSize);
}
Assert.Equal(g.Last().Length, expectedSizeOfLastBlock);
Assert.Equal(g.Last().Length, expectedSizeOfLastBuffer);
}
[Fact]
public void WhenBlockAlignmentIsOverCapacity_Throws_InvalidMemoryOperationException()
{
this.memoryAllocator.BlockCapacity = 42;
this.memoryAllocator.BufferCapacity = 84; // 42 * Int16
Assert.Throws<InvalidMemoryOperationException>(() =>
{
MemoryGroup<byte>.Allocate(this.memoryAllocator, 50, 43);
MemoryGroup<short>.Allocate(this.memoryAllocator, 50, 43);
});
}
[Theory]
[InlineData(AllocationOptions.None)]
[InlineData(AllocationOptions.Clean)]
public void MemoryAllocator_IsUtilizedCorrectly(AllocationOptions allocationOptions)
public void MemoryAllocatorIsUtilizedCorrectly(AllocationOptions allocationOptions)
{
this.memoryAllocator.BlockCapacity = 200;
this.memoryAllocator.BufferCapacity = 200;
HashSet<int> bufferHashes;
@ -113,15 +119,33 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
}
}
[Fact]
public void IsValid_TrueAfterCreation()
{
using var g = MemoryGroup<byte>.Allocate(this.memoryAllocator, 10, 100);
Assert.True(g.IsValid);
}
[Fact]
public void IsValid_FalseAfterDisposal()
{
using var g = MemoryGroup<byte>.Allocate(this.memoryAllocator, 10, 100);
g.Dispose();
Assert.False(g.IsValid);
}
[StructLayout(LayoutKind.Sequential, Size = 5)]
private struct S5
{
public override string ToString() => "S5";
}
[StructLayout(LayoutKind.Sequential, Size = 4)]
private struct S4
{
public override string ToString() => "S4";
}
}
}

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

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

Loading…
Cancel
Save