Thanks to visit codestin.com
Credit goes to github.com

Skip to content

System.Span<T> F# snippets #7851

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions snippets/fsharp/System/Span.Enumerator/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Program

open System
open System.Threading.Tasks

let array = Array.zeroCreate<byte> 5

let clearContents () =
Task.Delay(20).Wait()
lock array (fun () ->
Array.Clear(array, 0, array.Length) )

let enumerateSpan (span: Span<byte>) =
for element in span do
printfn $"{element}"
Task.Delay(10).Wait()

[<EntryPoint>]
let main _ =
Random(42).NextBytes array
printfn "%A" array
let span: Span<byte> = array

Task.Run clearContents |> ignore

enumerateSpan span

0

// The example displays output like the following:
// 62
// 23
// 186
// 0
// 0
42 changes: 42 additions & 0 deletions snippets/fsharp/System/Span.Enumerator/Program2.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
module Program2

open System
open System.Threading
open System.Threading.Tasks

let array = Array.zeroCreate<byte> 5

// <Snippet1>
let enumerateSpan (span: Span<byte>) =
// Spans cannot be accessed in closures including in the F# lock function.
// Monitor.Enter and Monitor.Exit are used here directly.
Monitor.Enter array
try
for element in span do
printfn $"{element}"
Task.Delay(10).Wait()
finally
Monitor.Exit array
// The example displays the following output:
// 62
// 23
// 186
// 150
// 174
// </Snippet1>

let clearContents () =
Task.Delay(20).Wait()
lock array (fun () ->
Array.Clear(array, 0, array.Length) )

[<EntryPoint>]
let main _ =
Random(42).NextBytes array
let span: Span<byte> = array

Task.Run clearContents |> ignore

enumerateSpan span

0
10 changes: 10 additions & 0 deletions snippets/fsharp/System/Span.Enumerator/Program2/fs.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="..\Program2.fs" />
</ItemGroup>
</Project>
10 changes: 10 additions & 0 deletions snippets/fsharp/System/Span.Enumerator/fs.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
10 changes: 10 additions & 0 deletions snippets/fsharp/System/Span/Overview/fs.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="program.fs" />
</ItemGroup>
</Project>
121 changes: 121 additions & 0 deletions snippets/fsharp/System/Span/Overview/program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
open System
open System.Runtime.InteropServices

let createSpanFromArray () =
// <Snippet1>
// Create a span over an array.
let array = Array.zeroCreate<byte> 100
let arraySpan = Span<byte> array

let mutable data = 0uy
for i = 0 to arraySpan.Length - 1 do
arraySpan[i] <- data
data <- data + 1uy

let mutable arraySum = 0
for value in array do
arraySum <- arraySum + int value

printfn $"The sum is {arraySum}"
// Output: The sum is 4950
// </Snippet1>

let createSpanFromNativeMemory () =
// <Snippet2>
// Create a span from native memory.
let native = Marshal.AllocHGlobal 100
let nativeSpan = Span<byte>(native.ToPointer(), 100)

let mutable data = 0uy
for i = 0 to nativeSpan.Length - 1 do
nativeSpan[i] <- data
data <- data + 1uy

let mutable nativeSum = 0
for value in nativeSpan do
nativeSum <- nativeSum + int value

printfn $"The sum is {nativeSum}"
Marshal.FreeHGlobal native
// Output: The sum is 4950
// </Snippet2>

let createSpanFromStack () =
// <Snippet3>
// Create a span on the stack.
let mutable data = 0uy
let stackSpan =
let p = NativeInterop.NativePtr.stackalloc<byte> 100 |> NativeInterop.NativePtr.toVoidPtr
Span<byte>(p, 100)

for i = 0 to stackSpan.Length - 1 do
stackSpan[i] <- data
data <- data + 1uy

let mutable stackSum = 0
for value in stackSpan do
stackSum <- stackSum + int value

printfn $"The sum is {stackSum}"
// Output: The sum is 4950
// </Snippet3>

module Program2 =
// <Snippet4>
open System
open System.Runtime.InteropServices
open FSharp.NativeInterop

// Package FSharp.NativeInterop.NativePtr.stackalloc for reuse.
let inline stackalloc<'a when 'a: unmanaged> length : Span<'a> =
let voidPointer = NativePtr.stackalloc<'a> length |> NativePtr.toVoidPtr
Span<'a>(voidPointer, length)

let initializeSpan (span: Span<byte>) =
let mutable value = 0uy
for i = 0 to span.Length - 1 do
span[i] <- value
value <- value + 1uy

let computeSum (span: Span<byte>) =
let mutable sum = 0
for value in span do
sum <- sum + int value
sum

let workWithSpans () =
// Create a span over an array.
let array = Array.zeroCreate<byte> 100
let arraySpan = Span<byte> array

initializeSpan arraySpan
printfn $"The sum is {computeSum arraySpan:N0}"

// Create an array from native memory.
let native = Marshal.AllocHGlobal 100
let nativeSpan = Span<byte>(native.ToPointer(), 100)

initializeSpan nativeSpan
printfn $"The sum is {computeSum nativeSpan:N0}"

Marshal.FreeHGlobal native

// Create a span on the stack.
let stackSpan = stackalloc 100

initializeSpan stackSpan
printfn $"The sum is {computeSum stackSpan:N0}"

// The example displays the following output:
// The sum is 4,950
// The sum is 4,950
// The sum is 4,950
// </Snippet4>

createSpanFromArray ()
printfn "-----"
createSpanFromNativeMemory ()
printfn "-----"
createSpanFromStack ()
printfn "-----"
Program2.workWithSpans ()
18 changes: 18 additions & 0 deletions snippets/fsharp/System/Span/Slice/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Program

open System

[<EntryPoint>]
let main _ =
let array = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |]
let slice = Span<int>(array, 2, 5)
for i = 0 to slice.Length - 1 do
slice[i] <- slice[i] * 2

// Examine the original array values.
for value in array do
printf $"{value} "
printfn ""
0
// The example displays the following output:
// 2 4 12 16 20 24 28 16 18 20
13 changes: 13 additions & 0 deletions snippets/fsharp/System/Span/Slice/Program2.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module Program2

open System

let getContentLength (span: ReadOnlySpan<char>) =
let slice = span.Slice 16
Int32.Parse slice

let contentLength = "Content-Length: 132"
let length = getContentLength (contentLength.ToCharArray())
printfn $"Content length: {length}"
// Output:
// Content length: 132
11 changes: 11 additions & 0 deletions snippets/fsharp/System/Span/Slice/fs.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<Compile Include="Program2.fs" />
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
2 changes: 2 additions & 0 deletions xml/System/Span`1+Enumerator.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,12 @@ If <xref:System.Span%601.Enumerator.MoveNext%2A> passes the end of the <xref:Sys
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:

:::code language="csharp" source="~/snippets/csharp/System/Span.Enumerator/Program.cs":::
:::code language="fsharp" source="~/snippets/fsharp/System/Span.Enumerator/Program.fs":::

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.

:::code language="csharp" source="~/snippets/csharp/System/Span.Enumerator/Program2.cs" id="Snippet1":::
:::code language="fsharp" source="~/snippets/fsharp/System/Span.Enumerator/Program2.fs" id="Snippet1":::

Unlike some other enumerator structures in .NET, the <xref:System.Span%601.Enumerator>:

Expand Down
7 changes: 7 additions & 0 deletions xml/System/Span`1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,22 @@ For spans that represent immutable or read-only structures, use <xref:System.Rea
A `Span<T>` represents a contiguous region of arbitrary memory. A `Span<T>` instance is often used to hold the elements of an array or a portion of an array. Unlike an array, however, a `Span<T>` instance can point to managed memory, native memory, or memory managed on the stack. The following example creates a `Span<Byte>` from an array:

:::code language="csharp" source="~/snippets/csharp/System/Span/Overview/program.cs" id="Snippet1":::
:::code language="fsharp" source="~/snippets/fsharp/System/Span/Overview/program.fs" id="Snippet1":::

The following example creates a `Span<Byte>` from 100 bytes of native memory:

:::code language="csharp" source="~/snippets/csharp/System/Span/Overview/program.cs" id="Snippet2":::
:::code language="fsharp" source="~/snippets/fsharp/System/Span/Overview/program.fs" id="Snippet2":::

The following example uses the C# [stackalloc](/dotnet/csharp/language-reference/keywords/stackalloc) keyword to allocate 100 bytes of memory on the stack:

:::code language="csharp" source="~/snippets/csharp/System/Span/Overview/program.cs" id="Snippet3":::
:::code language="fsharp" source="~/snippets/fsharp/System/Span/Overview/program.fs" id="Snippet3":::

Because `Span<T>` is an abstraction over an arbitrary block of memory, methods of the `Span<T>` type and methods with `Span<T>` parameters operate on any `Span<T>` object regardless of the kind of memory it encapsulates. For example, each of the separate sections of code that initialize the span and calculate the sum of its elements can be changed into single initialization and calculation methods, as the following example illustrates:

:::code language="csharp" source="~/snippets/csharp/System/Span/Overview/program.cs" id="Snippet4":::
:::code language="fsharp" source="~/snippets/fsharp/System/Span/Overview/program.fs" id="Snippet4":::

## Span\<T> and arrays

Expand All @@ -86,6 +90,7 @@ When it wraps an array, `Span<T>` can wrap an entire array, as it did in the exa
The following example creates a slice of the middle five elements of a 10-element integer array. Note that the code doubles the values of each integer in the slice. As the output shows, the changes made by the span are reflected in the values of the array.

:::code language="csharp" source="~/snippets/csharp/System/Span/Slice/Program.cs":::
:::code language="fsharp" source="~/snippets/fsharp/System/Span/Slice/Program.fs":::

## Span\<T> and slices

Expand All @@ -98,6 +103,7 @@ The following example creates a slice of the middle five elements of a 10-elemen
This allocation and copy operation can be eliminated by using either `Span<T>` or <xref:System.ReadOnlySpan%601>, as the following example shows:

:::code language="csharp" source="~/snippets/csharp/System/Span/Slice/Program2.cs":::
:::code language="fsharp" source="~/snippets/fsharp/System/Span/Slice/Program2.fs":::


]]></format>
Expand Down Expand Up @@ -599,6 +605,7 @@ If pinning a `Span<char>`, the resulting `char*` __is not__ assumed to be null-t
The following example demonstrates creating an integer array, pinning it, and writing each element to the console.

:::code language="csharp" source="~/snippets/csharp/System/ReadOnlySpanT/GetPinnableReference/getpinnablereference1.cs":::
:::code language="fsharp" source="~/snippets/fsharp/System/ReadOnlySpanT/GetPinnableReference/getpinnablereference1.fs":::

]]></format>
</remarks>
Expand Down