Skip to content

Commit 7977eda

Browse files
authored
Add new constructors to Span<T> and ReadOnlySpan<T> (#260)
***NO_CI***
1 parent 2a79b7a commit 7977eda

File tree

5 files changed

+628
-11
lines changed

5 files changed

+628
-11
lines changed

Tests/NFUnitTestTypes/NFUnitTestTypes.nfproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<ItemGroup>
2727
<Compile Include="UnitTestGuid.cs" />
2828
<Compile Include="UnitTestObjectTypeTests.cs" />
29+
<Compile Include="UnitTestsReadOnlySpanByte.cs" />
2930
<Compile Include="UnitTestsSpanByte.cs" />
3031
<Compile Include="UnitTestSubTypeTests.cs" />
3132
<Compile Include="UnitTestValueArrayTypess.cs" />
Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using nanoFramework.TestFramework;
6+
7+
namespace NFUnitTestTypes
8+
{
9+
[TestClass]
10+
public class UnitTestsReadOnlySpanByte
11+
{
12+
[TestMethod]
13+
public void EmptySpanTests()
14+
{
15+
// Empty span using default constructor
16+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>();
17+
18+
Assert.AreEqual(0, span.Length, "Empty ReadOnlySpan should have length of 0");
19+
Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
20+
21+
// Empty span from null array
22+
span = new ReadOnlySpan<byte>(null);
23+
24+
Assert.AreEqual(0, span.Length, "ReadOnlySpan from null should have length of 0");
25+
Assert.IsTrue(span.IsEmpty, "ReadOnlySpan from null should be IsEmpty");
26+
27+
// Empty span using Empty property
28+
span = ReadOnlySpan<byte>.Empty;
29+
30+
Assert.AreEqual(0, span.Length, "Empty ReadOnlySpan should have length of 0");
31+
Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
32+
}
33+
34+
[TestMethod]
35+
public void RaisingExceptionsOfAllKindsTests()
36+
{
37+
// Should raise an exception on creation
38+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(null, 1, 2); }, "ArgumentOutOfRangeException when array is null, start is 1 and length is 2");
39+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[1], 1, 2); }, "ArgumentOutOfRangeException when array is new byte[1], start is 1 and length is 2");
40+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[1], 0, 2); }, "ArgumentOutOfRangeException when array is new byte[1], start is 0 and length is 2");
41+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () => { ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[1], 2, 0); }, "ArgumentOutOfRangeException when array is new byte[1], start is 2 and length is 0");
42+
43+
// Exception on index access
44+
byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
45+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
46+
{
47+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
48+
_ = span[span.Length];
49+
});
50+
51+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
52+
{
53+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
54+
_ = span[-1];
55+
});
56+
57+
// Slicing arguments
58+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
59+
{
60+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
61+
_ = span.Slice(span.Length + 1);
62+
});
63+
64+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
65+
{
66+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
67+
_ = span.Slice(1, span.Length);
68+
});
69+
70+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
71+
{
72+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
73+
_ = span.Slice(-1, span.Length);
74+
});
75+
76+
Assert.ThrowsException(typeof(ArgumentOutOfRangeException), () =>
77+
{
78+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
79+
_ = span.Slice(1, -1);
80+
});
81+
}
82+
83+
[TestMethod]
84+
public void ToArrayTest()
85+
{
86+
byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
87+
88+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
89+
90+
byte[] toArray = span.ToArray();
91+
92+
CollectionAssert.AreEqual(array, toArray, "Original array and ReadOnlySpan.ToArray should be the same");
93+
}
94+
95+
[TestMethod]
96+
public void ConstructorsOfAllKindsTests()
97+
{
98+
// Empty span using default constructor
99+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>();
100+
101+
Assert.AreEqual(span.Length, 0, "Empty ReadOnlySpan should have length of 0");
102+
Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
103+
104+
// Empty span from null with zero start and length
105+
span = new ReadOnlySpan<byte>(null, 0, 0);
106+
107+
Assert.AreEqual(span.Length, 0, "Empty ReadOnlySpan should have length of 0");
108+
Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
109+
110+
// Empty span using Empty property
111+
span = ReadOnlySpan<byte>.Empty;
112+
113+
Assert.AreEqual(span.Length, 0, "Empty ReadOnlySpan should have length of 0");
114+
Assert.IsTrue(span.IsEmpty, "Empty ReadOnlySpan should be IsEmpty");
115+
116+
// ReadOnlySpan from normal array
117+
byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
118+
span = new ReadOnlySpan<byte>(array);
119+
120+
Assert.AreEqual(span.Length, array.Length, $"ReadOnlySpan should have length of the array it takes: {array.Length}");
121+
Assert.IsFalse(span.IsEmpty, "ReadOnlySpan should NOT be IsEmpty");
122+
123+
// ReadOnlySpan from normal array with different start and length
124+
span = new ReadOnlySpan<byte>(array, 2, 8);
125+
126+
Assert.AreEqual(span.Length, 8, $"ReadOnlySpan should have length of 8");
127+
Assert.IsFalse(span.IsEmpty, "ReadOnlySpan should NOT be IsEmpty");
128+
}
129+
130+
[TestMethod]
131+
public void SliceTests()
132+
{
133+
// ReadOnlySpan from normal array
134+
byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
135+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
136+
137+
// Slice 2 elements and check
138+
ReadOnlySpan<byte> sliced = span.Slice(0, 2);
139+
140+
Assert.AreEqual(sliced.Length, 2, "Sliced span length must be 2");
141+
Assert.AreEqual(sliced[0], (byte)0x00, "Sliced first element must be value 0");
142+
Assert.AreEqual(sliced[1], (byte)0x01, "Sliced second element must be value 1");
143+
144+
// Slice 4 elements starting at index 2 and check
145+
sliced = span.Slice(2, 4);
146+
147+
Assert.AreEqual(sliced.Length, 4, "Sliced span length must be 4");
148+
Assert.AreEqual(sliced[0], (byte)0x02, "Sliced first element must be value 2");
149+
Assert.AreEqual(sliced[1], (byte)0x03, "Sliced second element must be value 3");
150+
Assert.AreEqual(sliced[2], (byte)0x04, "Sliced third element must be value 4");
151+
Assert.AreEqual(sliced[3], (byte)0x05, "Sliced fourth element must be value 5");
152+
153+
// Slice starting at element 4 and check
154+
sliced = span.Slice(4);
155+
156+
Assert.AreEqual(sliced.Length, 12, "Sliced span length must be 12");
157+
158+
for (int i = 0; i < sliced.Length; i++)
159+
{
160+
Assert.AreEqual(sliced[i], span[i + 4], "ReadOnlySpan value should be the same as from the original span");
161+
}
162+
163+
// Slice of slice
164+
ReadOnlySpan<byte> secondSliced = sliced.Slice(2, 4);
165+
166+
Assert.AreEqual(secondSliced.Length, 4, "Sliced span length must be 4");
167+
168+
for (int i = 0; i < secondSliced.Length; i++)
169+
{
170+
Assert.AreEqual(secondSliced[i], sliced[i + 2], "ReadOnlySpan value should be the same as from the original span");
171+
}
172+
173+
// Should be an empty one
174+
ReadOnlySpan<byte> empty = span.Slice(span.Length);
175+
176+
Assert.AreEqual(empty.Length, 0, "Slicing all the span should result in an empty span");
177+
Assert.IsTrue(empty.IsEmpty, "Empty span should be empty");
178+
}
179+
180+
[TestMethod]
181+
public void GetElementsTests()
182+
{
183+
// ReadOnlySpan from normal array
184+
byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
185+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
186+
for (int i = 0; i < span.Length; i++)
187+
{
188+
Assert.AreEqual(span[i], array[i], "ReadOnlySpan value should be the same as from the original array");
189+
}
190+
191+
// Partial span
192+
span = new ReadOnlySpan<byte>(array, 2, 8);
193+
for (int i = 0; i < span.Length; i++)
194+
{
195+
Assert.AreEqual(span[i], array[i + 2], "ReadOnlySpan value should be the same as from the original array");
196+
}
197+
}
198+
199+
[TestMethod]
200+
public void StackAllocReadOnlySpanTests()
201+
{
202+
// Create a ReadOnlySpan from stack-allocated memory
203+
ReadOnlySpan<byte> span = stackalloc byte[16];
204+
205+
Assert.AreEqual(16, span.Length, "Stack-allocated ReadOnlySpan should have length of 16");
206+
Assert.IsFalse(span.IsEmpty, "Stack-allocated ReadOnlySpan should NOT be IsEmpty");
207+
208+
// Verify all elements are initialized to zero
209+
for (int i = 0; i < span.Length; i++)
210+
{
211+
Assert.AreEqual((byte)0, span[i], "Stack-allocated ReadOnlySpan elements should be initialized to 0");
212+
}
213+
}
214+
215+
[TestMethod]
216+
public void StackAllocReadOnlySpanWithInitializerTests()
217+
{
218+
// Create a ReadOnlySpan from stack-allocated memory with initializer
219+
ReadOnlySpan<byte> span = stackalloc byte[8] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
220+
221+
Assert.AreEqual(8, span.Length, "Stack-allocated ReadOnlySpan with initializer should have length of 8");
222+
Assert.IsFalse(span.IsEmpty, "Stack-allocated ReadOnlySpan should NOT be IsEmpty");
223+
224+
// Verify the initialized values
225+
for (int i = 0; i < span.Length; i++)
226+
{
227+
Assert.AreEqual((byte)(i + 1), span[i], $"Stack-allocated ReadOnlySpan element at index {i} should be {i + 1}");
228+
}
229+
}
230+
231+
[TestMethod]
232+
public void StackAllocReadOnlySpanSliceTests()
233+
{
234+
// Create a ReadOnlySpan from stack-allocated memory
235+
ReadOnlySpan<byte> span = stackalloc byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
236+
237+
// Slice the stack-allocated span
238+
ReadOnlySpan<byte> sliced = span.Slice(4, 8);
239+
240+
Assert.AreEqual(8, sliced.Length, "Sliced stack-allocated ReadOnlySpan should have length of 8");
241+
242+
for (int i = 0; i < sliced.Length; i++)
243+
{
244+
Assert.AreEqual((byte)(i + 4), sliced[i], $"Sliced element at index {i} should be {i + 4}");
245+
}
246+
}
247+
248+
[TestMethod]
249+
public void StackAllocReadOnlySpanToArrayTests()
250+
{
251+
// Create a ReadOnlySpan from stack-allocated memory
252+
ReadOnlySpan<byte> span = stackalloc byte[6] { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
253+
254+
// Convert to array
255+
byte[] array = span.ToArray();
256+
257+
Assert.AreEqual(6, array.Length, "ToArray should create an array with the same length");
258+
259+
byte[] expected = new byte[] { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
260+
CollectionAssert.AreEqual(expected, array, "Stack-allocated ReadOnlySpan ToArray should match expected values");
261+
}
262+
263+
[TestMethod]
264+
public void StackAllocEmptyReadOnlySpanTests()
265+
{
266+
// Create an empty stack-allocated ReadOnlySpan
267+
ReadOnlySpan<byte> span = stackalloc byte[0];
268+
269+
Assert.AreEqual(0, span.Length, "Empty stack-allocated ReadOnlySpan should have length of 0");
270+
Assert.IsTrue(span.IsEmpty, "Empty stack-allocated ReadOnlySpan should be IsEmpty");
271+
}
272+
273+
[TestMethod]
274+
public void ImplicitConversionFromArrayTests()
275+
{
276+
// Test implicit conversion from array to ReadOnlySpan
277+
byte[] array = new byte[8] { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 };
278+
279+
ReadOnlySpan<byte> span = array; // Implicit conversion
280+
281+
Assert.AreEqual(array.Length, span.Length, "Implicitly converted ReadOnlySpan should have same length as array");
282+
283+
for (int i = 0; i < array.Length; i++)
284+
{
285+
Assert.AreEqual(array[i], span[i], $"Element at index {i} should match");
286+
}
287+
}
288+
289+
[TestMethod]
290+
public void EqualityOperatorTests()
291+
{
292+
byte[] array1 = new byte[4] { 0x01, 0x02, 0x03, 0x04 };
293+
byte[] array2 = new byte[4] { 0x01, 0x02, 0x03, 0x04 };
294+
byte[] array3 = new byte[4] { 0x01, 0x02, 0x03, 0x05 };
295+
296+
ReadOnlySpan<byte> span1 = new ReadOnlySpan<byte>(array1);
297+
ReadOnlySpan<byte> span2 = new ReadOnlySpan<byte>(array2);
298+
ReadOnlySpan<byte> span3 = new ReadOnlySpan<byte>(array3);
299+
300+
// Test equality with same content
301+
Assert.IsTrue(span1 == span2, "ReadOnlySpans with same content should be equal");
302+
303+
// Test inequality with different content
304+
Assert.IsTrue(span1 != span3, "ReadOnlySpans with different content should not be equal");
305+
Assert.IsFalse(span1 == span3, "ReadOnlySpans with different content should not be equal");
306+
307+
// Test empty spans
308+
ReadOnlySpan<byte> empty1 = new ReadOnlySpan<byte>();
309+
ReadOnlySpan<byte> empty2 = new ReadOnlySpan<byte>();
310+
311+
Assert.IsTrue(empty1 == empty2, "Empty ReadOnlySpans should be equal");
312+
}
313+
314+
[TestMethod]
315+
public void ReadOnlyBehaviorTests()
316+
{
317+
// Verify that ReadOnlySpan protects the underlying data from modification through the span
318+
byte[] array = new byte[4] { 0x01, 0x02, 0x03, 0x04 };
319+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array);
320+
321+
// We can read values
322+
Assert.AreEqual((byte)0x01, span[0], "Should be able to read from ReadOnlySpan");
323+
324+
// The following would not compile (which is the desired behavior):
325+
// span[0] = 0xFF; // Error: Cannot assign to a readonly ref return
326+
327+
// But we can still modify the underlying array directly
328+
array[0] = 0xFF;
329+
Assert.AreEqual((byte)0xFF, span[0], "Modifying underlying array should be visible through ReadOnlySpan");
330+
}
331+
332+
[TestMethod]
333+
public void PartialSpanTests()
334+
{
335+
byte[] array = new byte[16] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F };
336+
337+
// Create ReadOnlySpan that covers middle portion of array
338+
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(array, 4, 8);
339+
340+
Assert.AreEqual(8, span.Length, "Partial ReadOnlySpan should have correct length");
341+
Assert.AreEqual((byte)0x04, span[0], "First element should be from start index");
342+
Assert.AreEqual((byte)0x0B, span[7], "Last element should be at start + length - 1");
343+
344+
// Verify all elements in the partial span
345+
for (int i = 0; i < span.Length; i++)
346+
{
347+
Assert.AreEqual(array[i + 4], span[i], $"Element at index {i} should match array element at {i + 4}");
348+
}
349+
}
350+
}
351+
}

0 commit comments

Comments
 (0)