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