Browse Source

SwapOrCopyContent() works

af/octree-no-pixelmap
Anton Firszov 6 years ago
parent
commit
52f5f05243
  1. 84
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupView{T}.cs
  2. 3
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Consumed.cs
  3. 39
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.Owned.cs
  4. 66
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroup{T}.cs
  5. 29
      src/ImageSharp/Memory/MemoryAllocatorExtensions.cs
  6. 9
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupIndex.cs
  7. 1
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.Allocate.cs
  8. 105
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.SwapOrCopyContent.cs
  9. 84
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.View.cs
  10. 24
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs
  11. 3
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTestsBase.cs

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

@ -21,12 +21,11 @@ namespace SixLabors.ImageSharp.Memory
internal class MemoryGroupView<T> : IMemoryGroup<T> internal class MemoryGroupView<T> : IMemoryGroup<T>
where T : struct where T : struct
{ {
private readonly Memory.MemoryGroup<T> owner; private MemoryGroup<T> owner;
private readonly MemoryOwnerWrapper[] memoryWrappers; private readonly MemoryOwnerWrapper[] memoryWrappers;
public MemoryGroupView(Memory.MemoryGroup<T> owner) public MemoryGroupView(MemoryGroup<T> owner)
{ {
this.IsValid = true;
this.owner = owner; this.owner = owner;
this.memoryWrappers = new MemoryOwnerWrapper[owner.Count]; this.memoryWrappers = new MemoryOwnerWrapper[owner.Count];
@ -36,20 +35,68 @@ namespace SixLabors.ImageSharp.Memory
} }
} }
public int Count => this.owner.Count; public int Count
{
get
{
this.EnsureIsValid();
return this.owner.Count;
}
}
public int BufferLength => this.owner.BufferLength; public int BufferLength
{
get
{
this.EnsureIsValid();
return this.owner.BufferLength;
}
}
public long TotalLength => this.owner.TotalLength; public long TotalLength
{
get
{
this.EnsureIsValid();
return this.owner.TotalLength;
}
}
public bool IsValid { get; internal set; } public bool IsValid => this.owner != null;
public Memory<T> this[int index] => throw new NotImplementedException(); public Memory<T> this[int index]
{
get
{
this.EnsureIsValid();
return this.memoryWrappers[index].Memory;
}
}
public IEnumerator<Memory<T>> GetEnumerator() => throw new NotImplementedException(); public IEnumerator<Memory<T>> GetEnumerator()
{
this.EnsureIsValid();
for (int i = 0; i < this.Count; i++)
{
yield return this.memoryWrappers[i].Memory;
}
}
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
internal void Invalidate()
{
this.owner = null;
}
private void EnsureIsValid()
{
if (!this.IsValid)
{
throw new InvalidMemoryOperationException("Can not access an invalidated MemoryGroupView!");
}
}
private class MemoryOwnerWrapper : MemoryManager<T> private class MemoryOwnerWrapper : MemoryManager<T>
{ {
private readonly MemoryGroupView<T> view; private readonly MemoryGroupView<T> view;
@ -68,17 +115,20 @@ namespace SixLabors.ImageSharp.Memory
public override Span<T> GetSpan() public override Span<T> GetSpan()
{ {
if (!this.view.IsValid) this.view.EnsureIsValid();
{ return this.view.owner[this.index].Span;
throw new InvalidOperationException();
}
return this.view[this.index].Span;
} }
public override MemoryHandle Pin(int elementIndex = 0) => throw new NotImplementedException(); public override MemoryHandle Pin(int elementIndex = 0)
{
this.view.EnsureIsValid();
return this.view.owner[this.index].Pin();
}
public override void Unpin() => throw new NotImplementedException(); public override void Unpin()
{
throw new NotSupportedException();
}
} }
} }
} }

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

@ -17,6 +17,7 @@ namespace SixLabors.ImageSharp.Memory
: base(bufferLength, totalLength) : base(bufferLength, totalLength)
{ {
this.source = source; this.source = source;
this.View = new MemoryGroupView<T>(this);
} }
public override int Count => this.source.Length; public override int Count => this.source.Length;
@ -33,7 +34,7 @@ namespace SixLabors.ImageSharp.Memory
public override void Dispose() public override void Dispose()
{ {
// No ownership nothing to dispose this.View.Invalidate();
} }
} }
} }

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

@ -19,15 +19,9 @@ namespace SixLabors.ImageSharp.Memory
: base(bufferLength, totalLength) : base(bufferLength, totalLength)
{ {
this.memoryOwners = memoryOwners; this.memoryOwners = memoryOwners;
this.View = new MemoryGroupView<T>(this);
} }
public override IEnumerator<Memory<T>> GetEnumerator()
{
this.EnsureNotDisposed();
return this.memoryOwners.Select(mo => mo.Memory).GetEnumerator();
}
public override int Count public override int Count
{ {
get get
@ -46,6 +40,12 @@ namespace SixLabors.ImageSharp.Memory
} }
} }
public override IEnumerator<Memory<T>> GetEnumerator()
{
this.EnsureNotDisposed();
return this.memoryOwners.Select(mo => mo.Memory).GetEnumerator();
}
public override void Dispose() public override void Dispose()
{ {
if (this.memoryOwners == null) if (this.memoryOwners == null)
@ -53,6 +53,8 @@ namespace SixLabors.ImageSharp.Memory
return; return;
} }
this.View.Invalidate();
foreach (IMemoryOwner<T> memoryOwner in this.memoryOwners) foreach (IMemoryOwner<T> memoryOwner in this.memoryOwners)
{ {
memoryOwner.Dispose(); memoryOwner.Dispose();
@ -69,6 +71,29 @@ namespace SixLabors.ImageSharp.Memory
throw new ObjectDisposedException(nameof(MemoryGroup<T>)); throw new ObjectDisposedException(nameof(MemoryGroup<T>));
} }
} }
internal static void SwapContents(Owned a, Owned b)
{
a.EnsureNotDisposed();
b.EnsureNotDisposed();
IMemoryOwner<T>[] tempOwners = a.memoryOwners;
long tempTotalLength = a.TotalLength;
int tempBufferLength = a.BufferLength;
a.memoryOwners = b.memoryOwners;
a.TotalLength = b.TotalLength;
a.BufferLength = b.BufferLength;
b.memoryOwners = tempOwners;
b.TotalLength = tempTotalLength;
b.BufferLength = tempBufferLength;
a.View.Invalidate();
b.View.Invalidate();
a.View = new MemoryGroupView<T>(a);
b.View = new MemoryGroupView<T>(b);
}
} }
} }
} }

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

@ -26,41 +26,60 @@ namespace SixLabors.ImageSharp.Memory
this.TotalLength = totalLength; this.TotalLength = totalLength;
} }
/// <inheritdoc />
public abstract int Count { get; } public abstract int Count { get; }
public int BufferLength { get; } /// <inheritdoc />
public int BufferLength { get; private set; }
public long TotalLength { get; } /// <inheritdoc />
public long TotalLength { get; private set; }
/// <inheritdoc />
public bool IsValid { get; private set; } = true; public bool IsValid { get; private set; } = true;
public MemoryGroupView<T> View { get; private set; }
/// <inheritdoc />
public abstract Memory<T> this[int index] { get; } public abstract Memory<T> this[int index] { get; }
/// <inheritdoc />
public abstract void Dispose(); public abstract void Dispose();
/// <inheritdoc />
public abstract IEnumerator<Memory<T>> GetEnumerator(); public abstract IEnumerator<Memory<T>> GetEnumerator();
/// <inheritdoc />
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
// bufferLengthAlignment == image.Width in row-major images /// <summary>
/// Creates a new memory group, allocating it's buffers with the provided allocator.
/// </summary>
/// <param name="allocator">The <see cref="MemoryAllocator"/> to use.</param>
/// <param name="totalLength">The total length of the buffer.</param>
/// <param name="bufferAlignment">The expected alignment (eg. to make sure image rows fit into single buffers).</param>
/// <param name="options">The <see cref="AllocationOptions"/>.</param>
/// <returns>A new <see cref="MemoryGroup{T}"/>.</returns>
/// <exception cref="InvalidMemoryOperationException">Thrown when 'blockAlignment' converted to bytes is greater than the buffer capacity of the allocator.</exception>
public static MemoryGroup<T> Allocate( public static MemoryGroup<T> Allocate(
MemoryAllocator allocator, MemoryAllocator allocator,
long totalLength, long totalLength,
int blockAlignment, int bufferAlignment,
AllocationOptions allocationOptions = AllocationOptions.None) AllocationOptions options = AllocationOptions.None)
{ {
Guard.NotNull(allocator, nameof(allocator)); Guard.NotNull(allocator, nameof(allocator));
Guard.MustBeGreaterThanOrEqualTo(totalLength, 0, nameof(totalLength)); Guard.MustBeGreaterThanOrEqualTo(totalLength, 0, nameof(totalLength));
Guard.MustBeGreaterThan(blockAlignment, 0, nameof(blockAlignment)); Guard.MustBeGreaterThan(bufferAlignment, 0, nameof(bufferAlignment));
int blockCapacityInElements = allocator.GetBufferCapacityInBytes() / ElementSize; int blockCapacityInElements = allocator.GetBufferCapacityInBytes() / ElementSize;
if (blockAlignment > blockCapacityInElements) if (bufferAlignment > blockCapacityInElements)
{ {
throw new InvalidMemoryOperationException(); throw new InvalidMemoryOperationException(
$"The buffer capacity of the provided MemoryAllocator is insufficient for the requested buffer alignment: {bufferAlignment}.");
} }
int numberOfAlignedSegments = blockCapacityInElements / blockAlignment; int numberOfAlignedSegments = blockCapacityInElements / bufferAlignment;
int bufferLength = numberOfAlignedSegments * blockAlignment; int bufferLength = numberOfAlignedSegments * bufferAlignment;
if (totalLength > 0 && totalLength < bufferLength) if (totalLength > 0 && totalLength < bufferLength)
{ {
bufferLength = (int)totalLength; bufferLength = (int)totalLength;
@ -81,12 +100,12 @@ namespace SixLabors.ImageSharp.Memory
var buffers = new IMemoryOwner<T>[bufferCount]; var buffers = new IMemoryOwner<T>[bufferCount];
for (int i = 0; i < buffers.Length - 1; i++) for (int i = 0; i < buffers.Length - 1; i++)
{ {
buffers[i] = allocator.Allocate<T>(bufferLength, allocationOptions); buffers[i] = allocator.Allocate<T>(bufferLength, options);
} }
if (bufferCount > 0) if (bufferCount > 0)
{ {
buffers[^1] = allocator.Allocate<T>(sizeOfLastBuffer, allocationOptions); buffers[^1] = allocator.Allocate<T>(sizeOfLastBuffer, options);
} }
return new Owned(buffers, bufferLength, totalLength); return new Owned(buffers, bufferLength, totalLength);
@ -115,10 +134,27 @@ namespace SixLabors.ImageSharp.Memory
return new Consumed(source, bufferLength, totalLength); return new Consumed(source, bufferLength, totalLength);
} }
// Analogous to current MemorySource.SwapOrCopyContent() /// <summary>
public static void SwapOrCopyContent(MemoryGroup<T> destination, MemoryGroup<T> source) /// Swaps the contents of 'target' with 'source' if the buffers are allocated (1),
/// copies the contents of 'source' to 'target' otherwise (2).
/// Groups should be of same TotalLength in case 2.
/// </summary>
public static void SwapOrCopyContent(MemoryGroup<T> target, MemoryGroup<T> source)
{ {
throw new NotImplementedException(); if (source is Owned ownedSrc && target is Owned ownedTarget)
{
Owned.SwapContents(ownedTarget, ownedSrc);
}
else
{
if (target.TotalLength != source.TotalLength)
{
throw new InvalidMemoryOperationException(
"Trying to copy/swap incompatible buffers. This is most likely caused by applying an unsupported processor to wrapped-memory images.");
}
source.CopyTo(target);
}
} }
} }
} }

29
src/ImageSharp/Memory/MemoryAllocatorExtensions.cs

@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Memory
/// <typeparam name="T">The type of buffer items to allocate.</typeparam> /// <typeparam name="T">The type of buffer items to allocate.</typeparam>
/// <param name="memoryAllocator">The memory allocator.</param> /// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="width">The buffer width.</param> /// <param name="width">The buffer width.</param>
/// <param name="height">The buffer heght.</param> /// <param name="height">The buffer height.</param>
/// <param name="options">The allocation options.</param> /// <param name="options">The allocation options.</param>
/// <returns>The <see cref="Buffer2D{T}"/>.</returns> /// <returns>The <see cref="Buffer2D{T}"/>.</returns>
public static Buffer2D<T> Allocate2D<T>( public static Buffer2D<T> Allocate2D<T>(
@ -50,13 +50,13 @@ namespace SixLabors.ImageSharp.Memory
Allocate2D<T>(memoryAllocator, size.Width, size.Height, options); Allocate2D<T>(memoryAllocator, size.Width, size.Height, options);
/// <summary> /// <summary>
/// Allocates padded buffers for BMP encoder/decoder. (Replacing old PixelRow/PixelArea) /// Allocates padded buffers for BMP encoder/decoder. (Replacing old PixelRow/PixelArea).
/// </summary> /// </summary>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/></param> /// <param name="memoryAllocator">The <see cref="MemoryAllocator"/>.</param>
/// <param name="width">Pixel count in the row</param> /// <param name="width">Pixel count in the row</param>
/// <param name="pixelSizeInBytes">The pixel size in bytes, eg. 3 for RGB</param> /// <param name="pixelSizeInBytes">The pixel size in bytes, eg. 3 for RGB.</param>
/// <param name="paddingInBytes">The padding</param> /// <param name="paddingInBytes">The padding.</param>
/// <returns>A <see cref="IManagedByteBuffer"/></returns> /// <returns>A <see cref="IManagedByteBuffer"/>.</returns>
internal static IManagedByteBuffer AllocatePaddedPixelRowBuffer( internal static IManagedByteBuffer AllocatePaddedPixelRowBuffer(
this MemoryAllocator memoryAllocator, this MemoryAllocator memoryAllocator,
int width, int width,
@ -66,5 +66,22 @@ namespace SixLabors.ImageSharp.Memory
int length = (width * pixelSizeInBytes) + paddingInBytes; int length = (width * pixelSizeInBytes) + paddingInBytes;
return memoryAllocator.AllocateManagedByteBuffer(length); return memoryAllocator.AllocateManagedByteBuffer(length);
} }
/// <summary>
/// Allocates a <see cref="MemoryGroup{T}"/>.
/// </summary>
/// <param name="memoryAllocator">The <see cref="MemoryAllocator"/> to use.</param>
/// <param name="totalLength">The total length of the buffer.</param>
/// <param name="bufferAlignment">The expected alignment (eg. to make sure image rows fit into single buffers).</param>
/// <param name="options">The <see cref="AllocationOptions"/>.</param>
/// <returns>A new <see cref="MemoryGroup{T}"/>.</returns>
/// <exception cref="InvalidMemoryOperationException">Thrown when 'blockAlignment' converted to bytes is greater than the buffer capacity of the allocator.</exception>
internal static MemoryGroup<T> AllocateGroup<T>(
this MemoryAllocator memoryAllocator,
long totalLength,
int bufferAlignment,
AllocationOptions options = AllocationOptions.None)
where T : struct
=> MemoryGroup<T>.Allocate(memoryAllocator, totalLength, bufferAlignment, options);
} }
} }

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

@ -112,12 +112,9 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
public static MemoryGroupIndex MaxIndex<T>(this MemoryGroup<T> group) public static MemoryGroupIndex MaxIndex<T>(this MemoryGroup<T> group)
where T : struct where T : struct
{ {
if (group.Count == 0) return group.Count == 0
{ ? new MemoryGroupIndex(group.BufferLength, 0, 0)
return new MemoryGroupIndex(group.BufferLength, 0, 0); : new MemoryGroupIndex(group.BufferLength, group.Count - 1, group[^1].Length);
}
return new MemoryGroupIndex(group.BufferLength, group.Count - 1, group[^1].Length - 1);
} }
} }
} }

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

@ -1,6 +1,7 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;

105
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.SwapOrCopyContent.cs

@ -0,0 +1,105 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{
public partial class MemoryGroupTests
{
public class SwapOrCopyContent : MemoryGroupTestsBase
{
[Fact]
public void WhenBothAreMemoryOwners_ShouldSwap()
{
this.MemoryAllocator.BufferCapacityInBytes = sizeof(int) * 50;
using MemoryGroup<int> a = this.MemoryAllocator.AllocateGroup<int>(100, 50);
using MemoryGroup<int> b = this.MemoryAllocator.AllocateGroup<int>(120, 50);
Memory<int> a0 = a[0];
Memory<int> a1 = a[1];
Memory<int> b0 = b[0];
Memory<int> b1 = b[1];
MemoryGroup<int>.SwapOrCopyContent(a, b);
Assert.Equal(b0, a[0]);
Assert.Equal(b1, a[1]);
Assert.Equal(a0, b[0]);
Assert.Equal(a1, b[1]);
Assert.NotEqual(a[0], b[0]);
}
[Fact]
public void WhenBothAreMemoryOwners_ShouldReplaceViews()
{
using MemoryGroup<int> a = this.MemoryAllocator.AllocateGroup<int>(100, 100);
using MemoryGroup<int> b = this.MemoryAllocator.AllocateGroup<int>(120, 100);
a[0].Span[42] = 1;
b[0].Span[33] = 2;
MemoryGroupView<int> aView0 = a.View;
MemoryGroupView<int> bView0 = b.View;
MemoryGroup<int>.SwapOrCopyContent(a, b);
Assert.False(aView0.IsValid);
Assert.False(bView0.IsValid);
Assert.ThrowsAny<InvalidOperationException>(() => _ = aView0[0].Span);
Assert.ThrowsAny<InvalidOperationException>(() => _ = bView0[0].Span);
Assert.True(a.View.IsValid);
Assert.True(b.View.IsValid);
Assert.Equal(2, a.View[0].Span[33]);
Assert.Equal(1, b.View[0].Span[42]);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void WhenDestIsNotAllocated_SameSize_ShouldCopy(bool sourceIsAllocated)
{
var data = new Rgba32[21];
var color = new Rgba32(1, 2, 3, 4);
using var destOwner = new TestMemoryManager<Rgba32>(data);
using var dest = MemoryGroup<Rgba32>.Wrap(destOwner.Memory);
using MemoryGroup<Rgba32> source = this.MemoryAllocator.AllocateGroup<Rgba32>(21, 30);
source[0].Span[10] = color;
// Act:
MemoryGroup<Rgba32>.SwapOrCopyContent(dest, source);
// Assert:
Assert.Equal(color, dest[0].Span[10]);
Assert.NotEqual(source[0], dest[0]);
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void WhenDestIsNotMemoryOwner_DifferentSize_Throws(bool sourceIsOwner)
{
var data = new Rgba32[21];
var color = new Rgba32(1, 2, 3, 4);
using var destOwner = new TestMemoryManager<Rgba32>(data);
var dest = MemoryGroup<Rgba32>.Wrap(destOwner.Memory);
using MemoryGroup<Rgba32> source = this.MemoryAllocator.AllocateGroup<Rgba32>(22, 30);
source[0].Span[10] = color;
// Act:
Assert.ThrowsAny<InvalidOperationException>(() => MemoryGroup<Rgba32>.SwapOrCopyContent(dest, source));
Assert.Equal(color, source[0].Span[10]);
Assert.NotEqual(color, dest[0].Span[10]);
}
}
}
}

84
tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.View.cs

@ -0,0 +1,84 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using SixLabors.ImageSharp.Memory;
using Xunit;
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{
public partial class MemoryGroupTests
{
public class View : MemoryGroupTestsBase
{
[Fact]
public void RefersToOwnerGroupContent()
{
using MemoryGroup<int> group = this.CreateTestGroup(240, 80, true);
MemoryGroupView<int> view = group.View;
Assert.True(view.IsValid);
Assert.Equal(group.Count, view.Count);
Assert.Equal(group.BufferLength, view.BufferLength);
Assert.Equal(group.TotalLength, view.TotalLength);
int cnt = 1;
foreach (Memory<int> memory in view)
{
Span<int> span = memory.Span;
foreach (int t in span)
{
Assert.Equal(cnt, t);
cnt++;
}
}
}
[Fact]
public void IsInvalidatedOnOwnerGroupDispose()
{
MemoryGroupView<int> view;
using (MemoryGroup<int> group = this.CreateTestGroup(240, 80, true))
{
view = group.View;
}
Assert.False(view.IsValid);
Assert.ThrowsAny<InvalidMemoryOperationException>(() =>
{
_ = view.Count;
});
Assert.ThrowsAny<InvalidMemoryOperationException>(() =>
{
_ = view.BufferLength;
});
Assert.ThrowsAny<InvalidMemoryOperationException>(() =>
{
_ = view.TotalLength;
});
Assert.ThrowsAny<InvalidMemoryOperationException>(() =>
{
_ = view[0];
});
}
[Fact]
public void WhenInvalid_CanNotUseMemberMemory()
{
Memory<int> memory;
using (MemoryGroup<int> group = this.CreateTestGroup(240, 80, true))
{
memory = group.View[0];
}
Assert.ThrowsAny<InvalidMemoryOperationException>(() =>
{
_ = memory.Span;
});
}
}
}
}

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

@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using Xunit; using Xunit;
@ -103,8 +104,6 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
Assert.True(b == 2 * a, $"Mismatch @ {pos} Expected: {a} Actual: {b}"); Assert.True(b == 2 * a, $"Mismatch @ {pos} Expected: {a} Actual: {b}");
} }
} }
[Fact] [Fact]
@ -117,7 +116,6 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
} }
} }
[Theory] [Theory]
[InlineData(100, 5)] [InlineData(100, 5)]
[InlineData(100, 101)] [InlineData(100, 101)]
@ -136,6 +134,26 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
} }
} }
[Fact]
public void Wrap()
{
int[] data0 = { 1, 2, 3, 4 };
int[] data1 = { 5, 6, 7, 8 };
int[] data2 = { 9, 10 };
using var mgr0 = new TestMemoryManager<int>(data0);
using var mgr1 = new TestMemoryManager<int>(data1);
using var group = MemoryGroup<int>.Wrap(mgr0.Memory, mgr1.Memory, data2);
Assert.Equal(3, group.Count);
Assert.Equal(4, group.BufferLength);
Assert.Equal(10, group.TotalLength);
Assert.True(group[0].Span.SequenceEqual(data0));
Assert.True(group[1].Span.SequenceEqual(data1));
Assert.True(group[2].Span.SequenceEqual(data2));
}
private static void MultiplyAllBy2(ReadOnlySpan<int> source, Span<int> target) private static void MultiplyAllBy2(ReadOnlySpan<int> source, Span<int> target)
{ {
Assert.Equal(source.Length, target.Length); Assert.Equal(source.Length, target.Length);

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

@ -9,6 +9,9 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{ {
internal readonly TestMemoryAllocator MemoryAllocator = new TestMemoryAllocator(); internal readonly TestMemoryAllocator MemoryAllocator = new TestMemoryAllocator();
/// <summary>
/// Create a group, either uninitialized or filled with incrementing numbers starting with 1.
/// </summary>
internal MemoryGroup<int> CreateTestGroup(long totalLength, int bufferLength, bool fillSequence = false) internal MemoryGroup<int> CreateTestGroup(long totalLength, int bufferLength, bool fillSequence = false)
{ {
this.MemoryAllocator.BufferCapacityInBytes = bufferLength * sizeof(int); this.MemoryAllocator.BufferCapacityInBytes = bufferLength * sizeof(int);

Loading…
Cancel
Save