Skip to content

Commit f9f76d9

Browse files
authored
Span<T> F# snippets (#7851)
1 parent 5d52752 commit f9f76d9

File tree

11 files changed

+279
-0
lines changed

11 files changed

+279
-0
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module Program
2+
3+
open System
4+
open System.Threading.Tasks
5+
6+
let array = Array.zeroCreate<byte> 5
7+
8+
let clearContents () =
9+
Task.Delay(20).Wait()
10+
lock array (fun () ->
11+
Array.Clear(array, 0, array.Length) )
12+
13+
let enumerateSpan (span: Span<byte>) =
14+
for element in span do
15+
printfn $"{element}"
16+
Task.Delay(10).Wait()
17+
18+
[<EntryPoint>]
19+
let main _ =
20+
Random(42).NextBytes array
21+
printfn "%A" array
22+
let span: Span<byte> = array
23+
24+
Task.Run clearContents |> ignore
25+
26+
enumerateSpan span
27+
28+
0
29+
30+
// The example displays output like the following:
31+
// 62
32+
// 23
33+
// 186
34+
// 0
35+
// 0
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
module Program2
2+
3+
open System
4+
open System.Threading
5+
open System.Threading.Tasks
6+
7+
let array = Array.zeroCreate<byte> 5
8+
9+
// <Snippet1>
10+
let enumerateSpan (span: Span<byte>) =
11+
// Spans cannot be accessed in closures including in the F# lock function.
12+
// Monitor.Enter and Monitor.Exit are used here directly.
13+
Monitor.Enter array
14+
try
15+
for element in span do
16+
printfn $"{element}"
17+
Task.Delay(10).Wait()
18+
finally
19+
Monitor.Exit array
20+
// The example displays the following output:
21+
// 62
22+
// 23
23+
// 186
24+
// 150
25+
// 174
26+
// </Snippet1>
27+
28+
let clearContents () =
29+
Task.Delay(20).Wait()
30+
lock array (fun () ->
31+
Array.Clear(array, 0, array.Length) )
32+
33+
[<EntryPoint>]
34+
let main _ =
35+
Random(42).NextBytes array
36+
let span: Span<byte> = array
37+
38+
Task.Run clearContents |> ignore
39+
40+
enumerateSpan span
41+
42+
0
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net6.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Compile Include="..\Program2.fs" />
9+
</ItemGroup>
10+
</Project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net6.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Compile Include="Program.fs" />
9+
</ItemGroup>
10+
</Project>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net6.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Compile Include="program.fs" />
9+
</ItemGroup>
10+
</Project>
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
open System
2+
open System.Runtime.InteropServices
3+
4+
let createSpanFromArray () =
5+
// <Snippet1>
6+
// Create a span over an array.
7+
let array = Array.zeroCreate<byte> 100
8+
let arraySpan = Span<byte> array
9+
10+
let mutable data = 0uy
11+
for i = 0 to arraySpan.Length - 1 do
12+
arraySpan[i] <- data
13+
data <- data + 1uy
14+
15+
let mutable arraySum = 0
16+
for value in array do
17+
arraySum <- arraySum + int value
18+
19+
printfn $"The sum is {arraySum}"
20+
// Output: The sum is 4950
21+
// </Snippet1>
22+
23+
let createSpanFromNativeMemory () =
24+
// <Snippet2>
25+
// Create a span from native memory.
26+
let native = Marshal.AllocHGlobal 100
27+
let nativeSpan = Span<byte>(native.ToPointer(), 100)
28+
29+
let mutable data = 0uy
30+
for i = 0 to nativeSpan.Length - 1 do
31+
nativeSpan[i] <- data
32+
data <- data + 1uy
33+
34+
let mutable nativeSum = 0
35+
for value in nativeSpan do
36+
nativeSum <- nativeSum + int value
37+
38+
printfn $"The sum is {nativeSum}"
39+
Marshal.FreeHGlobal native
40+
// Output: The sum is 4950
41+
// </Snippet2>
42+
43+
let createSpanFromStack () =
44+
// <Snippet3>
45+
// Create a span on the stack.
46+
let mutable data = 0uy
47+
let stackSpan =
48+
let p = NativeInterop.NativePtr.stackalloc<byte> 100 |> NativeInterop.NativePtr.toVoidPtr
49+
Span<byte>(p, 100)
50+
51+
for i = 0 to stackSpan.Length - 1 do
52+
stackSpan[i] <- data
53+
data <- data + 1uy
54+
55+
let mutable stackSum = 0
56+
for value in stackSpan do
57+
stackSum <- stackSum + int value
58+
59+
printfn $"The sum is {stackSum}"
60+
// Output: The sum is 4950
61+
// </Snippet3>
62+
63+
module Program2 =
64+
// <Snippet4>
65+
open System
66+
open System.Runtime.InteropServices
67+
open FSharp.NativeInterop
68+
69+
// Package FSharp.NativeInterop.NativePtr.stackalloc for reuse.
70+
let inline stackalloc<'a when 'a: unmanaged> length : Span<'a> =
71+
let voidPointer = NativePtr.stackalloc<'a> length |> NativePtr.toVoidPtr
72+
Span<'a>(voidPointer, length)
73+
74+
let initializeSpan (span: Span<byte>) =
75+
let mutable value = 0uy
76+
for i = 0 to span.Length - 1 do
77+
span[i] <- value
78+
value <- value + 1uy
79+
80+
let computeSum (span: Span<byte>) =
81+
let mutable sum = 0
82+
for value in span do
83+
sum <- sum + int value
84+
sum
85+
86+
let workWithSpans () =
87+
// Create a span over an array.
88+
let array = Array.zeroCreate<byte> 100
89+
let arraySpan = Span<byte> array
90+
91+
initializeSpan arraySpan
92+
printfn $"The sum is {computeSum arraySpan:N0}"
93+
94+
// Create an array from native memory.
95+
let native = Marshal.AllocHGlobal 100
96+
let nativeSpan = Span<byte>(native.ToPointer(), 100)
97+
98+
initializeSpan nativeSpan
99+
printfn $"The sum is {computeSum nativeSpan:N0}"
100+
101+
Marshal.FreeHGlobal native
102+
103+
// Create a span on the stack.
104+
let stackSpan = stackalloc 100
105+
106+
initializeSpan stackSpan
107+
printfn $"The sum is {computeSum stackSpan:N0}"
108+
109+
// The example displays the following output:
110+
// The sum is 4,950
111+
// The sum is 4,950
112+
// The sum is 4,950
113+
// </Snippet4>
114+
115+
createSpanFromArray ()
116+
printfn "-----"
117+
createSpanFromNativeMemory ()
118+
printfn "-----"
119+
createSpanFromStack ()
120+
printfn "-----"
121+
Program2.workWithSpans ()
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module Program
2+
3+
open System
4+
5+
[<EntryPoint>]
6+
let main _ =
7+
let array = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
8+
let slice = Span<int>(array, 2, 5)
9+
for i = 0 to slice.Length - 1 do
10+
slice[i] <- slice[i] * 2
11+
12+
// Examine the original array values.
13+
for value in array do
14+
printf $"{value} "
15+
printfn ""
16+
0
17+
// The example displays the following output:
18+
// 2 4 12 16 20 24 28 16 18 20
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module Program2
2+
3+
open System
4+
5+
let getContentLength (span: ReadOnlySpan<char>) =
6+
let slice = span.Slice 16
7+
Int32.Parse slice
8+
9+
let contentLength = "Content-Length: 132"
10+
let length = getContentLength (contentLength.ToCharArray())
11+
printfn $"Content length: {length}"
12+
// Output:
13+
// Content length: 132
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
<PropertyGroup>
3+
<OutputType>Exe</OutputType>
4+
<TargetFramework>net6.0</TargetFramework>
5+
</PropertyGroup>
6+
7+
<ItemGroup>
8+
<Compile Include="Program2.fs" />
9+
<Compile Include="Program.fs" />
10+
</ItemGroup>
11+
</Project>

xml/System/Span`1+Enumerator.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,12 @@ If <xref:System.Span%601.Enumerator.MoveNext%2A> passes the end of the <xref:Sys
5757
The enumerator does not have exclusive access to the <xref:System.Span%601>. In addition, the underlying data on which the span is based can also be modified. Therefore, enumerating through a span is intrinsically not a thread-safe procedure. To guarantee thread safety during enumeration, you must implement your own synchronization. For example, the following code has a race condition. It does not ensure that the span will be enumerated before the `ClearContents` method executes. As a result, the underlying array is cleared during enumeration of the span:
5858
5959
:::code language="csharp" source="~/snippets/csharp/System/Span.Enumerator/Program.cs":::
60+
:::code language="fsharp" source="~/snippets/fsharp/System/Span.Enumerator/Program.fs":::
6061
6162
If you synchronize access to the array before enumerating the span, as the revised version of the `EnumerateSpan` method does in the following example, the `ClearContents` method doesn't modify underlying span data during enumeration. Note that the example locks the underlying array on which the span is based.
6263
6364
:::code language="csharp" source="~/snippets/csharp/System/Span.Enumerator/Program2.cs" id="Snippet1":::
65+
:::code language="fsharp" source="~/snippets/fsharp/System/Span.Enumerator/Program2.fs" id="Snippet1":::
6466
6567
Unlike some other enumerator structures in .NET, the <xref:System.Span%601.Enumerator>:
6668

0 commit comments

Comments
 (0)