From 8ddcabd9371a56c9930a2e064ebaa6294d99c62b Mon Sep 17 00:00:00 2001 From: albert-du <52804499+albert-du@users.noreply.github.com> Date: Sat, 19 Mar 2022 14:17:14 -0700 Subject: [PATCH] Span F# snippets --- .../fsharp/System/Span.Enumerator/Program.fs | 35 +++++ .../fsharp/System/Span.Enumerator/Program2.fs | 42 ++++++ .../System/Span.Enumerator/Program2/fs.fsproj | 10 ++ .../fsharp/System/Span.Enumerator/fs.fsproj | 10 ++ .../fsharp/System/Span/Overview/fs.fsproj | 10 ++ .../fsharp/System/Span/Overview/program.fs | 121 ++++++++++++++++++ snippets/fsharp/System/Span/Slice/Program.fs | 18 +++ snippets/fsharp/System/Span/Slice/Program2.fs | 13 ++ snippets/fsharp/System/Span/Slice/fs.fsproj | 11 ++ xml/System/Span`1+Enumerator.xml | 2 + xml/System/Span`1.xml | 7 + 11 files changed, 279 insertions(+) create mode 100644 snippets/fsharp/System/Span.Enumerator/Program.fs create mode 100644 snippets/fsharp/System/Span.Enumerator/Program2.fs create mode 100644 snippets/fsharp/System/Span.Enumerator/Program2/fs.fsproj create mode 100644 snippets/fsharp/System/Span.Enumerator/fs.fsproj create mode 100644 snippets/fsharp/System/Span/Overview/fs.fsproj create mode 100644 snippets/fsharp/System/Span/Overview/program.fs create mode 100644 snippets/fsharp/System/Span/Slice/Program.fs create mode 100644 snippets/fsharp/System/Span/Slice/Program2.fs create mode 100644 snippets/fsharp/System/Span/Slice/fs.fsproj diff --git a/snippets/fsharp/System/Span.Enumerator/Program.fs b/snippets/fsharp/System/Span.Enumerator/Program.fs new file mode 100644 index 00000000000..25ba582d717 --- /dev/null +++ b/snippets/fsharp/System/Span.Enumerator/Program.fs @@ -0,0 +1,35 @@ +module Program + +open System +open System.Threading.Tasks + +let array = Array.zeroCreate 5 + +let clearContents () = + Task.Delay(20).Wait() + lock array (fun () -> + Array.Clear(array, 0, array.Length) ) + +let enumerateSpan (span: Span) = + for element in span do + printfn $"{element}" + Task.Delay(10).Wait() + +[] +let main _ = + Random(42).NextBytes array + printfn "%A" array + let span: Span = array + + Task.Run clearContents |> ignore + + enumerateSpan span + + 0 + +// The example displays output like the following: +// 62 +// 23 +// 186 +// 0 +// 0 \ No newline at end of file diff --git a/snippets/fsharp/System/Span.Enumerator/Program2.fs b/snippets/fsharp/System/Span.Enumerator/Program2.fs new file mode 100644 index 00000000000..ba833a6869a --- /dev/null +++ b/snippets/fsharp/System/Span.Enumerator/Program2.fs @@ -0,0 +1,42 @@ +module Program2 + +open System +open System.Threading +open System.Threading.Tasks + +let array = Array.zeroCreate 5 + +// +let enumerateSpan (span: Span) = + // 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 +// + +let clearContents () = + Task.Delay(20).Wait() + lock array (fun () -> + Array.Clear(array, 0, array.Length) ) + +[] +let main _ = + Random(42).NextBytes array + let span: Span = array + + Task.Run clearContents |> ignore + + enumerateSpan span + + 0 \ No newline at end of file diff --git a/snippets/fsharp/System/Span.Enumerator/Program2/fs.fsproj b/snippets/fsharp/System/Span.Enumerator/Program2/fs.fsproj new file mode 100644 index 00000000000..a62ee53cd19 --- /dev/null +++ b/snippets/fsharp/System/Span.Enumerator/Program2/fs.fsproj @@ -0,0 +1,10 @@ + + + Exe + net6.0 + + + + + + \ No newline at end of file diff --git a/snippets/fsharp/System/Span.Enumerator/fs.fsproj b/snippets/fsharp/System/Span.Enumerator/fs.fsproj new file mode 100644 index 00000000000..b8765379405 --- /dev/null +++ b/snippets/fsharp/System/Span.Enumerator/fs.fsproj @@ -0,0 +1,10 @@ + + + Exe + net6.0 + + + + + + \ No newline at end of file diff --git a/snippets/fsharp/System/Span/Overview/fs.fsproj b/snippets/fsharp/System/Span/Overview/fs.fsproj new file mode 100644 index 00000000000..36cf252c6c2 --- /dev/null +++ b/snippets/fsharp/System/Span/Overview/fs.fsproj @@ -0,0 +1,10 @@ + + + Exe + net6.0 + + + + + + \ No newline at end of file diff --git a/snippets/fsharp/System/Span/Overview/program.fs b/snippets/fsharp/System/Span/Overview/program.fs new file mode 100644 index 00000000000..020d59e42e0 --- /dev/null +++ b/snippets/fsharp/System/Span/Overview/program.fs @@ -0,0 +1,121 @@ +open System +open System.Runtime.InteropServices + +let createSpanFromArray () = + // + // Create a span over an array. + let array = Array.zeroCreate 100 + let arraySpan = Span 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 + // + +let createSpanFromNativeMemory () = + // + // Create a span from native memory. + let native = Marshal.AllocHGlobal 100 + let nativeSpan = Span(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 + // + +let createSpanFromStack () = + // + // Create a span on the stack. + let mutable data = 0uy + let stackSpan = + let p = NativeInterop.NativePtr.stackalloc 100 |> NativeInterop.NativePtr.toVoidPtr + Span(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 +// + +module Program2 = + // + 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) = + let mutable value = 0uy + for i = 0 to span.Length - 1 do + span[i] <- value + value <- value + 1uy + + let computeSum (span: Span) = + 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 100 + let arraySpan = Span array + + initializeSpan arraySpan + printfn $"The sum is {computeSum arraySpan:N0}" + + // Create an array from native memory. + let native = Marshal.AllocHGlobal 100 + let nativeSpan = Span(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 + // + +createSpanFromArray () +printfn "-----" +createSpanFromNativeMemory () +printfn "-----" +createSpanFromStack () +printfn "-----" +Program2.workWithSpans () diff --git a/snippets/fsharp/System/Span/Slice/Program.fs b/snippets/fsharp/System/Span/Slice/Program.fs new file mode 100644 index 00000000000..c915c701a71 --- /dev/null +++ b/snippets/fsharp/System/Span/Slice/Program.fs @@ -0,0 +1,18 @@ +module Program + +open System + +[] +let main _ = + let array = [| 2; 4; 6; 8; 10; 12; 14; 16; 18; 20 |] + let slice = Span(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 \ No newline at end of file diff --git a/snippets/fsharp/System/Span/Slice/Program2.fs b/snippets/fsharp/System/Span/Slice/Program2.fs new file mode 100644 index 00000000000..7690b30c183 --- /dev/null +++ b/snippets/fsharp/System/Span/Slice/Program2.fs @@ -0,0 +1,13 @@ +module Program2 + +open System + +let getContentLength (span: ReadOnlySpan) = + 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 \ No newline at end of file diff --git a/snippets/fsharp/System/Span/Slice/fs.fsproj b/snippets/fsharp/System/Span/Slice/fs.fsproj new file mode 100644 index 00000000000..96b3241a2a1 --- /dev/null +++ b/snippets/fsharp/System/Span/Slice/fs.fsproj @@ -0,0 +1,11 @@ + + + Exe + net6.0 + + + + + + + \ No newline at end of file diff --git a/xml/System/Span`1+Enumerator.xml b/xml/System/Span`1+Enumerator.xml index 81f2f0ed0f2..a7b22b0c3d7 100644 --- a/xml/System/Span`1+Enumerator.xml +++ b/xml/System/Span`1+Enumerator.xml @@ -57,10 +57,12 @@ If passes the end of the . 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 : diff --git a/xml/System/Span`1.xml b/xml/System/Span`1.xml index ec6ea64ba33..289ae67e089 100644 --- a/xml/System/Span`1.xml +++ b/xml/System/Span`1.xml @@ -66,18 +66,22 @@ For spans that represent immutable or read-only structures, use ` represents a contiguous region of arbitrary memory. A `Span` instance is often used to hold the elements of an array or a portion of an array. Unlike an array, however, a `Span` instance can point to managed memory, native memory, or memory managed on the stack. The following example creates a `Span` 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` 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` is an abstraction over an arbitrary block of memory, methods of the `Span` type and methods with `Span` parameters operate on any `Span` 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\ and arrays @@ -86,6 +90,7 @@ When it wraps an array, `Span` 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\ and slices @@ -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` or , 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"::: ]]> @@ -599,6 +605,7 @@ If pinning a `Span`, 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"::: ]]>