forked from dotnet/runtime
-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathCopyTo.cs
More file actions
205 lines (168 loc) · 7.09 KB
/
Copy pathCopyTo.cs
File metadata and controls
205 lines (168 loc) · 7.09 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
197
198
199
200
201
202
203
204
205
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Xunit;
namespace System.SpanTests
{
public static partial class ReadOnlySpanTests
{
[Fact]
public static void TryCopyTo()
{
int[] src = { 1, 2, 3 };
int[] dst = { 99, 100, 101 };
ReadOnlySpan<int> srcSpan = new ReadOnlySpan<int>(src);
bool success = srcSpan.TryCopyTo(dst);
Assert.True(success);
Assert.Equal<int>(src, dst);
}
[Fact]
public static void TryCopyToSingle()
{
int[] src = { 1 };
int[] dst = { 99 };
ReadOnlySpan<int> srcSpan = new ReadOnlySpan<int>(src);
bool success = srcSpan.TryCopyTo(dst);
Assert.True(success);
Assert.Equal<int>(src, dst);
}
[Fact]
public static void TryCopyToArraySegmentImplicit()
{
int[] src = { 1, 2, 3 };
int[] dst = { 5, 99, 100, 101, 10 };
var segment = new ArraySegment<int>(dst, 1, 3);
ReadOnlySpan<int> srcSpan = new ReadOnlySpan<int>(src);
bool success = srcSpan.TryCopyTo(segment);
Assert.True(success);
Assert.Equal(src.AsSpan(), segment);
}
[Fact]
public static void TryCopyToEmpty()
{
int[] src = { };
int[] dst = { 99, 100, 101 };
ReadOnlySpan<int> srcSpan = new ReadOnlySpan<int>(src);
bool success = srcSpan.TryCopyTo(dst);
Assert.True(success);
int[] expected = { 99, 100, 101 };
Assert.Equal<int>(expected, dst);
}
[Fact]
public static void TryCopyToLonger()
{
int[] src = { 1, 2, 3 };
int[] dst = { 99, 100, 101, 102 };
ReadOnlySpan<int> srcSpan = new ReadOnlySpan<int>(src);
bool success = srcSpan.TryCopyTo(dst);
Assert.True(success);
int[] expected = { 1, 2, 3, 102 };
Assert.Equal<int>(expected, dst);
}
[Fact]
public static void TryCopyToShorter()
{
int[] src = { 1, 2, 3 };
int[] dst = { 99, 100 };
ReadOnlySpan<int> srcSpan = new ReadOnlySpan<int>(src);
bool success = srcSpan.TryCopyTo(dst);
Assert.False(success);
int[] expected = { 99, 100 };
Assert.Equal<int>(expected, dst); // TryCopyTo() checks for sufficient space before doing any copying.
}
[Fact]
public static void CopyToShorter()
{
int[] src = { 1, 2, 3 };
int[] dst = { 99, 100 };
ReadOnlySpan<int> srcSpan = new ReadOnlySpan<int>(src);
TestHelpers.AssertThrows<ArgumentException, int>(srcSpan, (_srcSpan) => _srcSpan.CopyTo(dst));
int[] expected = { 99, 100 };
Assert.Equal<int>(expected, dst); // CopyTo() checks for sufficient space before doing any copying.
}
[Fact]
public static void Overlapping1()
{
int[] a = { 90, 91, 92, 93, 94, 95, 96, 97 };
ReadOnlySpan<int> src = new ReadOnlySpan<int>(a, 1, 6);
Span<int> dst = new Span<int>(a, 2, 6);
src.CopyTo(dst);
int[] expected = { 90, 91, 91, 92, 93, 94, 95, 96 };
Assert.Equal<int>(expected, a);
}
[Fact]
public static void Overlapping2()
{
int[] a = { 90, 91, 92, 93, 94, 95, 96, 97 };
ReadOnlySpan<int> src = new ReadOnlySpan<int>(a, 2, 6);
Span<int> dst = new Span<int>(a, 1, 6);
src.CopyTo(dst);
int[] expected = { 90, 92, 93, 94, 95, 96, 97, 97 };
Assert.Equal<int>(expected, a);
}
// Verifies that ReadOnlySpan.CopyTo does not truncate the byte count when it exceeds
// uint.MaxValue. For a >4GB span, the element count times sizeof(T) must stay a 64-bit value
// all the way down to the native memmove; a 32-bit truncation would copy far fewer bytes. A
// single overlapping buffer is used so only one >4GB block is needed instead of two.
[Theory]
[OuterLoop]
[PlatformSpecific(TestPlatforms.Windows | TestPlatforms.OSX)]
[InlineData(4L * 1024L * 1024L * 1024L)]
public static unsafe void CopyToLargeSizeTest(long bufferSize)
{
// If this test is run in a 32-bit process, the large allocation will fail.
if (sizeof(IntPtr) != sizeof(long))
{
return;
}
int guidCount = (int)(bufferSize / sizeof(Guid));
const int Gap = 1; // Offset source and destination so the copy is a real move, not a self-copy.
if (!AllocationHelper.TryAllocNative((IntPtr)(bufferSize + (Gap * sizeof(Guid))), out IntPtr memBlock))
{
return;
}
try
{
var buffer = new Span<Guid>((void*)memBlock, guidCount + Gap);
Span<Guid> source = buffer.Slice(0, guidCount);
Span<Guid> destination = buffer.Slice(Gap, guidCount);
Guid fill = Guid.Parse("900DBAD9-00DB-AD90-00DB-AD900DBADBAD");
Guid tail = Guid.Parse("2B2B2B2B-2B2B-2B2B-2B2B-2B2B2B2B2B2B");
// Only the boundaries are initialized and checked. Filling all 4GB would add a full
// extra memory pass on top of the copy for no additional coverage.
source[0] = fill;
source[guidCount - 1] = tail;
destination[guidCount - 1] = fill; // A truncated copy leaves this as 'fill' rather than 'tail'.
((ReadOnlySpan<Guid>)source).CopyTo(destination);
Assert.Equal(fill, destination[0]);
Assert.Equal(tail, destination[guidCount - 1]);
}
finally
{
AllocationHelper.ReleaseNative(ref memBlock);
}
}
[Fact]
public static void CopyToVaryingSizes()
{
const int MaxLength = 2048;
var rng = new Random();
byte[] inputArray = new byte[MaxLength];
ReadOnlySpan<byte> inputSpan = inputArray;
Span<byte> outputSpan = new byte[MaxLength];
Span<byte> allZerosSpan = new byte[MaxLength];
// Test all inputs from size 0 .. MaxLength (inclusive) to make sure we don't have
// gaps in our Memmove logic.
for (int i = 0; i <= MaxLength; i++)
{
// Arrange
rng.NextBytes(inputArray);
outputSpan.Clear();
// Act
inputSpan.Slice(0, i).CopyTo(outputSpan);
// Assert
Assert.True(inputSpan.Slice(0, i).SequenceEqual(outputSpan.Slice(0, i))); // src successfully copied to dst
Assert.True(outputSpan.Slice(i).SequenceEqual(allZerosSpan.Slice(i))); // no other part of dst was overwritten
}
}
}
}