mirror of https://github.com/SixLabors/ImageSharp
12 changed files with 467 additions and 145 deletions
@ -0,0 +1,16 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System; |
|||
|
|||
namespace SixLabors.ImageSharp.Memory |
|||
{ |
|||
internal static class MemoryGroupExtensions |
|||
{ |
|||
public static void CopyTo<T>(this IMemoryGroup<T> source, IMemoryGroup<T> target) |
|||
where T : struct |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,120 @@ |
|||
using System; |
|||
using SixLabors.ImageSharp.Memory; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers |
|||
{ |
|||
public struct MemoryGroupIndex : IEquatable<MemoryGroupIndex> |
|||
{ |
|||
public override bool Equals(object obj) => obj is MemoryGroupIndex other && this.Equals(other); |
|||
|
|||
public override int GetHashCode() => HashCode.Combine(this.BufferLength, this.BufferIndex, this.ElementIndex); |
|||
|
|||
public int BufferLength { get; } |
|||
|
|||
public int BufferIndex { get; } |
|||
|
|||
public int ElementIndex { get; } |
|||
|
|||
public MemoryGroupIndex(int bufferLength, int bufferIndex, int elementIndex) |
|||
{ |
|||
this.BufferLength = bufferLength; |
|||
this.BufferIndex = bufferIndex; |
|||
this.ElementIndex = elementIndex; |
|||
} |
|||
|
|||
public static MemoryGroupIndex operator +(MemoryGroupIndex idx, int val) |
|||
{ |
|||
int nextElementIndex = idx.ElementIndex + val; |
|||
return new MemoryGroupIndex( |
|||
idx.BufferLength, |
|||
idx.BufferIndex + (nextElementIndex / idx.BufferLength), |
|||
nextElementIndex % idx.BufferLength); |
|||
} |
|||
|
|||
public bool Equals(MemoryGroupIndex other) |
|||
{ |
|||
if (this.BufferLength != other.BufferLength) |
|||
{ |
|||
throw new InvalidOperationException(); |
|||
} |
|||
|
|||
return this.BufferIndex == other.BufferIndex && this.ElementIndex == other.ElementIndex; |
|||
} |
|||
|
|||
public static bool operator ==(MemoryGroupIndex a, MemoryGroupIndex b) => a.Equals(b); |
|||
|
|||
public static bool operator !=(MemoryGroupIndex a, MemoryGroupIndex b) => !a.Equals(b); |
|||
|
|||
public static bool operator <(MemoryGroupIndex a, MemoryGroupIndex b) |
|||
{ |
|||
if (a.BufferLength != b.BufferLength) |
|||
{ |
|||
throw new InvalidOperationException(); |
|||
} |
|||
|
|||
if (a.BufferIndex < b.BufferIndex) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (a.BufferIndex == b.BufferIndex) |
|||
{ |
|||
return a.ElementIndex < b.ElementIndex; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public static bool operator >(MemoryGroupIndex a, MemoryGroupIndex b) |
|||
{ |
|||
if (a.BufferLength != b.BufferLength) |
|||
{ |
|||
throw new InvalidOperationException(); |
|||
} |
|||
|
|||
if (a.BufferIndex > b.BufferIndex) |
|||
{ |
|||
return true; |
|||
} |
|||
|
|||
if (a.BufferIndex == b.BufferIndex) |
|||
{ |
|||
return a.ElementIndex > b.ElementIndex; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
|
|||
internal static class MemoryGroupIndexExtensions |
|||
{ |
|||
public static T GetElementAt<T>(this MemoryGroup<T> group, MemoryGroupIndex idx) |
|||
where T : struct |
|||
{ |
|||
return group[idx.BufferIndex].Span[idx.ElementIndex]; |
|||
} |
|||
|
|||
public static void SetElementAt<T>(this MemoryGroup<T> group, MemoryGroupIndex idx, T value) |
|||
where T : struct |
|||
{ |
|||
group[idx.BufferIndex].Span[idx.ElementIndex] = value; |
|||
} |
|||
|
|||
public static MemoryGroupIndex MinIndex<T>(this MemoryGroup<T> group) |
|||
where T : struct |
|||
{ |
|||
return new MemoryGroupIndex(group.BufferLength, 0, 0); |
|||
} |
|||
|
|||
public static MemoryGroupIndex MaxIndex<T>(this MemoryGroup<T> group) |
|||
where T : struct |
|||
{ |
|||
if (group.Count == 0) |
|||
{ |
|||
return new MemoryGroupIndex(group.BufferLength, 0, 0); |
|||
} |
|||
|
|||
return new MemoryGroupIndex(group.BufferLength, group.Count - 1, group[^1].Length - 1); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers |
|||
{ |
|||
public class MemoryGroupIndexTests |
|||
{ |
|||
[Fact] |
|||
public void Equal() |
|||
{ |
|||
var a = new MemoryGroupIndex(10, 1, 3); |
|||
var b = new MemoryGroupIndex(10, 1, 3); |
|||
|
|||
Assert.True(a.Equals(b)); |
|||
Assert.True(a == b); |
|||
Assert.False(a != b); |
|||
Assert.False(a < b); |
|||
Assert.False(a > b); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SmallerBufferIndex() |
|||
{ |
|||
var a = new MemoryGroupIndex(10, 3, 3); |
|||
var b = new MemoryGroupIndex(10, 5, 3); |
|||
|
|||
Assert.False(a == b); |
|||
Assert.True(a != b); |
|||
Assert.True(a < b); |
|||
Assert.False(a > b); |
|||
} |
|||
|
|||
[Fact] |
|||
public void SmallerElementIndex() |
|||
{ |
|||
var a = new MemoryGroupIndex(10, 3, 3); |
|||
var b = new MemoryGroupIndex(10, 3, 9); |
|||
|
|||
Assert.False(a == b); |
|||
Assert.True(a != b); |
|||
Assert.True(a < b); |
|||
Assert.False(a > b); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Increment() |
|||
{ |
|||
var a = new MemoryGroupIndex(10, 3, 3); |
|||
a += 1; |
|||
Assert.Equal(new MemoryGroupIndex(10, 3, 4), a); |
|||
} |
|||
|
|||
[Fact] |
|||
public void Increment_OverflowBuffer() |
|||
{ |
|||
var a = new MemoryGroupIndex(10, 5, 3); |
|||
var b = new MemoryGroupIndex(10, 5, 9); |
|||
a += 8; |
|||
b += 1; |
|||
|
|||
Assert.Equal(new MemoryGroupIndex(10, 6, 1), a); |
|||
Assert.Equal(new MemoryGroupIndex(10, 6, 0), b); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,119 @@ |
|||
// Copyright (c) Six Labors and contributors.
|
|||
// Licensed under the Apache License, Version 2.0.
|
|||
|
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using SixLabors.ImageSharp.Memory; |
|||
using Xunit; |
|||
|
|||
namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers |
|||
{ |
|||
public partial class MemoryGroupTests |
|||
{ |
|||
public class Allocate : MemoryGroupTestsBase |
|||
{ |
|||
#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, 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, 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 }, |
|||
{ 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, 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 BufferSizesAreCorrect<T>( |
|||
T dummy, |
|||
int bufferCapacity, |
|||
int bufferAlignment, |
|||
long totalLength, |
|||
int expectedNumberOfBuffers, |
|||
int expectedBufferSize, |
|||
int expectedSizeOfLastBuffer) |
|||
where T : struct |
|||
{ |
|||
this.MemoryAllocator.BufferCapacity = bufferCapacity; |
|||
|
|||
// Act:
|
|||
using var g = MemoryGroup<T>.Allocate(this.MemoryAllocator, totalLength, bufferAlignment); |
|||
|
|||
// Assert:
|
|||
Assert.Equal(expectedNumberOfBuffers, g.Count); |
|||
Assert.Equal(expectedBufferSize, g.BufferLength); |
|||
if (g.Count == 0) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
for (int i = 0; i < g.Count - 1; i++) |
|||
{ |
|||
Assert.Equal(g[i].Length, expectedBufferSize); |
|||
} |
|||
|
|||
Assert.Equal(g.Last().Length, expectedSizeOfLastBuffer); |
|||
} |
|||
|
|||
[Fact] |
|||
public void WhenBlockAlignmentIsOverCapacity_Throws_InvalidMemoryOperationException() |
|||
{ |
|||
this.MemoryAllocator.BufferCapacity = 84; // 42 * Int16
|
|||
|
|||
Assert.Throws<InvalidMemoryOperationException>(() => |
|||
{ |
|||
MemoryGroup<short>.Allocate(this.MemoryAllocator, 50, 43); |
|||
}); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(AllocationOptions.None)] |
|||
[InlineData(AllocationOptions.Clean)] |
|||
public void MemoryAllocatorIsUtilizedCorrectly(AllocationOptions allocationOptions) |
|||
{ |
|||
this.MemoryAllocator.BufferCapacity = 200; |
|||
|
|||
HashSet<int> bufferHashes; |
|||
|
|||
int expectedBlockCount = 5; |
|||
using (var g = MemoryGroup<short>.Allocate(this.MemoryAllocator, 500, 100, allocationOptions)) |
|||
{ |
|||
IReadOnlyList<TestMemoryAllocator.AllocationRequest> allocationLog = this.MemoryAllocator.AllocationLog; |
|||
Assert.Equal(expectedBlockCount, allocationLog.Count); |
|||
bufferHashes = allocationLog.Select(l => l.HashCodeOfBuffer).ToHashSet(); |
|||
Assert.Equal(expectedBlockCount, bufferHashes.Count); |
|||
Assert.Equal(0, this.MemoryAllocator.ReturnLog.Count); |
|||
|
|||
for (int i = 0; i < expectedBlockCount; i++) |
|||
{ |
|||
Assert.Equal(allocationOptions, allocationLog[i].AllocationOptions); |
|||
Assert.Equal(100, allocationLog[i].Length); |
|||
Assert.Equal(200, allocationLog[i].LengthInBytes); |
|||
} |
|||
} |
|||
|
|||
Assert.Equal(expectedBlockCount, this.MemoryAllocator.ReturnLog.Count); |
|||
Assert.True(bufferHashes.SetEquals(this.MemoryAllocator.ReturnLog.Select(l => l.HashCodeOfBuffer))); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
// 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 |
|||
{ |
|||
public static readonly TheoryData<int, int, int, int> WhenSourceBufferIsShorterOrEqual_Data = |
|||
new TheoryData<int, int, int, int>() |
|||
{ |
|||
{ 20, 5, 20, 4 }, |
|||
{ 20, 4, 20, 5 }, |
|||
{ 18, 6, 20, 5 }, |
|||
{ 19, 10, 20, 10 }, |
|||
{ 21, 10, 22, 2 }, |
|||
{ 1, 5, 5, 4 }, |
|||
}; |
|||
|
|||
[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); |
|||
|
|||
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); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue