Chapter 24 - Span and Memory
Slicing
void Main() { var numbers = new int [1000]; for (int i = 0; i < numbers.Length; i++) numbers [i] = i; Sum (numbers).Dump ("total - using implicit conversion"); Sum (numbers.AsSpan()).Dump ("total - using .AsSpan()"); Sum (numbers.AsSpan (250, 500)).Dump ("total - slicing"); Span<int> span = numbers; Console.WriteLine (span [^1]); // Last element Console.WriteLine (Sum (span [..10])); // First 10 elements Console.WriteLine (Sum (span [100..])); // 100th element to end Console.WriteLine (Sum (span [^5..])); // Last 5 elements } int Sum (ReadOnlySpan<int> numbers) { int total = 0; foreach (int i in numbers) total += i; return total; }
CopyTo
{ Span<int> x = new[] { 1, 2, 3, 4 }; Span<int> y = new int [4]; x.CopyTo (y); y.Dump ("Copy"); } { Span<int> x = new[] { 1, 2, 3, 4 }; Span<int> y = new[] { 10, 20, 30, 40 }; x [..2].CopyTo (y [2..]); // y is now { 10, 20, 1, 2 } y.Dump ("Copy - with span"); }
Working with strings
void Main() { CountWhitespace ("Word1 Word2").Dump(); CountWhitespace ("1 2 3 4 5".AsSpan (3,3)).Dump(); var span = "This ".AsSpan(); // ReadOnlySpan<char> Console.WriteLine (span.StartsWith ("This")); // True Console.WriteLine (span.Trim().Length); // 4 } int CountWhitespace (ReadOnlySpan<char> s) { int count = 0; foreach (char c in s) if (char.IsWhiteSpace (c)) count++; return count; }
Memory of T
void Main() { Split ("Word1 Word2 Word3".AsMemory()).Dump(); } IEnumerable<ReadOnlyMemory<char>> Split (ReadOnlyMemory<char> input) { int wordStart = 0; for (int i = 0; i <= input.Length; i++) if (i == input.Length || char.IsWhiteSpace (input.Span [i])) { yield return input [wordStart..i]; // Slice with C# range operator wordStart = i + 1; } }
Splitting into Ranges
void Main() { ReadOnlySpan<char> source = "The quick brown fox"; foreach (var range in Split (source)) { ReadOnlySpan<char> wordSpan = source [range]; wordSpan.Dump(); } } Range[] Split (ReadOnlySpan<char> input) { int pos = 0; var list = new List<Range>(); for (int i = 0; i <= input.Length; i++) if (i == input.Length || char.IsWhiteSpace (input [i])) { list.Add (new Range (pos, i)); pos = i + 1; } return list.ToArray(); }
CharSpanSplitter
void Main() { ReadOnlySpan<char> source = "The quick brown fox"; foreach (var word in source.Split()) word.ToString().Dump(); } public readonly ref struct CharSpanSplitter { readonly ReadOnlySpan<char> _input; public CharSpanSplitter (ReadOnlySpan<char> input) => _input = input; public Enumerator GetEnumerator() => new Enumerator (_input); public ref struct Enumerator // Forward-only enumerator { readonly ReadOnlySpan<char> _input; int _wordPos; public ReadOnlySpan<char> Current { get; private set; } public Enumerator (ReadOnlySpan<char> input) { _input = input; _wordPos = 0; Current = default; } public bool MoveNext() { for (int i = _wordPos; i <= _input.Length; i++) if (i == _input.Length || char.IsWhiteSpace (_input [i])) { Current = _input [_wordPos..i]; _wordPos = i + 1; return true; } return false; } } } public static class CharSpanExtensions { public static CharSpanSplitter Split (this ReadOnlySpan<char> input) => new CharSpanSplitter (input); }
Summing with pointers
unsafe void Main() { int* numbers = stackalloc int [1000]; // Allocate array on the stack for (int i = 0; i < 1000; i++) numbers [i] = i; int total = Sum (numbers, 1000).Dump(); } unsafe int Sum (int* numbers, int length) { int total = 0; for (int i = 0; i < length; i++) total += numbers [i]; return total; }
Span and stackalloc
unsafe void Main() { Span<int> numbers = stackalloc int [1000]; for (int i = 0; i < numbers.Length; i++) numbers[i] = i; int total = Sum (numbers).Dump(); } int Sum (ReadOnlySpan<int> numbers) { int total = 0; foreach (int i in numbers) total += numbers [i]; return total; }
Span and unmanaged memory
unsafe void Main() { var source = "The quick brown fox".AsSpan(); var ptr = Marshal.AllocHGlobal (source.Length * sizeof (char)); try { var unmanaged = new Span<char> ((char*)ptr, source.Length); source.CopyTo (unmanaged); foreach (var word in unmanaged.Split()) Console.WriteLine (word.ToString()); } finally { Marshal.FreeHGlobal (ptr); } } public readonly ref struct CharSpanSplitter { readonly ReadOnlySpan<char> _input; public CharSpanSplitter (ReadOnlySpan<char> input) => _input = input; public Rator GetEnumerator() => new Rator (_input); public ref struct Rator // Forward-only enumerator { readonly ReadOnlySpan<char> _input; int _wordPos; public ReadOnlySpan<char> Current { get; private set; } public Rator (ReadOnlySpan<char> input) { _input = input; _wordPos = 0; Current = default; } public bool MoveNext() { for (int i = _wordPos; i <= _input.Length; i++) if (i == _input.Length || char.IsWhiteSpace (_input [i])) { Current = _input [_wordPos..i]; _wordPos = i + 1; return true; } return false; } } } public static class CharSpanExtensions { public static CharSpanSplitter Split (this ReadOnlySpan<char> input) => new CharSpanSplitter (input); public static CharSpanSplitter Split (this Span<char> input) => new CharSpanSplitter (input); }