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
+}