Skip to content
338 changes: 337 additions & 1 deletion src/CommunityToolkit.HighPerformance/Extensions/ArrayExtensions.3D.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,51 @@ public static Memory2D<T> AsMemory2D<T>(this Memory<T> memory, int offset, int h
{
return new(memory, offset, height, width, pitch);
}

/// <summary>
/// Returns a <see cref="Memory3D{T}"/> instance wrapping the underlying data for the given <see cref="Memory{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items in the input <see cref="Memory{T}"/> instance.</typeparam>
/// <param name="memory">The input <see cref="Memory{T}"/> instance.</param>
/// <param name="depth">The depth of the resulting 3D volume.</param>
/// <param name="height">The height of each slice in the resulting 3D volume.</param>
/// <param name="width">The width of each row in the resulting 3D volume.</param>
/// <returns>The resulting <see cref="Memory3D{T}"/> instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the input parameters is out of range.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the requested volume is outside of bounds for <paramref name="memory"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Memory3D<T> AsMemory3D<T>(this Memory<T> memory, int depth, int height, int width)
{
return new(memory, depth, height, width);
}

/// <summary>
/// Returns a <see cref="Memory3D{T}"/> instance wrapping the underlying data for the given <see cref="Memory{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items in the input <see cref="Memory{T}"/> instance.</typeparam>
/// <param name="memory">The input <see cref="Memory{T}"/> instance.</param>
/// <param name="offset">The initial offset within <paramref name="memory"/>.</param>
/// <param name="depth">The depth of the resulting 3D volume.</param>
/// <param name="height">The height of each slice in the resulting 3D volume.</param>
/// <param name="width">The width of each row in the resulting 3D volume.</param>
/// <param name="slicePitch">The pitch of each slice in the resulting 3D volume (distance between slices).</param>
/// <param name="rowPitch">The pitch of each row in the resulting 3D volume (distance between rows).</param>
/// <returns>The resulting <see cref="Memory3D{T}"/> instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the input parameters is out of range.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the requested volume is outside of bounds for <paramref name="memory"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Memory3D<T> AsMemory3D<T>(this Memory<T> memory, int offset, int depth, int height, int width, int slicePitch, int rowPitch)
{
return new(memory, offset, depth, height, width, slicePitch, rowPitch);
}
#endif

/// <summary>
Expand Down Expand Up @@ -109,4 +154,4 @@ public static Stream AsStream(this Memory<byte> memory)
{
return MemoryStream.Create(memory, false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,51 @@ public static ReadOnlyMemory2D<T> AsMemory2D<T>(this ReadOnlyMemory<T> memory, i
{
return new(memory, offset, height, width, pitch);
}

/// <summary>
/// Returns a <see cref="ReadOnlyMemory3D{T}"/> instance wrapping the underlying data for the given <see cref="ReadOnlyMemory{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items in the input <see cref="ReadOnlyMemory{T}"/> instance.</typeparam>
/// <param name="memory">The input <see cref="ReadOnlyMemory{T}"/> instance.</param>
/// <param name="depth">The depth of the resulting 3D area.</param>
/// <param name="height">The height of each slice in the resulting 3D area.</param>
/// <param name="width">The width of each row in the resulting 3D area.</param>
/// <returns>The resulting <see cref="ReadOnlyMemory3D{T}"/> instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the input parameters is out of range.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the requested volume is outside of bounds for <paramref name="memory"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlyMemory3D<T> AsMemory3D<T>(this ReadOnlyMemory<T> memory, int depth, int height, int width)
{
return new(memory, depth, height, width);
}

/// <summary>
/// Returns a <see cref="ReadOnlyMemory3D{T}"/> instance wrapping the underlying data for the given <see cref="ReadOnlyMemory{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items in the input <see cref="ReadOnlyMemory{T}"/> instance.</typeparam>
/// <param name="memory">The input <see cref="ReadOnlyMemory{T}"/> instance.</param>
/// <param name="offset">The initial offset within <paramref name="memory"/>.</param>
/// <param name="depth">The depth of the resulting 3D area.</param>
/// <param name="height">The height of each slice in the resulting 3D area.</param>
/// <param name="width">The width of each row in the resulting 3D area.</param>
/// <param name="slicePitch">The slice pitch in the resulting 3D area (distance between slices).</param>
/// <param name="rowPitch">The row pitch in the resulting 3D area (distance between rows).</param>
/// <returns>The resulting <see cref="ReadOnlyMemory3D{T}"/> instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the input parameters is out of range.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the requested volume is outside of bounds for <paramref name="memory"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlyMemory3D<T> AsMemory3D<T>(this ReadOnlyMemory<T> memory, int offset, int depth, int height, int width, int slicePitch, int rowPitch)
{
return new(memory, offset, depth, height, width, slicePitch, rowPitch);
}
#endif

/// <summary>
Expand Down Expand Up @@ -148,4 +193,4 @@ public static Stream AsStream(this ReadOnlyMemory<byte> memory)
{
return MemoryStream.Create(memory, true);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,51 @@ public static ReadOnlySpan2D<T> AsSpan2D<T>(this ReadOnlySpan<T> span, int offse
{
return new(span, offset, height, width, pitch);
}

/// <summary>
/// Returns a <see cref="ReadOnlySpan3D{T}"/> instance wrapping the underlying data for the given <see cref="ReadOnlySpan{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items in the input <see cref="ReadOnlySpan{T}"/> instance.</typeparam>
/// <param name="span">The input <see cref="ReadOnlySpan{T}"/> instance.</param>
/// <param name="depth">The depth of the resulting 3D volume.</param>
/// <param name="height">The height of each slice in the resulting 3D volume.</param>
/// <param name="width">The width of each row in the resulting 3D volume.</param>
/// <returns>The resulting <see cref="ReadOnlySpan3D{T}"/> instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the input parameters is out of range.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the requested volume is outside of bounds for <paramref name="span"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan3D<T> AsSpan3D<T>(this ReadOnlySpan<T> span, int depth, int height, int width)
{
return new(span, depth, height, width);
}

/// <summary>
/// Returns a <see cref="ReadOnlySpan3D{T}"/> instance wrapping the underlying data for the given <see cref="ReadOnlySpan{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items in the input <see cref="ReadOnlySpan{T}"/> instance.</typeparam>
/// <param name="span">The input <see cref="ReadOnlySpan{T}"/> instance.</param>
/// <param name="offset">The initial offset within <paramref name="span"/>.</param>
/// <param name="depth">The depth of the resulting 3D volume.</param>
/// <param name="height">The height of each slice in the resulting 3D volume.</param>
/// <param name="width">The width of each row in the resulting 3D volume.</param>
/// <param name="slicePitch">The slice pitch in the resulting 3D volume (distance between slices).</param>
/// <param name="rowPitch">The row pitch in the resulting 3D volume (distance between rows).</param>
/// <returns>The resulting <see cref="ReadOnlySpan3D{T}"/> instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the input parameters is out of range.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the requested volume is outside of bounds for <paramref name="span"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlySpan3D<T> AsSpan3D<T>(this ReadOnlySpan<T> span, int offset, int depth, int height, int width, int slicePitch, int rowPitch)
{
return new(span, offset, depth, height, width, slicePitch, rowPitch);
}
#endif

/// <summary>
Expand Down Expand Up @@ -383,4 +428,4 @@ public static bool TryCopyTo<T>(this ReadOnlySpan<T> span, RefEnumerable<T> dest
{
return destination.TryCopyFrom(span);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,51 @@ public static Span2D<T> AsSpan2D<T>(this Span<T> span, int offset, int height, i
{
return new(span, offset, height, width, pitch);
}

/// <summary>
/// Returns a <see cref="Span3D{T}"/> instance wrapping the underlying data for the given <see cref="Span{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items in the input <see cref="Span{T}"/> instance.</typeparam>
/// <param name="span">The input <see cref="Span{T}"/> instance.</param>
/// <param name="depth">The depth of the resulting 3D volume.</param>
/// <param name="height">The height of each slice in the resulting 3D volume.</param>
/// <param name="width">The width of each row in the resulting 3D volume.</param>
/// <returns>The resulting <see cref="Span3D{T}"/> instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the input parameters is out of range.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the requested volume is outside of bounds for <paramref name="span"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span3D<T> AsSpan3D<T>(this Span<T> span, int depth, int height, int width)
{
return new(span, depth, height, width);
}

/// <summary>
/// Returns a <see cref="Span3D{T}"/> instance wrapping the underlying data for the given <see cref="Span{T}"/> instance.
/// </summary>
/// <typeparam name="T">The type of items in the input <see cref="Span{T}"/> instance.</typeparam>
/// <param name="span">The input <see cref="Span{T}"/> instance.</param>
/// <param name="offset">The initial offset within <paramref name="span"/>.</param>
/// <param name="depth">The depth of the resulting 3D volume.</param>
/// <param name="height">The height of each slice in the resulting 3D volume.</param>
/// <param name="width">The width of each row in the resulting 3D volume.</param>
/// <param name="slicePitch">The slice pitch in the resulting 3D volume (distance between slices).</param>
/// <param name="rowPitch">The row pitch in the resulting 3D volume (distance between rows).</param>
/// <returns>The resulting <see cref="Span3D{T}"/> instance.</returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when one of the input parameters is out of range.
/// </exception>
/// <exception cref="ArgumentException">
/// Thrown when the requested volume is outside of bounds for <paramref name="span"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Span3D<T> AsSpan3D<T>(this Span<T> span, int offset, int depth, int height, int width, int slicePitch, int rowPitch)
{
return new(span, offset, depth, height, width, slicePitch, rowPitch);
}
#endif

/// <summary>
Expand Down Expand Up @@ -280,4 +325,4 @@ public static bool TryCopyTo<T>(this Span<T> span, RefEnumerable<T> destination)
{
return destination.TryCopyFrom(span);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
namespace CommunityToolkit.HighPerformance.Memory.Internals;

/// <summary>
/// A helper to validate arithmetic operations for <see cref="Memory2D{T}"/> and <see cref="Span2D{T}"/>.
/// A helper to validate arithmetic operations for <see cref="Memory2D{T}"/>, <see cref="Span2D{T}"/>,
/// <see cref="Memory3D{T}"/>, and <see cref="Span3D{T}"/>.
/// </summary>
internal static class OverflowHelper
{
Expand Down Expand Up @@ -66,4 +67,48 @@ public static int ComputeInt32Area(int height, int width, int pitch)
{
return Max(checked(((width + pitch) * (height - 1)) + width), 0);
}
}

/// <summary>
/// Ensures that the input parameters will not exceed the maximum native int value when indexing.
/// </summary>
/// <param name="height">The height of the 3D memory area to map.</param>
/// <param name="width">The width of the 3D memory area to map.</param>
/// <param name="depth">The depth of the 3D memory area to map.</param>
/// <param name="rowPitch">The row pitch of the 3D memory area (the distance between each row).</param>
/// <param name="slicePitch">The slice pitch of the 3D memory area (the distance between each 2D slice).</param>
/// <exception cref="OverflowException">Throw when the inputs don't fit in the expected range.</exception>
/// <remarks>The input parameters are assumed to always be positive.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void EnsureIsInNativeIntRange(int depth, int height, int width, int slicePitch, int rowPitch)
{
// Refer to the explanation above for the Memory2D<T> and Span2D<T> types.
// For the Memory3D<T> and Span3D<T> types it is similar, except we now have a "volume"
// consisting of one or more 2D slices. For these 2D slices, rowPitch is the distance
// between the end of a row and the start of the next row. Similarly, slicePitch is
// the distance between the end of a slice and the start of the next slice.
// Note that we're also subtracting 1 to the depth as we don't want to include the trailing pitch
// for the 3D memory area.
_ = checked(((nint)(((width + rowPitch) * height) + slicePitch) * Max(unchecked(depth - 1), 0)) +
((nint)(width + rowPitch) * Max(unchecked(height - 1), 0)) +
Max(unchecked(width - 1), 0));
}

/// <summary>
/// Ensures that the input parameters will not exceed <see cref="int.MaxValue"/> when indexing.
/// </summary>
/// <param name="height">The height of the 3D memory area to map.</param>
/// <param name="width">The width of the 3D memory area to map.</param>
/// <param name="depth">The depth of the 3D memory area to map.</param>
/// <param name="rowPitch">The row pitch of the 3D memory area (the distance between each row).</param>
/// <param name="slicePitch">The slice pitch of the 3D memory area (the distance between each 2D slice).</param>
/// <returns>The volume resulting from the given parameters.</returns>
/// <exception cref="OverflowException">Throw when the inputs don't fit in the expected range.</exception>
/// <remarks>The input parameters are assumed to always be positive.</remarks>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ComputeInt32Volume(int depth, int height, int width, int slicePitch, int rowPitch)
{
return Max(checked(((width + rowPitch) * height + slicePitch) * (depth - 1) +
(width + rowPitch) * (height - 1) +
width), 0);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@ public static void ThrowArgumentOutOfRangeExceptionForDepth()
{
throw new ArgumentOutOfRangeException("depth");
}

/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the "slice" parameter is invalid.
/// </summary>
public static void ThrowArgumentOutOfRangeExceptionForSlice()
{
throw new ArgumentOutOfRangeException("slice");
}

/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the "row" parameter is invalid.
Expand Down Expand Up @@ -126,4 +134,20 @@ public static void ThrowArgumentOutOfRangeExceptionForPitch()
{
throw new ArgumentOutOfRangeException("pitch");
}

/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the "rowPitch" parameter is invalid.
/// </summary>
public static void ThrowArgumentOutOfRangeExceptionForRowPitch()
{
throw new ArgumentOutOfRangeException("rowPitch");
}

/// <summary>
/// Throws an <see cref="ArgumentOutOfRangeException"/> when the "slicePitch" parameter is invalid.
/// </summary>
public static void ThrowArgumentOutOfRangeExceptionForSlicePitch()
{
throw new ArgumentOutOfRangeException("slicePitch");
}
}
Loading