Browse Source

GetBoundedSlice, CopyTo/From span

af/octree-no-pixelmap
Anton Firszov 6 years ago
parent
commit
eb8ea992f5
  1. 70
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
  2. 111
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs
  3. 81
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs

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

@ -7,6 +7,76 @@ namespace SixLabors.ImageSharp.Memory
{ {
internal static class MemoryGroupExtensions internal static class MemoryGroupExtensions
{ {
/// <summary>
/// Returns a slice that is expected to be within the bounds of a single buffer.
/// Otherwise <see cref="ArgumentOutOfRangeException"/> is thrown.
/// </summary>
public static Memory<T> GetBoundedSlice<T>(this IMemoryGroup<T> group, long start, int length)
where T : struct
{
Guard.NotNull(group, nameof(group));
Guard.IsTrue(group.IsValid, nameof(group), "Group must be valid!");
Guard.MustBeGreaterThanOrEqualTo(length, 0, nameof(length));
Guard.MustBeLessThan(start, group.TotalLength, nameof(start));
int bufferIdx = (int)(start / group.BufferLength);
if (bufferIdx >= group.Count)
{
throw new ArgumentOutOfRangeException(nameof(start));
}
int bufferStart = (int)(start % group.BufferLength);
int bufferEnd = bufferStart + length;
Memory<T> memory = group[bufferIdx];
if (bufferEnd > memory.Length)
{
throw new ArgumentOutOfRangeException(nameof(length));
}
return memory.Slice(bufferStart, length);
}
public static void CopyTo<T>(this IMemoryGroup<T> source, Span<T> target)
where T : struct
{
Guard.NotNull(source, nameof(source));
Guard.MustBeGreaterThanOrEqualTo(target.Length, source.TotalLength, nameof(target));
var cur = new MemoryGroupCursor<T>(source);
long position = 0;
while (position < source.TotalLength)
{
int fwd = Math.Min(cur.LookAhead(), target.Length);
cur.GetSpan(fwd).CopyTo(target);
cur.Forward(fwd);
target = target.Slice(fwd);
position += fwd;
}
}
public static void CopyTo<T>(this Span<T> source, IMemoryGroup<T> target)
where T : struct
=> CopyTo((ReadOnlySpan<T>)source, target);
public static void CopyTo<T>(this ReadOnlySpan<T> source, IMemoryGroup<T> target)
where T : struct
{
Guard.NotNull(target, nameof(target));
Guard.MustBeGreaterThanOrEqualTo(target.TotalLength, source.Length, nameof(target));
var cur = new MemoryGroupCursor<T>(target);
while (!source.IsEmpty)
{
int fwd = Math.Min(cur.LookAhead(), source.Length);
source.Slice(0, fwd).CopyTo(cur.GetSpan(fwd));
cur.Forward(fwd);
source = source.Slice(fwd);
}
}
public static void CopyTo<T>(this IMemoryGroup<T> source, IMemoryGroup<T> target) public static void CopyTo<T>(this IMemoryGroup<T> source, IMemoryGroup<T> target)
where T : struct where T : struct
{ {

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

@ -0,0 +1,111 @@
// 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 CopyTo : MemoryGroupTestsBase
{
public static readonly TheoryData<int, int, int, int> WhenSourceBufferIsShorterOrEqual_Data =
CopyAndTransformData;
[Theory]
[MemberData(nameof(WhenSourceBufferIsShorterOrEqual_Data))]
public void WhenSourceBufferIsShorterOrEqual(int srcTotal, int srcBufLen, int trgTotal, int trgBufLen)
{
using MemoryGroup<int> src = this.CreateTestGroup(srcTotal, srcBufLen, true);
using MemoryGroup<int> trg = this.CreateTestGroup(trgTotal, trgBufLen, false);
src.CopyTo(trg);
int pos = 0;
MemoryGroupIndex i = src.MinIndex();
MemoryGroupIndex j = trg.MinIndex();
for (; i < src.MaxIndex(); i += 1, j += 1, pos++)
{
int a = src.GetElementAt(i);
int b = trg.GetElementAt(j);
Assert.True(a == b, $"Mismatch @ {pos} Expected: {a} Actual: {b}");
}
}
[Fact]
public void WhenTargetBufferTooShort_Throws()
{
using MemoryGroup<int> src = this.CreateTestGroup(10, 20, true);
using MemoryGroup<int> trg = this.CreateTestGroup(5, 20, false);
Assert.Throws<ArgumentOutOfRangeException>(() => src.CopyTo(trg));
}
[Theory]
[InlineData(30, 10, 40)]
[InlineData(42, 23, 42)]
[InlineData(1, 3, 10)]
[InlineData(0, 4, 0)]
public void GroupToSpan_Success(long totalLength, int bufferLength, int spanLength)
{
using MemoryGroup<int> src = this.CreateTestGroup(totalLength, bufferLength, true);
var trg = new int[spanLength];
src.CopyTo(trg);
int expected = 1;
foreach (int val in trg.AsSpan().Slice(0, (int)totalLength))
{
Assert.Equal(expected, val);
expected++;
}
}
[Theory]
[InlineData(20, 7, 19)]
[InlineData(2, 1, 1)]
public void GroupToSpan_OutOfRange(long totalLength, int bufferLength, int spanLength)
{
using MemoryGroup<int> src = this.CreateTestGroup(totalLength, bufferLength, true);
var trg = new int[spanLength];
Assert.ThrowsAny<ArgumentOutOfRangeException>(() => src.CopyTo(trg));
}
[Theory]
[InlineData(30, 35, 10)]
[InlineData(42, 23, 42)]
[InlineData(10, 3, 1)]
[InlineData(0, 3, 0)]
public void SpanToGroup_Success(long totalLength, int bufferLength, int spanLength)
{
var src = new int[spanLength];
for (int i = 0; i < src.Length; i++)
{
src[i] = i + 1;
}
using MemoryGroup<int> trg = this.CreateTestGroup(totalLength, bufferLength);
src.AsSpan().CopyTo(trg);
int position = 0;
for (MemoryGroupIndex i = trg.MinIndex(); position < spanLength; i += 1, position++)
{
int expected = position + 1;
Assert.Equal(expected, trg.GetElementAt(i));
}
}
[Theory]
[InlineData(10, 3, 11)]
[InlineData(0, 3, 1)]
public void SpanToGroup_OutOfRange(long totalLength, int bufferLength, int spanLength)
{
var src = new int[spanLength];
using MemoryGroup<int> trg = this.CreateTestGroup(totalLength, bufferLength, true);
Assert.ThrowsAny<ArgumentOutOfRangeException>(() => src.AsSpan().CopyTo(trg));
}
}
}
}

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

@ -44,42 +44,6 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
{ 30, 5, 40, 12 }, { 30, 5, 40, 12 },
}; };
public class CopyTo : MemoryGroupTestsBase
{
public static readonly TheoryData<int, int, int, int> WhenSourceBufferIsShorterOrEqual_Data =
CopyAndTransformData;
[Theory]
[MemberData(nameof(WhenSourceBufferIsShorterOrEqual_Data))]
public void WhenSourceBufferIsShorterOrEqual(int srcTotal, int srcBufLen, int trgTotal, int trgBufLen)
{
using MemoryGroup<int> src = this.CreateTestGroup(srcTotal, srcBufLen, true);
using MemoryGroup<int> trg = this.CreateTestGroup(trgTotal, trgBufLen, false);
src.CopyTo(trg);
int pos = 0;
MemoryGroupIndex i = src.MinIndex();
MemoryGroupIndex j = trg.MinIndex();
for (; i < src.MaxIndex(); i += 1, j += 1, pos++)
{
int a = src.GetElementAt(i);
int b = trg.GetElementAt(j);
Assert.True(a == b, $"Mismatch @ {pos} Expected: {a} Actual: {b}");
}
}
[Fact]
public void WhenTargetBufferTooShort_Throws()
{
using MemoryGroup<int> src = this.CreateTestGroup(10, 20, true);
using MemoryGroup<int> trg = this.CreateTestGroup(5, 20, false);
Assert.Throws<ArgumentOutOfRangeException>(() => src.CopyTo(trg));
}
}
public class TransformTo : MemoryGroupTestsBase public class TransformTo : MemoryGroupTestsBase
{ {
public static readonly TheoryData<int, int, int, int> WhenSourceBufferIsShorterOrEqual_Data = public static readonly TheoryData<int, int, int, int> WhenSourceBufferIsShorterOrEqual_Data =
@ -154,6 +118,51 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
Assert.True(group[2].Span.SequenceEqual(data2)); Assert.True(group[2].Span.SequenceEqual(data2));
} }
public static TheoryData<long, int, long, int> GetBoundedSlice_SuccessData = new TheoryData<long, int, long, int>()
{
{ 300, 100, 110, 80 },
{ 300, 100, 100, 100 },
{ 280, 100, 201, 79 },
{ 42, 7, 0, 0 },
{ 42, 7, 0, 1 },
{ 42, 7, 0, 7 },
{ 42, 9, 9, 9 },
};
[Theory]
[MemberData(nameof(GetBoundedSlice_SuccessData))]
public void GetBoundedSlice_WhenArgsAreCorrect(long totalLength, int bufferLength, long start, int length)
{
using MemoryGroup<int> group = this.CreateTestGroup(totalLength, bufferLength, true);
Memory<int> slice = group.GetBoundedSlice(start, length);
Assert.Equal(length, slice.Length);
int expected = (int)start + 1;
foreach (int val in slice.Span)
{
Assert.Equal(expected, val);
expected++;
}
}
public static TheoryData<long, int, long, int> GetBoundedSlice_ErrorData = new TheoryData<long, int, long, int>()
{
{ 300, 100, 110, 91 },
{ 42, 7, 0, 8 },
{ 42, 7, 1, 7 },
{ 42, 7, 1, 30 },
};
[Theory]
[MemberData(nameof(GetBoundedSlice_ErrorData))]
public void GetBoundedSlice_WhenOverlapsBuffers_Throws(long totalLength, int bufferLength, long start, int length)
{
using MemoryGroup<int> group = this.CreateTestGroup(totalLength, bufferLength, true);
Assert.ThrowsAny<ArgumentOutOfRangeException>(() => group.GetBoundedSlice(start, length));
}
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);

Loading…
Cancel
Save