From 7edc41814111e5f8d023f06200545edff96556d7 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 3 Feb 2020 00:31:12 +0100 Subject: [PATCH] Implemented: CopyTo, TransformTo, TransformInplace --- .../MemoryGroupExtensions.cs | 46 +++++++ .../Memory/TransformItemsDelegate{T}.cs | 9 ++ .../Memory/TransformItemsInplaceDelegate.cs | 9 ++ .../MemoryGroupTests.CopyTo.cs | 50 -------- .../DiscontiguousBuffers/MemoryGroupTests.cs | 119 ++++++++++++++++++ 5 files changed, 183 insertions(+), 50 deletions(-) create mode 100644 src/ImageSharp/Memory/TransformItemsDelegate{T}.cs create mode 100644 src/ImageSharp/Memory/TransformItemsInplaceDelegate.cs delete mode 100644 tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs index 68a1f2e80..14e676dbe 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs @@ -38,6 +38,52 @@ namespace SixLabors.ImageSharp.Memory } } + public static void TransformTo( + this IMemoryGroup source, + IMemoryGroup target, + TransformItemsDelegate transform) + where T : struct + { + Guard.NotNull(source, nameof(source)); + Guard.NotNull(target, nameof(target)); + Guard.NotNull(transform, nameof(transform)); + 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(source); + var trgCur = new MemoryGroupCursor(target); + + while (position < source.TotalLength) + { + int fwd = Math.Min(srcCur.LookAhead(), trgCur.LookAhead()); + Span srcSpan = srcCur.GetSpan(fwd); + Span trgSpan = trgCur.GetSpan(fwd); + transform(srcSpan, trgSpan); + + srcCur.Forward(fwd); + trgCur.Forward(fwd); + position += fwd; + } + } + + public static void TransformInplace( + this IMemoryGroup memoryGroup, + TransformItemsInplaceDelegate transform) + where T : struct + { + foreach (Memory memory in memoryGroup) + { + transform(memory.Span); + } + } + public static bool IsEmpty(this IMemoryGroup group) where T : struct => group.Count == 0; diff --git a/src/ImageSharp/Memory/TransformItemsDelegate{T}.cs b/src/ImageSharp/Memory/TransformItemsDelegate{T}.cs new file mode 100644 index 000000000..898d704f0 --- /dev/null +++ b/src/ImageSharp/Memory/TransformItemsDelegate{T}.cs @@ -0,0 +1,9 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Memory +{ + internal delegate void TransformItemsDelegate(ReadOnlySpan source, Span target); +} diff --git a/src/ImageSharp/Memory/TransformItemsInplaceDelegate.cs b/src/ImageSharp/Memory/TransformItemsInplaceDelegate.cs new file mode 100644 index 000000000..023606f52 --- /dev/null +++ b/src/ImageSharp/Memory/TransformItemsInplaceDelegate.cs @@ -0,0 +1,9 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +namespace SixLabors.ImageSharp.Memory +{ + internal delegate void TransformItemsInplaceDelegate(Span data); +} diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs deleted file mode 100644 index fd679d071..000000000 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.CopyTo.cs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.Memory; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers -{ - public partial class MemoryGroupTests - { - public class CopyTo : MemoryGroupTestsBase - { -#pragma warning disable SA1509 - public static readonly TheoryData WhenSourceBufferIsShorterOrEqual_Data = - new TheoryData() - { - { 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] - [MemberData(nameof(WhenSourceBufferIsShorterOrEqual_Data))] - public void WhenSourceBufferIsShorterOrEqual(int srcTotal, int srcBufLen, int trgTotal, int trgBufLen) - { - using MemoryGroup src = this.CreateTestGroup(srcTotal, srcBufLen, true); - using MemoryGroup trg = this.CreateTestGroup(trgTotal, trgBufLen, false); - - src.CopyTo(trg); - - MemoryGroupIndex i = src.MinIndex(); - MemoryGroupIndex j = trg.MinIndex(); - for (; i < src.MaxIndex(); i += 1, j += 1) - { - int a = src.GetElementAt(i); - int b = src.GetElementAt(j); - - Assert.Equal(a, b); - } - } - } - } -} diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs index f7865b00c..f9f725fb4 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using Xunit; @@ -26,6 +27,124 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers Assert.False(g.IsValid); } +#pragma warning disable SA1509 + private static readonly TheoryData CopyAndTransformData = + new TheoryData() + { + { 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 }, + }; + + public class CopyTo : MemoryGroupTestsBase + { + public static readonly TheoryData WhenSourceBufferIsShorterOrEqual_Data = + CopyAndTransformData; + + [Theory] + [MemberData(nameof(WhenSourceBufferIsShorterOrEqual_Data))] + public void WhenSourceBufferIsShorterOrEqual(int srcTotal, int srcBufLen, int trgTotal, int trgBufLen) + { + using MemoryGroup src = this.CreateTestGroup(srcTotal, srcBufLen, true); + using MemoryGroup 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 src = this.CreateTestGroup(10, 20, true); + using MemoryGroup trg = this.CreateTestGroup(5, 20, false); + + Assert.Throws(() => src.CopyTo(trg)); + } + } + + public class TransformTo : MemoryGroupTestsBase + { + public static readonly TheoryData WhenSourceBufferIsShorterOrEqual_Data = + CopyAndTransformData; + + [Theory] + [MemberData(nameof(WhenSourceBufferIsShorterOrEqual_Data))] + public void WhenSourceBufferIsShorterOrEqual(int srcTotal, int srcBufLen, int trgTotal, int trgBufLen) + { + using MemoryGroup src = this.CreateTestGroup(srcTotal, srcBufLen, true); + using MemoryGroup trg = this.CreateTestGroup(trgTotal, trgBufLen, false); + + src.TransformTo(trg, MultiplyAllBy2); + + 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(b == 2 * a, $"Mismatch @ {pos} Expected: {a} Actual: {b}"); + } + + + } + + [Fact] + public void WhenTargetBufferTooShort_Throws() + { + using MemoryGroup src = this.CreateTestGroup(10, 20, true); + using MemoryGroup trg = this.CreateTestGroup(5, 20, false); + + Assert.Throws(() => src.TransformTo(trg, MultiplyAllBy2)); + } + } + + + [Theory] + [InlineData(100, 5)] + [InlineData(100, 101)] + public void TransformInplace(int totalLength, int bufferLength) + { + using MemoryGroup src = this.CreateTestGroup(10, 20, true); + + src.TransformInplace(s => MultiplyAllBy2(s, s)); + + int cnt = 1; + for (MemoryGroupIndex i = src.MinIndex(); i < src.MaxIndex(); i += 1) + { + int val = src.GetElementAt(i); + Assert.Equal(expected: cnt * 2, val); + cnt++; + } + } + + private static void MultiplyAllBy2(ReadOnlySpan source, Span target) + { + Assert.Equal(source.Length, target.Length); + for (int k = 0; k < source.Length; k++) + { + target[k] = source[k] * 2; + } + } + [StructLayout(LayoutKind.Sequential, Size = 5)] private struct S5 {