Skip to content

Commit 6abe41c

Browse files
authored
Merge pull request #18 from rameel/memorymarshal-view-extensions
MemoryMarshal for unsafe/unchecked StringView/ArrayView creation
2 parents 03dc23b + 71ba80e commit 6abe41c

4 files changed

Lines changed: 172 additions & 2 deletions

File tree

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
namespace Ramstack.InteropServices;
2+
3+
[TestFixture]
4+
public class MemoryMarshalTests
5+
{
6+
[Test]
7+
public void CreateStringView()
8+
{
9+
Assert.That(
10+
MemoryMarshal.CreateStringView("Hello, World!", 7).ToString(),
11+
Is.EqualTo("World!"));
12+
13+
Assert.That(
14+
MemoryMarshal.CreateStringView("Hello, World!", 7).Length,
15+
Is.EqualTo(6));
16+
17+
Assert.That(
18+
MemoryMarshal.CreateStringView("Hello, World!", 0, 5).ToString(),
19+
Is.EqualTo("Hello"));
20+
21+
Assert.That(
22+
MemoryMarshal.CreateStringView("Hello, World!", 0, 5).Length,
23+
Is.EqualTo(5));
24+
}
25+
26+
[Test]
27+
public void CreateArrayView()
28+
{
29+
var array = "Hello, World!".ToArray();
30+
31+
Assert.That(
32+
MemoryMarshal.CreateArrayView(array, 7).AsSpan().ToString(),
33+
Is.EqualTo("World!"));
34+
35+
Assert.That(
36+
MemoryMarshal.CreateArrayView(array, 7).Length,
37+
Is.EqualTo(6));
38+
39+
Assert.That(
40+
MemoryMarshal.CreateArrayView(array, 0, 5).AsSpan().ToString(),
41+
Is.EqualTo("Hello"));
42+
43+
Assert.That(
44+
MemoryMarshal.CreateArrayView(array, 0, 5).Length,
45+
Is.EqualTo(5));
46+
}
47+
}

Ramstack.Structures/Collections/ArrayViewExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static ArrayView<T> AsView<T>(this T[]? value, int index) =>
3434
/// starting at a specified position for a specified number of elements.
3535
/// </summary>
3636
/// <typeparam name="T">The type of elements in the array.</typeparam>
37-
/// <param name="value">The array to create a view over. Can be null.</param>
37+
/// <param name="value">The array to create a view over.</param>
3838
/// <param name="index">The zero-based starting position of the view in the array.</param>
3939
/// <param name="count">The number of elements to include in the view.</param>
4040
/// <returns>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
using Ramstack.Collections;
2+
using Ramstack.Text;
3+
4+
namespace Ramstack.InteropServices;
5+
6+
/// <summary>
7+
/// Provides extension methods for the <see cref="MemoryMarshal"/> class.
8+
/// </summary>
9+
public static class MemoryMarshalExtensions
10+
{
11+
extension(MemoryMarshal)
12+
{
13+
/// <summary>
14+
/// Creates a new <see cref="StringView"/> over a portion of the specified string
15+
/// starting at a specified position to the end of the string.
16+
/// </summary>
17+
/// <param name="value">The string to create a view over.</param>
18+
/// <param name="index">The zero-based starting position of the view in the string.</param>
19+
/// <returns>
20+
/// A <see cref="StringView"/> representing the specified portion of the string.
21+
/// </returns>
22+
/// <remarks>
23+
/// This method should be used with caution as it doesn't perform argument validation.
24+
/// The caller is responsible for ensuring:
25+
/// <list type="bullet">
26+
/// <item><description><paramref name="value"/> is not null.</description></item>
27+
/// <item><description><paramref name="index"/> is within the bounds of the string.</description></item>
28+
/// </list>
29+
/// </remarks>
30+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
31+
public static StringView CreateStringView(string value, int index)
32+
{
33+
Debug.Assert(value is not null);
34+
Debug.Assert(value.AsSpan(index).Length == value.Length - index);
35+
36+
return new StringView(value, index, value.Length - index, unused: 0);
37+
}
38+
39+
/// <summary>
40+
/// Creates a new <see cref="StringView"/> over a portion of the specified string
41+
/// starting at a specified position for a specified number of characters.
42+
/// </summary>
43+
/// <param name="value">The string to create a view over.</param>
44+
/// <param name="index">The zero-based starting position of the view in the string.</param>
45+
/// <param name="length">The number of characters to include in the view.</param>
46+
/// <returns>
47+
/// A <see cref="StringView"/> representing the specified portion of the string.
48+
/// </returns>
49+
/// <remarks>
50+
/// This method should be used with caution as it doesn't perform argument validation.
51+
/// The caller is responsible for ensuring:
52+
/// <list type="bullet">
53+
/// <item><description><paramref name="value"/> is not null.</description></item>
54+
/// <item><description><paramref name="index"/> is within the bounds of the string.</description></item>
55+
/// <item><description><paramref name="length"/> is valid for the specified index.</description></item>
56+
/// </list>
57+
/// </remarks>
58+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
59+
public static StringView CreateStringView(string value, int index, int length)
60+
{
61+
Debug.Assert(value is not null);
62+
Debug.Assert(value.AsSpan(index, length).Length == length);
63+
64+
return new StringView(value, index, length, unused: 0);
65+
}
66+
67+
/// <summary>
68+
/// Creates a new <see cref="ArrayView{T}"/> over a portion of the specified array,
69+
/// starting at a specified position to the end of the array.
70+
/// </summary>
71+
/// <typeparam name="T">The type of elements in the array.</typeparam>
72+
/// <param name="array">The array to create a view over.</param>
73+
/// <param name="index">The zero-based starting position of the view in the array.</param>
74+
/// <returns>
75+
/// An <see cref="ArrayView{T}"/> representing the specified portion of the array.
76+
/// </returns>
77+
/// <remarks>
78+
/// This method should be used with caution as it doesn't perform argument validation.
79+
/// The caller is responsible for ensuring:
80+
/// <list type="bullet">
81+
/// <item><description><paramref name="array"/> is not null.</description></item>
82+
/// <item><description><paramref name="index"/> is within the bounds of the array.</description></item>
83+
/// </list>
84+
/// </remarks>
85+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
86+
public static ArrayView<T> CreateArrayView<T>(T[] array, int index)
87+
{
88+
Debug.Assert(array is not null);
89+
Debug.Assert(array.AsSpan(index).Length == array.Length - index);
90+
91+
return new ArrayView<T>(array, index, array.Length - index, unused: 0);
92+
}
93+
94+
/// <summary>
95+
/// Creates a new <see cref="ArrayView{T}"/> over a portion of the specified array
96+
/// starting at a specified position for a specified number of elements.
97+
/// </summary>
98+
/// <typeparam name="T">The type of elements in the array.</typeparam>
99+
/// <param name="array">The array to create a view over.</param>
100+
/// <param name="index">The zero-based starting position of the view in the array.</param>
101+
/// <param name="length">The number of elements to include in the view.</param>
102+
/// <returns>
103+
/// An <see cref="ArrayView{T}"/> representing the specified portion of the array.
104+
/// </returns>
105+
/// <remarks>
106+
/// This method should be used with caution as it doesn't perform argument validation.
107+
/// The caller is responsible for ensuring:
108+
/// <list type="bullet">
109+
/// <item><description><paramref name="array"/> is not null.</description></item>
110+
/// <item><description><paramref name="index"/> is within the bounds of the array.</description></item>
111+
/// <item><description><paramref name="length"/> is valid for the specified index.</description></item>
112+
/// </list>
113+
/// </remarks>
114+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
115+
public static ArrayView<T> CreateArrayView<T>(T[] array, int index, int length)
116+
{
117+
Debug.Assert(array is not null);
118+
Debug.Assert(array.AsSpan(index, length).Length == length);
119+
120+
return new ArrayView<T>(array, index, length, unused: 0);
121+
}
122+
}
123+
}

Ramstack.Structures/Text/StringView.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public StringView(string value, int index, int length)
107107
/// <param name="length">The number of characters to include in the view.</param>
108108
/// <param name="unused">Unused parameter used only to differentiate between overloads.</param>
109109
[MethodImpl(MethodImplOptions.AggressiveInlining)]
110-
private StringView(string value, int index, int length, int unused)
110+
internal StringView(string value, int index, int length, int unused)
111111
{
112112
_index = index;
113113
_length = length;

0 commit comments

Comments
 (0)