Browse Source

BufferPointer<T> became BufferSpan<T>

af/merge-core
Anton Firszov 9 years ago
parent
commit
86043577e5
  1. 32
      src/ImageSharp/Colors/Color.BulkOperations.cs
  2. 84
      src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs
  3. 143
      src/ImageSharp/Common/Memory/BufferPointer{T}.cs
  4. 28
      src/ImageSharp/Common/Memory/BufferSpan.cs
  5. 209
      src/ImageSharp/Common/Memory/BufferSpan{T}.cs
  6. 48
      src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
  7. 38
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  8. 6
      src/ImageSharp/Image/PixelArea{TColor}.cs
  9. 8
      tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
  10. 184
      tests/ImageSharp.Tests/Common/BufferSpanTests.cs
  11. 10
      tests/ImageSharp.Tests/Common/PinnedBufferTests.cs

32
src/ImageSharp/Colors/Color.BulkOperations.cs

@ -24,8 +24,8 @@ namespace ImageSharp
/// SIMD optimized bulk implementation of <see cref="IPixel.PackFromVector4(Vector4)"/>
/// that works only with `count` divisible by <see cref="Vector{UInt32}.Count"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferPointer{T}"/> to the dstination vectors.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferSpan{T}"/> to the dstination vectors.</param>
/// <param name="count">The number of pixels to convert.</param>
/// <remarks>
/// Implementation adapted from:
@ -38,8 +38,8 @@ namespace ImageSharp
/// </see>
/// </remarks>
internal static unsafe void ToVector4SimdAligned(
BufferPointer<Color> sourceColors,
BufferPointer<Vector4> destVectors,
BufferSpan<Color> sourceColors,
BufferSpan<Vector4> destVectors,
int count)
{
int vecSize = Vector<uint>.Count;
@ -85,12 +85,12 @@ namespace ImageSharp
vf.CopyTo(fTemp, i);
}
BufferPointer.Copy<uint>(tempBuf, (BufferPointer<byte>)destVectors, unpackedRawCount);
BufferSpan.Copy<uint>(tempBuf, (BufferSpan<byte>)destVectors, unpackedRawCount);
}
}
/// <inheritdoc />
internal override void ToVector4(BufferPointer<Color> sourceColors, BufferPointer<Vector4> destVectors, int count)
internal override void ToVector4(BufferSpan<Color> sourceColors, BufferSpan<Vector4> destVectors, int count)
{
if (count < 256)
{
@ -117,7 +117,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void PackFromXyzBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count)
internal override unsafe void PackFromXyzBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
@ -132,7 +132,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void ToXyzBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count)
internal override unsafe void ToXyzBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;
@ -149,19 +149,19 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override void PackFromXyzwBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count)
internal override void PackFromXyzwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{
BufferPointer.Copy(sourceBytes, destColors, count);
BufferSpan.Copy(sourceBytes, destColors, count);
}
/// <inheritdoc />
internal override void ToXyzwBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count)
internal override void ToXyzwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
BufferPointer.Copy(sourceColors, destBytes, count);
BufferSpan.Copy(sourceColors, destBytes, count);
}
/// <inheritdoc />
internal override unsafe void PackFromZyxBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count)
internal override unsafe void PackFromZyxBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
@ -176,7 +176,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void ToZyxBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count)
internal override unsafe void ToZyxBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;
@ -193,7 +193,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void PackFromZyxwBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count)
internal override unsafe void PackFromZyxwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
@ -208,7 +208,7 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void ToZyxwBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count)
internal override unsafe void ToZyxwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;

84
src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs

@ -29,12 +29,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromVector4(Vector4)"/>
/// </summary>
/// <param name="sourceVectors">The <see cref="BufferPointer{T}"/> to the source vectors.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceVectors">The <see cref="BufferSpan{T}"/> to the source vectors.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromVector4(
BufferPointer<Vector4> sourceVectors,
BufferPointer<TColor> destColors,
BufferSpan<Vector4> sourceVectors,
BufferSpan<TColor> destColors,
int count)
{
Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset;
@ -55,12 +55,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToVector4()"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferPointer{T}"/> to the destination vectors.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferSpan{T}"/> to the destination vectors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToVector4(
BufferPointer<TColor> sourceColors,
BufferPointer<Vector4> destVectors,
BufferSpan<TColor> sourceColors,
BufferSpan<Vector4> destVectors,
int count)
{
byte* sp = (byte*)sourceColors;
@ -78,12 +78,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyz"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromXyzBytes(
BufferPointer<byte> sourceBytes,
BufferPointer<TColor> destColors,
BufferSpan<byte> sourceBytes,
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
@ -102,15 +102,15 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToXyzBytes(byte[], int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzBytes(BufferPointer<TColor> sourceColors, BufferPointer<byte> destBytes, int count)
internal virtual void ToXyzBytes(BufferSpan<TColor> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array;
for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3)
for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToXyzBytes(dest, i);
@ -121,12 +121,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyzw"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromXyzwBytes(
BufferPointer<byte> sourceBytes,
BufferPointer<TColor> destColors,
BufferSpan<byte> sourceBytes,
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
@ -145,18 +145,18 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToXyzwBytes(byte[], int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzwBytes(
BufferPointer<TColor> sourceColors,
BufferPointer<byte> destBytes,
BufferSpan<TColor> sourceColors,
BufferSpan<byte> destBytes,
int count)
{
byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array;
for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4)
for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToXyzwBytes(dest, i);
@ -167,12 +167,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyx"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromZyxBytes(
BufferPointer<byte> sourceBytes,
BufferPointer<TColor> destColors,
BufferSpan<byte> sourceBytes,
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
@ -191,15 +191,15 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToZyxBytes(byte[], int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxBytes(BufferPointer<TColor> sourceColors, BufferPointer<byte> destBytes, int count)
internal virtual void ToZyxBytes(BufferSpan<TColor> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array;
for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3)
for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToZyxBytes(dest, i);
@ -210,12 +210,12 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyxw"/>.
/// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param>
/// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromZyxwBytes(
BufferPointer<byte> sourceBytes,
BufferPointer<TColor> destColors,
BufferSpan<byte> sourceBytes,
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
@ -234,18 +234,18 @@ namespace ImageSharp
/// <summary>
/// Bulk version of <see cref="IPixel.ToZyxwBytes(byte[], int)"/>.
/// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param>
/// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxwBytes(
BufferPointer<TColor> sourceColors,
BufferPointer<byte> destBytes,
BufferSpan<TColor> sourceColors,
BufferSpan<byte> destBytes,
int count)
{
byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array;
for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4)
for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToZyxwBytes(dest, i);

143
src/ImageSharp/Common/Memory/BufferPointer{T}.cs

@ -1,143 +0,0 @@
// <copyright file="BufferPointer{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Provides access to elements in an array from a given position.
/// This type shares many similarities with corefx System.Span&lt;T&gt; but there are significant differences in it's functionalities and semantics:
/// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays
/// - It's possible to retrieve a reference to the array (<see cref="Array"/>) so we can pass it to API-s like <see cref="Marshal.Copy(byte[], int, IntPtr, int)"/>
/// - There is no bounds checking for performance reasons. Therefore we don't need to store length. (However this could be added as DEBUG-only feature.)
/// This makes <see cref="BufferPointer{T}"/> an unsafe type!
/// - Currently the arrays provided to BufferPointer need to be pinned. This behaviour could be changed using C#7 features.
/// </summary>
/// <typeparam name="T">The type of elements of the array</typeparam>
internal unsafe struct BufferPointer<T>
where T : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="BufferPointer{T}"/> struct from a pinned array and an offset.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of array</param>
/// <param name="offset">The offset inside the array</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferPointer(T[] array, void* pointerToArray, int offset)
{
DebugGuard.NotNull(array, nameof(array));
this.Array = array;
this.Offset = offset;
this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf<T>() * offset);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferPointer{T}"/> struct from a pinned array.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the start of 'array'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferPointer(T[] array, void* pointerToArray)
{
DebugGuard.NotNull(array, nameof(array));
this.Array = array;
this.Offset = 0;
this.PointerAtOffset = (IntPtr)pointerToArray;
}
/// <summary>
/// Gets the array
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets the offset inside <see cref="Array"/>
/// </summary>
public int Offset { get; private set; }
/// <summary>
/// Gets the offset inside <see cref="Array"/> in bytes.
/// </summary>
public int ByteOffset => this.Offset * Unsafe.SizeOf<T>();
/// <summary>
/// Gets the pointer to the offseted array position
/// </summary>
public IntPtr PointerAtOffset { get; private set; }
/// <summary>
/// Convertes <see cref="BufferPointer{T}"/> instance to a raw 'void*' pointer
/// </summary>
/// <param name="bufferPointer">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator void*(BufferPointer<T> bufferPointer)
{
return (void*)bufferPointer.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferPointer{T}"/> instance to a raw 'byte*' pointer
/// </summary>
/// <param name="bufferPointer">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator byte*(BufferPointer<T> bufferPointer)
{
return (byte*)bufferPointer.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferPointer{T}"/> instance to <see cref="BufferPointer{Byte}"/>
/// setting it's <see cref="Offset"/> and <see cref="PointerAtOffset"/> to correct values.
/// </summary>
/// <param name="source">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator BufferPointer<byte>(BufferPointer<T> source)
{
BufferPointer<byte> result = default(BufferPointer<byte>);
result.Array = Unsafe.As<byte[]>(source.Array);
result.Offset = source.Offset * Unsafe.SizeOf<T>();
result.PointerAtOffset = source.PointerAtOffset;
return result;
}
/// <summary>
/// Forms a slice out of the given BufferPointer, beginning at 'offset'.
/// </summary>
/// <param name="offset">The offset in number of elements</param>
/// <returns>The offseted (sliced) BufferPointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferPointer<T> Slice(int offset)
{
BufferPointer<T> result = default(BufferPointer<T>);
result.Array = this.Array;
result.Offset = this.Offset + offset;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * offset);
return result;
}
/// <summary>
/// Clears `count` elements beginning from the pointed position.
/// </summary>
/// <param name="count">The number of elements to clear</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear(int count)
{
if (count < 256)
{
Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferPointer.USizeOf<T>(count));
}
else
{
System.Array.Clear(this.Array, this.Offset, count);
}
}
}
}

28
src/ImageSharp/Common/Memory/BufferPointer.cs → src/ImageSharp/Common/Memory/BufferSpan.cs

@ -1,4 +1,4 @@
// <copyright file="BufferPointer.cs" company="James Jackson-South">
// <copyright file="BufferSpan.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
@ -11,9 +11,9 @@ namespace ImageSharp
using System.Runtime.InteropServices;
/// <summary>
/// Utility methods for <see cref="BufferPointer{T}"/>
/// Utility methods for <see cref="BufferSpan{T}"/>
/// </summary>
internal static class BufferPointer
internal static class BufferSpan
{
/// <summary>
/// It's worth to use Marshal.Copy() or Buffer.BlockCopy() over this size.
@ -24,11 +24,11 @@ namespace ImageSharp
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="source">The input <see cref="BufferPointer{T}"/></param>
/// <param name="destination">The destination <see cref="BufferPointer{T}"/>.</param>
/// <param name="source">The input <see cref="BufferSpan{T}"/></param>
/// <param name="destination">The destination <see cref="BufferSpan{T}"/>.</param>
/// <param name="count">The number of elements to copy</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(BufferPointer<T> source, BufferPointer<T> destination, int count)
public static void Copy<T>(BufferSpan<T> source, BufferSpan<T> destination, int count)
where T : struct
{
CopyImpl(source, destination, count);
@ -42,7 +42,7 @@ namespace ImageSharp
/// <param name="destination">The destination buffer.</param>
/// <param name="countInSource">The number of elements to copy from 'source'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(BufferPointer<T> source, BufferPointer<byte> destination, int countInSource)
public static void Copy<T>(BufferSpan<T> source, BufferSpan<byte> destination, int countInSource)
where T : struct
{
CopyImpl(source, destination, countInSource);
@ -56,14 +56,14 @@ namespace ImageSharp
/// <param name="destination">The destination buffer"/></param>
/// <param name="countInDest">The number of <typeparamref name="T"/> elements to copy.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(BufferPointer<byte> source, BufferPointer<T> destination, int countInDest)
public static unsafe void Copy<T>(BufferSpan<byte> source, BufferSpan<T> destination, int countInDest)
where T : struct
{
int byteCount = SizeOf<T>(countInDest);
if (byteCount > (int)ByteCountThreshold)
{
Marshal.Copy(source.Array, source.Offset, destination.PointerAtOffset, byteCount);
Marshal.Copy(source.Array, source.Start, destination.PointerAtOffset, byteCount);
}
else
{
@ -93,7 +93,7 @@ namespace ImageSharp
=> (uint)SizeOf<T>(count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CopyImpl<T, TDest>(BufferPointer<T> source, BufferPointer<TDest> destination, int count)
private static unsafe void CopyImpl<T, TDest>(BufferSpan<T> source, BufferSpan<TDest> destination, int count)
where T : struct
where TDest : struct
{
@ -103,22 +103,22 @@ namespace ImageSharp
{
if (Unsafe.SizeOf<T>() == sizeof(long))
{
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(int))
{
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(short))
{
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(byte))
{
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Offset, destination.PointerAtOffset, count);
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
}

209
src/ImageSharp/Common/Memory/BufferSpan{T}.cs

@ -0,0 +1,209 @@
// <copyright file="BufferSpan{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Represents a contiguous region of a pinned managed array.
/// The array is usually owned by a <see cref="PinnedBuffer{T}"/> instance.
/// </summary>
/// <remarks>
/// <see cref="BufferSpan{T}"/> is very similar to corefx System.Span&lt;T&gt;, and we try to maintain a compatible API.
/// There are several differences though:
/// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays.
/// - It's possible to retrieve a reference to the array (<see cref="Array"/>) so we can pass it to API-s like <see cref="Marshal.Copy(byte[], int, IntPtr, int)"/>
/// - It's possible to retrieve the pinned pointer. This enables optimized (unchecked) unsafe operations.
/// - There is no bounds checking for performance reasons, only in debug mode. This makes <see cref="BufferSpan{T}"/> an unsafe type!
/// </remarks>
/// <typeparam name="T">The type of elements of the array</typeparam>
internal unsafe struct BufferSpan<T>
where T : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array and an start.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of the array</param>
/// <param name="start">The index at which to begin the span.</param>
/// <param name="length">The length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray, int start, int length)
{
GuardArrayAndPointer(array, pointerToArray);
DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start));
DebugGuard.MustBeLessThanOrEqualTo(length, array.Length - start, nameof(length));
this.Array = array;
this.Length = length;
this.Start = start;
this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf<T>() * start);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array and an start.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of the array</param>
/// <param name="start">The index at which to begin the span.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray, int start)
{
GuardArrayAndPointer(array, pointerToArray);
DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start));
this.Array = array;
this.Length = array.Length - start;
this.Start = start;
this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf<T>() * start);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the start of 'array'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray)
{
GuardArrayAndPointer(array, pointerToArray);
this.Array = array;
this.Start = 0;
this.Length = array.Length;
this.PointerAtOffset = (IntPtr)pointerToArray;
}
/// <summary>
/// Gets the backing array
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets the length of the <see cref="BufferSpan{T}"/>
/// </summary>
public int Length { get; private set; }
/// <summary>
/// Gets the start inside <see cref="Array"/>
/// </summary>
public int Start { get; private set; }
/// <summary>
/// Gets the start inside <see cref="Array"/> in bytes.
/// </summary>
public int ByteOffset => this.Start * Unsafe.SizeOf<T>();
/// <summary>
/// Gets the pointer to the offseted array position
/// </summary>
public IntPtr PointerAtOffset { get; private set; }
/// <summary>
/// Convertes <see cref="BufferSpan{T}"/> instance to a raw 'void*' pointer
/// </summary>
/// <param name="bufferSpan">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator void*(BufferSpan<T> bufferSpan)
{
return (void*)bufferSpan.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferSpan{T}"/> instance to a raw 'byte*' pointer
/// </summary>
/// <param name="bufferSpan">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator byte*(BufferSpan<T> bufferSpan)
{
return (byte*)bufferSpan.PointerAtOffset;
}
/// <summary>
/// Converts generic <see cref="BufferSpan{T}"/> to a <see cref="BufferSpan{T}"/> of bytes
/// setting it's <see cref="Start"/> and <see cref="PointerAtOffset"/> to correct values.
/// </summary>
/// <param name="source">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator BufferSpan<byte>(BufferSpan<T> source)
{
BufferSpan<byte> result = default(BufferSpan<byte>);
result.Array = Unsafe.As<byte[]>(source.Array);
result.Start = source.Start * Unsafe.SizeOf<T>();
result.PointerAtOffset = source.PointerAtOffset;
return result;
}
/// <summary>
/// Forms a slice out of the given BufferSpan, beginning at 'start'.
/// </summary>
/// <param name="start">TThe index at which to begin this slice.</param>
/// <returns>The offseted (sliced) BufferSpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan<T> Slice(int start)
{
DebugGuard.MustBeLessThan(start, this.Length, nameof(start));
BufferSpan<T> result = default(BufferSpan<T>);
result.Array = this.Array;
result.Start = this.Start + start;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * start);
result.Length = this.Length - start;
return result;
}
/// <summary>
/// Forms a slice out of the given BufferSpan, beginning at 'start'.
/// </summary>
/// <param name="start">The index at which to begin this slice.</param>
/// <param name="length">The desired length for the slice (exclusive).</param>
/// <returns>The sliced BufferSpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan<T> Slice(int start, int length)
{
DebugGuard.MustBeLessThanOrEqualTo(start, this.Length, nameof(start));
DebugGuard.MustBeLessThanOrEqualTo(length, this.Length - start, nameof(length));
BufferSpan<T> result = default(BufferSpan<T>);
result.Array = this.Array;
result.Start = this.Start + start;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * start);
result.Length = length;
return result;
}
/// <summary>
/// Clears `count` elements beginning from the pointed position.
/// </summary>
/// <param name="count">The number of elements to clear</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear(int count)
{
if (count < 256)
{
Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferSpan.USizeOf<T>(count));
}
else
{
System.Array.Clear(this.Array, this.Start, count);
}
}
[Conditional("DEBUG")]
private static void GuardArrayAndPointer(T[] array, void* pointerToArray)
{
DebugGuard.NotNull(array, nameof(array));
DebugGuard.IsFalse(
pointerToArray == (void*)0,
nameof(pointerToArray),
"pointerToArray should not be null pointer!");
}
}
}

48
src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs

@ -32,11 +32,11 @@ namespace ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int count)
/// <param name="length">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int length)
{
this.Count = count;
this.Array = PixelDataPool<T>.Rent(count);
this.Length = length;
this.Array = PixelDataPool<T>.Rent(length);
this.isPoolingOwner = true;
this.Pin();
}
@ -47,7 +47,7 @@ namespace ImageSharp
/// <param name="array">The array to pin.</param>
public PinnedBuffer(T[] array)
{
this.Count = array.Length;
this.Length = array.Length;
this.Array = array;
this.isPoolingOwner = false;
this.Pin();
@ -56,16 +56,16 @@ namespace ImageSharp
/// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary>
/// <param name="count">The count of "relevant" elements in 'array'.</param>
/// <param name="length">The count of "relevant" elements in 'array'.</param>
/// <param name="array">The array to pin.</param>
public PinnedBuffer(int count, T[] array)
public PinnedBuffer(int length, T[] array)
{
if (array.Length < count)
if (array.Length < length)
{
throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array));
}
this.Count = count;
this.Length = length;
this.Array = array;
this.isPoolingOwner = false;
this.Pin();
@ -85,9 +85,9 @@ namespace ImageSharp
public bool IsDisposedOrLostArrayOwnership { get; private set; }
/// <summary>
/// Gets the count of "relevant" elements. Usually be smaller than 'Array.Length' when <see cref="Array"/> is pooled.
/// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when <see cref="Array"/> is pooled.
/// </summary>
public int Count { get; private set; }
public int Length { get; private set; }
/// <summary>
/// Gets the backing pinned array.
@ -100,11 +100,11 @@ namespace ImageSharp
public IntPtr Pointer { get; private set; }
/// <summary>
/// Converts <see cref="PinnedBuffer{T}"/> to an <see cref="BufferPointer{T}"/>.
/// Converts <see cref="PinnedBuffer{T}"/> to an <see cref="BufferSpan{T}"/>.
/// </summary>
/// <param name="buffer">The <see cref="PinnedBuffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator BufferPointer<T>(PinnedBuffer<T> buffer)
public static implicit operator BufferSpan<T>(PinnedBuffer<T> buffer)
{
return buffer.Slice();
}
@ -122,24 +122,24 @@ namespace ImageSharp
}
/// <summary>
/// Gets a <see cref="BufferPointer{T}"/> to the beginning of the raw data of the buffer.
/// Gets a <see cref="BufferSpan{T}"/> to the beginning of the raw data of the buffer.
/// </summary>
/// <returns>The <see cref="BufferPointer{T}"/></returns>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferPointer<T> Slice()
public unsafe BufferSpan<T> Slice()
{
return new BufferPointer<T>(this.Array, (void*)this.Pointer);
return new BufferSpan<T>(this.Array, (void*)this.Pointer);
}
/// <summary>
/// Gets a <see cref="BufferPointer{T}"/> to an offseted position inside the buffer.
/// Gets a <see cref="BufferSpan{T}"/> to an offseted position inside the buffer.
/// </summary>
/// <param name="offset">The offset</param>
/// <returns>The <see cref="BufferPointer{T}"/></returns>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferPointer<T> Slice(int offset)
public unsafe BufferSpan<T> Slice(int offset)
{
return new BufferPointer<T>(this.Array, (void*)this.Pointer, offset);
return new BufferSpan<T>(this.Array, (void*)this.Pointer, offset);
}
/// <summary>
@ -163,7 +163,7 @@ namespace ImageSharp
this.isPoolingOwner = false;
this.Array = null;
this.Count = 0;
this.Length = 0;
GC.SuppressFinalize(this);
}
@ -190,12 +190,12 @@ namespace ImageSharp
}
/// <summary>
/// Clears the buffer, filling elements between 0 and <see cref="Count"/>-1 with default(T)
/// Clears the buffer, filling elements between 0 and <see cref="Length"/>-1 with default(T)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear()
{
this.Slice().Clear(this.Count);
this.Slice().Clear(this.Length);
}
/// <summary>

38
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -239,12 +239,12 @@ namespace ImageSharp
}
/// <summary>
/// Gets a <see cref="BufferPointer{TColor}"/> to the row 'y' beginning from the pixel at 'x'.
/// Gets a <see cref="BufferSpan{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// </summary>
/// <param name="x">The x coordinate</param>
/// <param name="y">The y coordinate</param>
/// <returns>The <see cref="BufferPointer{TColor}"/></returns>
internal BufferPointer<TColor> GetRowPointer(int x, int y)
/// <returns>The <see cref="BufferSpan{T}"/></returns>
internal BufferSpan<TColor> GetRowPointer(int x, int y)
{
return this.pixelBuffer.Slice((y * this.Width) + x);
}
@ -288,8 +288,8 @@ namespace ImageSharp
{
for (int y = 0; y < height; y++)
{
BufferPointer<byte> source = area.GetRowPointer(y);
BufferPointer<TColor> destination = this.GetRowPointer(targetX, targetY + y);
BufferSpan<byte> source = area.GetRowPointer(y);
BufferSpan<TColor> destination = this.GetRowPointer(targetX, targetY + y);
Operations.PackFromZyxBytes(source, destination, width);
}
@ -308,8 +308,8 @@ namespace ImageSharp
{
for (int y = 0; y < height; y++)
{
BufferPointer<byte> source = area.GetRowPointer(y);
BufferPointer<TColor> destination = this.GetRowPointer(targetX, targetY + y);
BufferSpan<byte> source = area.GetRowPointer(y);
BufferSpan<TColor> destination = this.GetRowPointer(targetX, targetY + y);
Operations.PackFromZyxwBytes(source, destination, width);
}
@ -328,8 +328,8 @@ namespace ImageSharp
{
for (int y = 0; y < height; y++)
{
BufferPointer<byte> source = area.GetRowPointer(y);
BufferPointer<TColor> destination = this.GetRowPointer(targetX, targetY + y);
BufferSpan<byte> source = area.GetRowPointer(y);
BufferSpan<TColor> destination = this.GetRowPointer(targetX, targetY + y);
Operations.PackFromXyzBytes(source, destination, width);
}
@ -348,8 +348,8 @@ namespace ImageSharp
{
for (int y = 0; y < height; y++)
{
BufferPointer<byte> source = area.GetRowPointer(y);
BufferPointer<TColor> destination = this.GetRowPointer(targetX, targetY + y);
BufferSpan<byte> source = area.GetRowPointer(y);
BufferSpan<TColor> destination = this.GetRowPointer(targetX, targetY + y);
Operations.PackFromXyzwBytes(source, destination, width);
}
}
@ -367,8 +367,8 @@ namespace ImageSharp
{
for (int y = 0; y < height; y++)
{
BufferPointer<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferPointer<byte> destination = area.GetRowPointer(y);
BufferSpan<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferSpan<byte> destination = area.GetRowPointer(y);
Operations.ToZyxBytes(source, destination, width);
}
}
@ -386,8 +386,8 @@ namespace ImageSharp
{
for (int y = 0; y < height; y++)
{
BufferPointer<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferPointer<byte> destination = area.GetRowPointer(y);
BufferSpan<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferSpan<byte> destination = area.GetRowPointer(y);
Operations.ToZyxwBytes(source, destination, width);
}
}
@ -405,8 +405,8 @@ namespace ImageSharp
{
for (int y = 0; y < height; y++)
{
BufferPointer<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferPointer<byte> destination = area.GetRowPointer(y);
BufferSpan<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferSpan<byte> destination = area.GetRowPointer(y);
Operations.ToXyzBytes(source, destination, width);
}
}
@ -424,8 +424,8 @@ namespace ImageSharp
{
for (int y = 0; y < height; y++)
{
BufferPointer<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferPointer<byte> destination = area.GetRowPointer(y);
BufferSpan<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferSpan<byte> destination = area.GetRowPointer(y);
Operations.ToXyzwBytes(source, destination, width);
}
}

6
src/ImageSharp/Image/PixelArea{TColor}.cs

@ -202,11 +202,11 @@ namespace ImageSharp
}
/// <summary>
/// Gets a <see cref="BufferPointer{Byte}"/> to the row y.
/// Gets a <see cref="BufferSpan{T}"/> to the row y.
/// </summary>
/// <param name="y">The y coordinate</param>
/// <returns>The <see cref="BufferPointer{Byte}"/></returns>
internal BufferPointer<byte> GetRowPointer(int y)
/// <returns>The <see cref="BufferSpan{T}"/></returns>
internal BufferSpan<byte> GetRowPointer(int y)
{
return this.byteBuffer.Slice(y * this.RowStride);
}

8
tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs

@ -314,8 +314,8 @@ namespace ImageSharp.Tests.Colors
public PinnedBuffer<TDest> ActualDestBuffer { get; }
public PinnedBuffer<TDest> ExpectedDestBuffer { get; }
public BufferPointer<TSource> Source => this.SourceBuffer.Slice();
public BufferPointer<TDest> ActualDest => this.ActualDestBuffer.Slice();
public BufferSpan<TSource> Source => this.SourceBuffer.Slice();
public BufferSpan<TDest> ActualDest => this.ActualDestBuffer.Slice();
public TestBuffers(TSource[] source, TDest[] expectedDest)
{
@ -335,7 +335,7 @@ namespace ImageSharp.Tests.Colors
public void Verify()
{
int count = this.ExpectedDestBuffer.Count;
int count = this.ExpectedDestBuffer.Length;
if (typeof(TDest) == typeof(Vector4))
{
@ -364,7 +364,7 @@ namespace ImageSharp.Tests.Colors
internal static void TestOperation<TSource, TDest>(
TSource[] source,
TDest[] expected,
Action<BufferPointer<TSource>, BufferPointer<TDest>> action)
Action<BufferSpan<TSource>, BufferSpan<TDest>> action)
where TSource : struct
where TDest : struct
{

184
tests/ImageSharp.Tests/Common/BufferPointerTests.cs → tests/ImageSharp.Tests/Common/BufferSpanTests.cs

@ -7,7 +7,7 @@ namespace ImageSharp.Tests.Common
using Xunit;
public unsafe class BufferPointerTests
public unsafe class BufferSpanTests
{
public struct Foo
{
@ -70,68 +70,122 @@ namespace ImageSharp.Tests.Common
using (PinnedBuffer<Foo> colorBuf = new PinnedBuffer<Foo>(fooz))
{
BufferPointer<Foo> orig = colorBuf.Slice(1);
BufferPointer<byte> asBytes = (BufferPointer < byte > )orig;
BufferSpan<Foo> orig = colorBuf.Slice(1);
BufferSpan<byte> asBytes = (BufferSpan < byte > )orig;
Assert.Equal(asBytes.Offset, sizeof(Foo));
Assert.Equal(asBytes.Start, sizeof(Foo));
Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset);
}
}
[Fact]
public void ConstructWithoutOffset()
public class Construct
{
Foo[] array = Foo.CreateArray(3);
fixed (Foo* p = array)
[Fact]
public void Basic()
{
// Act:
BufferPointer<Foo> ap = new BufferPointer<Foo>(array, p);
Foo[] array = Foo.CreateArray(3);
fixed (Foo* p = array)
{
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p);
// Assert:
Assert.Equal(array, ap.Array);
Assert.Equal((IntPtr)p, ap.PointerAtOffset);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal((IntPtr)p, span.PointerAtOffset);
Assert.Equal(3, span.Length);
}
}
}
[Fact]
public void ConstructWithOffset()
{
Foo[] array = Foo.CreateArray(3);
int offset = 2;
fixed (Foo* p = array)
[Fact]
public void WithStart()
{
// Act:
BufferPointer<Foo> ap = new BufferPointer<Foo>(array, p, offset);
Foo[] array = Foo.CreateArray(4);
int start = 2;
fixed (Foo* p = array)
{
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(start, span.Start);
Assert.Equal((IntPtr)(p + start), span.PointerAtOffset);
Assert.Equal(array.Length - start, span.Length);
}
}
// Assert:
Assert.Equal(array, ap.Array);
Assert.Equal(offset, ap.Offset);
Assert.Equal((IntPtr)(p+offset), ap.PointerAtOffset);
[Fact]
public void WithStartAndLength()
{
Foo[] array = Foo.CreateArray(10);
int start = 2;
int length = 3;
fixed (Foo* p = array)
{
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start, length);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(start, span.Start);
Assert.Equal((IntPtr)(p + start), span.PointerAtOffset);
Assert.Equal(length, span.Length);
}
}
}
[Fact]
public void Slice()
public class Slice
{
Foo[] array = Foo.CreateArray(5);
int offset0 = 2;
int offset1 = 2;
int totalOffset = offset0 + offset1;
fixed (Foo* p = array)
[Fact]
public void StartOnly()
{
BufferPointer<Foo> ap = new BufferPointer<Foo>(array, p, offset0);
Foo[] array = Foo.CreateArray(5);
int start0 = 2;
int start1 = 2;
int totalOffset = start0 + start1;
// Act:
ap = ap.Slice(offset1);
fixed (Foo* p = array)
{
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start0);
// Act:
span = span.Slice(start1);
// Assert:
Assert.Equal(array, ap.Array);
Assert.Equal(totalOffset, ap.Offset);
Assert.Equal((IntPtr)(p + totalOffset), ap.PointerAtOffset);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(totalOffset, span.Start);
Assert.Equal((IntPtr)(p + totalOffset), span.PointerAtOffset);
Assert.Equal(array.Length - totalOffset, span.Length);
}
}
[Fact]
public void StartAndLength()
{
Foo[] array = Foo.CreateArray(10);
int start0 = 2;
int start1 = 2;
int totalOffset = start0 + start1;
int sliceLength = 3;
fixed (Foo* p = array)
{
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start0);
// Act:
span = span.Slice(start1, sliceLength);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(totalOffset, span.Start);
Assert.Equal((IntPtr)(p + totalOffset), span.PointerAtOffset);
Assert.Equal(sliceLength, span.Length);
}
}
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
@ -142,7 +196,7 @@ namespace ImageSharp.Tests.Common
int offset = 2;
fixed (Foo* p = array)
{
BufferPointer<Foo> ap = new BufferPointer<Foo>(array, p, offset);
BufferSpan<Foo> ap = new BufferSpan<Foo>(array, p, offset);
// Act:
ap.Clear(count);
@ -194,10 +248,10 @@ namespace ImageSharp.Tests.Common
fixed (Foo* pSource = source)
fixed (Foo* pDest = dest)
{
BufferPointer<Foo> apSource = new BufferPointer<Foo>(source, pSource, 1);
BufferPointer<Foo> apDest = new BufferPointer<Foo>(dest, pDest, 1);
BufferSpan<Foo> apSource = new BufferSpan<Foo>(source, pSource, 1);
BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest, pDest, 1);
BufferPointer.Copy(apSource, apDest, count-1);
BufferSpan.Copy(apSource, apDest, count-1);
}
AssertNotDefault(source, 1);
@ -221,10 +275,10 @@ namespace ImageSharp.Tests.Common
fixed (AlignedFoo* pSource = source)
fixed (AlignedFoo* pDest = dest)
{
BufferPointer<AlignedFoo> apSource = new BufferPointer<AlignedFoo>(source, pSource, 1);
BufferPointer<AlignedFoo> apDest = new BufferPointer<AlignedFoo>(dest, pDest, 1);
BufferSpan<AlignedFoo> apSource = new BufferSpan<AlignedFoo>(source, pSource, 1);
BufferSpan<AlignedFoo> apDest = new BufferSpan<AlignedFoo>(dest, pDest, 1);
BufferPointer.Copy(apSource, apDest, count - 1);
BufferSpan.Copy(apSource, apDest, count - 1);
}
AssertNotDefault(source, 1);
@ -248,10 +302,10 @@ namespace ImageSharp.Tests.Common
fixed (int* pSource = source)
fixed (int* pDest = dest)
{
BufferPointer<int> apSource = new BufferPointer<int>(source, pSource, 1);
BufferPointer<int> apDest = new BufferPointer<int>(dest, pDest, 1);
BufferSpan<int> apSource = new BufferSpan<int>(source, pSource, 1);
BufferSpan<int> apDest = new BufferSpan<int>(dest, pDest, 1);
BufferPointer.Copy(apSource, apDest, count -1);
BufferSpan.Copy(apSource, apDest, count -1);
}
AssertNotDefault(source, 1);
@ -276,10 +330,10 @@ namespace ImageSharp.Tests.Common
fixed (Foo* pSource = source)
fixed (byte* pDest = dest)
{
BufferPointer<Foo> apSource = new BufferPointer<Foo>(source, pSource, 1);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest, sizeof(Foo));
BufferSpan<Foo> apSource = new BufferSpan<Foo>(source, pSource, 1);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest, sizeof(Foo));
BufferPointer.Copy(apSource, apDest, count - 1);
BufferSpan.Copy(apSource, apDest, count - 1);
}
AssertNotDefault(source, 1);
@ -303,10 +357,10 @@ namespace ImageSharp.Tests.Common
fixed (AlignedFoo* pSource = source)
fixed (byte* pDest = dest)
{
BufferPointer<AlignedFoo> apSource = new BufferPointer<AlignedFoo>(source, pSource, 1);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest, sizeof(AlignedFoo));
BufferSpan<AlignedFoo> apSource = new BufferSpan<AlignedFoo>(source, pSource, 1);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest, sizeof(AlignedFoo));
BufferPointer.Copy(apSource, apDest, count - 1);
BufferSpan.Copy(apSource, apDest, count - 1);
}
AssertNotDefault(source, 1);
@ -330,10 +384,10 @@ namespace ImageSharp.Tests.Common
fixed (int* pSource = source)
fixed (byte* pDest = dest)
{
BufferPointer<int> apSource = new BufferPointer<int>(source, pSource);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest);
BufferSpan<int> apSource = new BufferSpan<int>(source, pSource);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest);
BufferPointer.Copy(apSource, apDest, count);
BufferSpan.Copy(apSource, apDest, count);
}
AssertNotDefault(source, 1);
@ -355,10 +409,10 @@ namespace ImageSharp.Tests.Common
fixed(byte* pSource = source)
fixed (Foo* pDest = dest)
{
BufferPointer<byte> apSource = new BufferPointer<byte>(source, pSource);
BufferPointer<Foo> apDest = new BufferPointer<Foo>(dest, pDest);
BufferSpan<byte> apSource = new BufferSpan<byte>(source, pSource);
BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest, pDest);
BufferPointer.Copy(apSource, apDest, count);
BufferSpan.Copy(apSource, apDest, count);
}
AssertNotDefault(source, sizeof(Foo) + 1);
@ -378,11 +432,11 @@ namespace ImageSharp.Tests.Common
using (PinnedBuffer<Color> colorBuf = new PinnedBuffer<Color>(colors))
using (PinnedBuffer<byte> byteBuf = new PinnedBuffer<byte>(colors.Length*4))
{
BufferPointer.Copy<Color>(colorBuf, byteBuf, colorBuf.Count);
BufferSpan.Copy<Color>(colorBuf, byteBuf, colorBuf.Length);
byte[] a = byteBuf.Array;
for (int i = 0; i < byteBuf.Count; i++)
for (int i = 0; i < byteBuf.Length; i++)
{
Assert.Equal((byte)i, a[i]);
}

10
tests/ImageSharp.Tests/Common/PinnedBufferTests.cs

@ -25,7 +25,7 @@
{
Assert.False(buffer.IsDisposedOrLostArrayOwnership);
Assert.NotNull(buffer.Array);
Assert.Equal(count, buffer.Count);
Assert.Equal(count, buffer.Length);
Assert.True(buffer.Array.Length >= count);
VerifyPointer(buffer);
@ -42,7 +42,7 @@
{
Assert.False(buffer.IsDisposedOrLostArrayOwnership);
Assert.Equal(array, buffer.Array);
Assert.Equal(count, buffer.Count);
Assert.Equal(count, buffer.Length);
VerifyPointer(buffer);
}
@ -71,7 +71,7 @@
{
using (PinnedBuffer<int> buffer = PinnedBuffer<int>.CreateClean(42))
{
for (int j = 0; j < buffer.Count; j++)
for (int j = 0; j < buffer.Length; j++)
{
Assert.Equal(0, buffer.Array[j]);
buffer.Array[j] = 666;
@ -96,10 +96,10 @@
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a))
{
BufferPointer<Foo> arrayPtr = buffer.Slice();
BufferSpan<Foo> arrayPtr = buffer.Slice();
Assert.Equal(a, arrayPtr.Array);
Assert.Equal(0, arrayPtr.Offset);
Assert.Equal(0, arrayPtr.Start);
Assert.Equal(buffer.Pointer, arrayPtr.PointerAtOffset);
}
}

Loading…
Cancel
Save