diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index f503ea64a..0d803475a 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -25,9 +25,9 @@ - - - + + + diff --git a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs index c4d92ca3c..57a5b77bc 100644 --- a/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/ArrayPoolMemoryAllocator.cs @@ -89,6 +89,14 @@ namespace SixLabors.ImageSharp.Memory this.InitArrayPools(); } + /// + /// Gets or sets the length of the largest contiguous buffer that can be handled by this allocator instance. + /// + public int MaximumContiguousBufferLength { get; set; } = Int32.MaxValue; + + /// + protected internal override int GetMaximumContiguousBufferLength() => this.MaximumContiguousBufferLength; + /// public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { @@ -147,4 +155,4 @@ namespace SixLabors.ImageSharp.Memory this.normalArrayPool = ArrayPool.Create(this.PoolSelectorThresholdInBytes, this.maxArraysPerBucketNormalPool); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs index 20598c3e3..1e1f69784 100644 --- a/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/MemoryAllocator.cs @@ -10,6 +10,11 @@ namespace SixLabors.ImageSharp.Memory /// public abstract class MemoryAllocator { + /// + /// Gets the length of the largest contiguous buffer that can be handled by this allocator instance. + /// + protected internal abstract int GetMaximumContiguousBufferLength(); + /// /// Allocates an , holding a of length . /// diff --git a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs index 54b64b131..ee9afbac5 100644 --- a/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs +++ b/src/ImageSharp/Memory/Allocators/SimpleGcMemoryAllocator.cs @@ -11,6 +11,9 @@ namespace SixLabors.ImageSharp.Memory /// public sealed class SimpleGcMemoryAllocator : MemoryAllocator { + /// + protected internal override int GetMaximumContiguousBufferLength() => int.MaxValue; + /// public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { @@ -27,4 +30,4 @@ namespace SixLabors.ImageSharp.Memory return new BasicByteBuffer(new byte[length]); } } -} \ No newline at end of file +} diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index 08731846e..ec7665998 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Memory /// Represents a rectangular area inside a 2D memory buffer (). /// This type is kind-of 2D Span, but it can live on heap. /// - /// The element type + /// The element type. internal readonly struct BufferArea where T : struct { diff --git a/src/ImageSharp/Memory/DiscontinuousProto/IUniformMemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontinuousProto/IUniformMemoryGroup{T}.cs new file mode 100644 index 000000000..e6c71fa55 --- /dev/null +++ b/src/ImageSharp/Memory/DiscontinuousProto/IUniformMemoryGroup{T}.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Memory.DiscontinuousProto +{ + /// + /// Represents a group of one or more uniformly-sized discontinuous memory segments. + /// The last segment can be smaller than the preceding ones. + /// + /// The element type. + public interface IUniformMemoryGroup : IReadOnlyList> where T : struct + { + bool IsValid { get; } + } +} diff --git a/src/ImageSharp/Memory/DiscontinuousProto/InvalidMemoryOperationException.cs b/src/ImageSharp/Memory/DiscontinuousProto/InvalidMemoryOperationException.cs new file mode 100644 index 000000000..f756a1246 --- /dev/null +++ b/src/ImageSharp/Memory/DiscontinuousProto/InvalidMemoryOperationException.cs @@ -0,0 +1,8 @@ +using System; + +namespace SixLabors.ImageSharp.Memory.DiscontinuousProto +{ + public class InvalidMemoryOperationException : InvalidOperationException + { + } +} diff --git a/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroupView{T}.cs b/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroupView{T}.cs new file mode 100644 index 000000000..68ef5c25d --- /dev/null +++ b/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroupView{T}.cs @@ -0,0 +1,76 @@ +using System; +using System.Buffers; +using System.Collections; +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Memory.DiscontinuousProto +{ + /// + /// Implements , defining a view for + /// rather than owning the segments. + /// + /// + /// This type provides an indirection, protecting the users of publicly exposed memory API-s + /// from internal memory-swaps. Whenever an internal swap happens, the + /// instance becomes invalid, throwing an exception on all operations. + /// + /// The element type. + public class UniformMemoryGroupView : IUniformMemoryGroup where T : struct + { + private readonly UniformMemoryGroup owner; + private readonly MemoryOwnerWrapper[] memoryWrappers; + + public UniformMemoryGroupView(UniformMemoryGroup owner) + { + this.IsValid = true; + this.owner = owner; + this.memoryWrappers = new MemoryOwnerWrapper[owner.Count]; + + for (int i = 0; i < owner.Count; i++) + { + this.memoryWrappers[i] = new MemoryOwnerWrapper(this, i); + } + } + + public IEnumerator> GetEnumerator() => throw new NotImplementedException(); + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + + public int Count { get; } + + public Memory this[int index] => throw new NotImplementedException(); + + public bool IsValid { get; internal set; } + + class MemoryOwnerWrapper : MemoryManager + { + private UniformMemoryGroupView view; + + private int index; + + public MemoryOwnerWrapper(UniformMemoryGroupView view, int index) + { + this.view = view; + this.index = index; + } + + protected override void Dispose(bool disposing) + { + } + + public override Span GetSpan() + { + if (!this.view.IsValid) + { + throw new InvalidOperationException(); + } + + return this.view[this.index].Span; + } + + public override MemoryHandle Pin(int elementIndex = 0) => throw new NotImplementedException(); + + public override void Unpin() => throw new NotImplementedException(); + } + } +} diff --git a/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.Allocated.cs b/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.Allocated.cs new file mode 100644 index 000000000..b9157b59d --- /dev/null +++ b/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.Allocated.cs @@ -0,0 +1,69 @@ +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; + +namespace SixLabors.ImageSharp.Memory.DiscontinuousProto +{ + public abstract partial class UniformMemoryGroup + { + private class Allocated : UniformMemoryGroup + { + private IMemoryOwner[] memoryOwners; + + public Allocated(IMemoryOwner[] memoryOwners) + { + this.memoryOwners = memoryOwners; + } + + public override IEnumerator> GetEnumerator() + { + this.EnsureNotDisposed(); + return this.memoryOwners.Select(mo => mo.Memory).GetEnumerator(); + } + + + public override int Count + { + get + { + this.EnsureNotDisposed(); + return this.memoryOwners.Length; + } + } + + public override Memory this[int index] + { + get + { + this.EnsureNotDisposed(); + return this.memoryOwners[index].Memory; + } + } + + public override void Dispose() + { + if (this.memoryOwners == null) + { + return; + } + + foreach (IMemoryOwner memoryOwner in this.memoryOwners) + { + memoryOwner.Dispose(); + } + + this.memoryOwners = null; + this.IsValid = false; + } + + private void EnsureNotDisposed() + { + if (this.memoryOwners == null) + { + throw new ObjectDisposedException(nameof(UniformMemoryGroup)); + } + } + } + } +} diff --git a/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.External.cs b/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.External.cs new file mode 100644 index 000000000..024b42a3c --- /dev/null +++ b/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.External.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Memory.DiscontinuousProto +{ + public abstract partial class UniformMemoryGroup + { + private class External : UniformMemoryGroup + { + private readonly ReadOnlyMemory> source; + + public External(ReadOnlyMemory> source) + { + // TODO: sizes should be uniform, validate! + + this.source = source; + } + + public override IEnumerator> GetEnumerator() + { + for (int i = 0; i < this.source.Length; i++) + { + yield return this.source.Span[i]; + } + } + + public override int Count => this.source.Length; + + public override Memory this[int index] => this.source.Span[index]; + + public override void Dispose() + { + // No ownership nothing to dispose + } + } + } +} diff --git a/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.cs b/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.cs new file mode 100644 index 000000000..4682d6813 --- /dev/null +++ b/src/ImageSharp/Memory/DiscontinuousProto/UniformMemoryGroup{T}.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace SixLabors.ImageSharp.Memory.DiscontinuousProto +{ + /// + /// Represents a group of one or more uniformly-sized discontinuous memory segments, owned by this instance. + /// + /// The element type. + public abstract partial class UniformMemoryGroup : IUniformMemoryGroup, IDisposable where T : struct + { + public abstract IEnumerator> GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); + + public abstract int Count { get; } + + public abstract Memory this[int index] { get; } + + public abstract void Dispose(); + + public bool IsValid { get; protected set; } + + public static UniformMemoryGroup Allocate(MemoryAllocator allocator, long length) + { + long bufferCount = length / allocator.GetMaximumContiguousBufferLength(); + + // TODO: Allocate bufferCount buffers + throw new NotImplementedException(); + } + + public static UniformMemoryGroup Wrap(params Memory[] source) => Wrap(source.AsMemory()); + + public static UniformMemoryGroup Wrap(ReadOnlyMemory> source) + { + return new External(source); + } + } +} diff --git a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs index fcda2eaa1..47bedda9a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestMemoryAllocator.cs @@ -24,6 +24,8 @@ namespace SixLabors.ImageSharp.Tests.Memory public IList AllocationLog => this.allocationLog; + protected internal override int GetMaximumContiguousBufferLength() => int.MaxValue; + public override IMemoryOwner Allocate(int length, AllocationOptions options = AllocationOptions.None) { T[] array = this.AllocateArray(length, options); @@ -152,4 +154,4 @@ namespace SixLabors.ImageSharp.Tests.Memory } } } -} \ No newline at end of file +}