-
Notifications
You must be signed in to change notification settings - Fork 383
Expand file tree
/
Copy pathReadOnlyMemoryExtensions.cs
More file actions
196 lines (181 loc) · 10.7 KB
/
ReadOnlyMemoryExtensions.cs
File metadata and controls
196 lines (181 loc) · 10.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Buffers;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using CommunityToolkit.HighPerformance.Buffers.Internals;
using CommunityToolkit.HighPerformance.Buffers.Internals.Interfaces;
using MemoryStream = CommunityToolkit.HighPerformance.Streams.MemoryStream;
namespace CommunityToolkit.HighPerformance;
/// <summary>
/// Helpers for working with the <see cref="ReadOnlyMemory{T}"/> type.
/// </summary>
public static class ReadOnlyMemoryExtensions
{
#if NETSTANDARD2_1_OR_GREATER
/// <summary>
/// Returns a <see cref="ReadOnlyMemory2D{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="height">The height of the resulting 2D area.</param>
/// <param name="width">The width of each row in the resulting 2D area.</param>
/// <returns>The resulting <see cref="ReadOnlyMemory2D{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 area is outside of bounds for <paramref name="memory"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlyMemory2D<T> AsMemory2D<T>(this ReadOnlyMemory<T> memory, int height, int width)
{
return new(memory, height, width);
}
/// <summary>
/// Returns a <see cref="ReadOnlyMemory2D{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="height">The height of the resulting 2D area.</param>
/// <param name="width">The width of each row in the resulting 2D area.</param>
/// <param name="pitch">The pitch in the resulting 2D area.</param>
/// <returns>The resulting <see cref="ReadOnlyMemory2D{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 area is outside of bounds for <paramref name="memory"/>.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlyMemory2D<T> AsMemory2D<T>(this ReadOnlyMemory<T> memory, int offset, int height, int width, int pitch)
{
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>
/// Casts a <see cref="ReadOnlyMemory{T}"/> of one primitive type <typeparamref name="T"/> to <see cref="ReadOnlyMemory{T}"/> of bytes.
/// </summary>
/// <typeparam name="T">The type if items in the source <see cref="ReadOnlyMemory{T}"/>.</typeparam>
/// <param name="memory">The source <see cref="ReadOnlyMemory{T}"/>, of type <typeparamref name="T"/>.</param>
/// <returns>A <see cref="ReadOnlyMemory{T}"/> of bytes.</returns>
/// <exception cref="OverflowException">
/// Thrown if the <see cref="ReadOnlyMemory{T}.Length"/> property of the new <see cref="ReadOnlyMemory{T}"/> would exceed <see cref="int.MaxValue"/>.
/// </exception>
/// <exception cref="ArgumentException">Thrown when the data store of <paramref name="memory"/> is not supported.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlyMemory<byte> AsBytes<T>(this ReadOnlyMemory<T> memory)
where T : unmanaged
{
return Cast<T, byte>(memory);
}
/// <summary>
/// Casts a <see cref="ReadOnlyMemory{T}"/> of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
/// </summary>
/// <typeparam name="TFrom">The type of items in the source <see cref="ReadOnlyMemory{T}"/>.</typeparam>
/// <typeparam name="TTo">The type of items in the destination <see cref="ReadOnlyMemory{T}"/>.</typeparam>
/// <param name="memory">The source <see cref="ReadOnlyMemory{T}"/>, of type <typeparamref name="TFrom"/>.</param>
/// <returns>A <see cref="ReadOnlyMemory{T}"/> of type <typeparamref name="TTo"/></returns>
/// <exception cref="ArgumentException">Thrown when the data store of <paramref name="memory"/> is not supported.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ReadOnlyMemory<TTo> Cast<TFrom, TTo>(this ReadOnlyMemory<TFrom> memory)
where TFrom : unmanaged
where TTo : unmanaged
{
if (memory.IsEmpty)
{
return default;
}
if (typeof(TFrom) == typeof(char) &&
MemoryMarshal.TryGetString((ReadOnlyMemory<char>)(object)memory, out string? text, out int start, out int length))
{
return new StringMemoryManager<TTo>(text!, start, length).Memory;
}
if (MemoryMarshal.TryGetArray(memory, out ArraySegment<TFrom> segment))
{
return new ArrayMemoryManager<TFrom, TTo>(segment.Array!, segment.Offset, segment.Count).Memory;
}
if (MemoryMarshal.TryGetMemoryManager(memory, out MemoryManager<TFrom>? memoryManager, out start, out length))
{
// If the memory manager is the one resulting from a previous cast, we can use it directly to retrieve
// a new manager for the target type that wraps the original data store, instead of creating one that
// wraps the current manager. This ensures that doing repeated casts always results in only up to one
// indirection level in the chain of memory managers needed to access the target data buffer to use.
if (memoryManager is IMemoryManager wrappingManager)
{
return wrappingManager.GetMemory<TTo>(start, length);
}
return new ProxyMemoryManager<TFrom, TTo>(memoryManager!, start, length).Memory;
}
// Throws when the memory instance has an unsupported backing store
static ReadOnlyMemory<TTo> ThrowArgumentExceptionForUnsupportedMemory()
{
throw new ArgumentException("The input instance doesn't have a supported underlying data store.");
}
return ThrowArgumentExceptionForUnsupportedMemory();
}
/// <summary>
/// Returns a <see cref="Stream"/> wrapping the contents of the given <see cref="ReadOnlyMemory{T}"/> of <see cref="byte"/> instance.
/// </summary>
/// <param name="memory">The input <see cref="ReadOnlyMemory{T}"/> of <see cref="byte"/> instance.</param>
/// <returns>A <see cref="Stream"/> wrapping the data within <paramref name="memory"/>.</returns>
/// <remarks>
/// Since this method only receives a <see cref="Memory{T}"/> instance, which does not track
/// the lifetime of its underlying buffer, it is responsibility of the caller to manage that.
/// In particular, the caller must ensure that the target buffer is not disposed as long
/// as the returned <see cref="Stream"/> is in use, to avoid unexpected issues.
/// </remarks>
/// <exception cref="ArgumentException">Thrown when <paramref name="memory"/> has an invalid data store.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Stream AsStream(this ReadOnlyMemory<byte> memory)
{
return MemoryStream.Create(memory, true);
}
}